阅读分享 -- 《Linux 系统编程 第二版》III

书接上回II 阅读分享 -- 《Linux 系统编程 第二版》II|ZklMao-Space (zklkk.online) 这部分主要分享的是文件属性和目录,也就是书的第八章部分。

概述

这章内容不多,主要用得比较多就stat(...) 函数族,inotify_ ...() 文件监视函数族,含有软硬连接link(...) unlink(...) symlink(...) 其他文件操作函数在下面简单罗列...

stat(...)结构体

<sys/stat.h>
  
struct stat {
            dev_t st_dev;         /* ID of device containing file */
            ino_t st_ino;         /* inode number */
            mode_t st_mode;       /* permissions */
            nlink_t st_nlink;     /* number of hard links */
            uid_t st_uid;         /* user ID of owner */
            gid_t st_gid;         /* group ID of owner */
            dev_t st_rdev;        /* device ID (if special file) */
            off_t st_size;        /* total size in bytes */
            blksize_t st_blksize; /* block size for filesystem I/O */
            blkcnt_t st_blocks;   /* number of blocks allocated */
            time_t st_atime;      /* last access time */
            time_t st_mtime;      /* last modification time */
            time_t st_ctime;      /* last status change time */
        };

结构体成员说明

  • 文件设备信息
    • st_dev:表示文件所在的设备编号。
    • st_rdev:如果文件是特殊设备文件,则表示设备编号。
  • 索引节点信息
    • st_ino:文件的索引节点编号,用于唯一标识文件。
  • 文件权限和属性
    • st_mode:文件的权限模式,包括文件类型、读写执行权限等。
    • st_nlink:文件的硬链接数量。
    • st_uidst_gid:分别表示文件所有者的用户 ID 和所属组的组 ID。
  • 文件大小和块信息
    • st_size:文件的大小,以字节为单位。
    • st_blksize:文件系统进行 I/O 操作的首选块大小。
    • st_blocks:分配给文件的块数量。
  • 文件时间信息
    • st_atime:文件的最后访问时间。
    • st_mtime:文件的最后修改时间。
    • st_ctime:文件的最后状态改变时间,通常是指文件的权限、所有者等属性发生改变的时间。

stat(...) 函数族

这张就这几个函数:

#include <sys/stat.h>

// 获取指定路径的文件信息,包括文件类型、权限、所有者、组、大小、修改时间等,并将信息存储在 `stat`结构体中
int stat(const char *restrict pathname,
                struct stat *restrict statbuf);

// 通过文件描述符获取文件的相关信息,其功能与 `stat`函数类似,但参数是文件描述符而不是文件路径。
int fstat(int fd, struct stat *statbuf);

// 与 `stat`函数类似,但对于符号链接,`lstat`函数返回的是符号链接本身的信息,而不是链接所指向的文件的信息。
int lstat(const char *restrict pathname,
                struct stat *restrict statbuf);
#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>

// 该函数用于获取指定路径的文件状态信息,并将这些信息存储在 statbuf 所指向的 stat 结构体中
int fstatat(int dirfd, const char *restrict pathname,
                struct stat *restrict statbuf, int flags);

其他文件操作函数

文件权限管理

int chmod(const char *path, mode_t mode)
int fchmod(int fd, mode_t mode)
int chown(const char *path, uid_t owner, gid_t group)
int lchown(const char *path, uid_t owner, gid_t group)
int fchown(int fd, uid_t owner, gid_t group)
int rename(const char *oldpath, const char *newpath)
....


目录相关函数

char* getcwd(char *buf, size_t size) :获取当前工作目录的绝对路径,并将其存储在buf所指向的缓冲区中。

char* get_current_dir_name(void):获取当前工作目录的路径名。与 `getcwd`函数类似,但在某些情况下使用更方便,例如不需要指定缓冲区大小。

int chdir(const char *path)   功能描述:将当前工作目录更改为path指定的目录。

int fchdir(int fd) :通过文件描述符fd来更改当前工作目录。fd必须是一个打开的目录文件描述符。

int mkdir(const char* path, mode_t mode)* :创建一个新的目录,目录路径由path指定,mode参数指定了新目录的权限。

int rmdir(const char* path) :*删除指定的空目录,path为要删除的目录路径。

DIR* opendir(const char *name)  :打开指定名称的目录,并返回一个指向DIR结构体的指针,该结构体用于后续读取目录内容。

struct dirent* readdir(DIR *dir)  :从由dir指向的目录流中读取下一个目录项,并将其信息存储在dirent结构体中。返回值为指向dirent结构体的指针,当读取完所有目录项时返回NULL。

int closedir(DIR *dir)  :关闭由opendir函数打开的目录流

文件监视事件

struct inotify_event结构体

struct inotify_event{
    int wd;     // Watch descriptor on which event occured
    uint32_t mask;  // Bits describing event that occured #发生发事件掩码 - 具体见下mask 表
    uint32_t cookie;    // Cookie for related events (for rename())
    uint32_t len;   // Size of 'name' field # 下面name[]数组 长度
    char name[];    // Optional null-terminated filename # 变更的文件名
};

int inotify_init1(...)

  • int inotify_init1(int flags) : 初始化一个 inotify 实例,并返回一个文件描述符,该文件描述符将用于后续的 inotify 操作.
  • flags:可选的标志参数(可不填),目前支持的标志包括 IN_CLOEXECIN_NONBLOCKIN_CLOEXEC表示在执行新的程序时关闭该文件描述符;IN_NONBLOCK表示将文件描述符设置为非阻塞模式。

int inotify_add_watch(...)

  • int inotify_add_watch(int fd, const char *path, uint32_t mask) :在指定的 inotify 文件描述符 fd上,为指定的文件或目录 path添加一个监视,并指定要监视的事件掩码 mask.
  • fd:之前通过 inotify_init1函数获取的 inotify 文件描述符.
  • path:要监视的文件或目录的路径名.
  • mask:事件掩码,用于指定要监视的事件类型,是一个由多个事件标志组成的位掩码。可选参数见下:
mask
IN_ACCESS 文件被访问
IN_ATTRIB 文件元数据改变
IN_CLOSE_WRITE 关闭为了写入而打开的文件
IN_CLOSE_NOWRITE 关闭只读方式打开的文件
IN_CREATE 在监听目录内创建了文件/目录
IN_DELETE 在监听目录内删除文件/目录
IN_DELETE_SELF 监听目录/文件本身被删除。
IN_MODIFY 文件被修改
IN_MOVE_SELF 受监控目录/文件本身被移动
IN_MOVED 文件被移
IN_OPEN 文件被打开
IN_ALL_EVENTS 以上所有输出事件的统称

int inotify_rm_watch(...)

  • int inotify_rm_watch(int fd, uint32_t wd) :从指定的 inotify 文件描述符 fd中删除指定的监视描述符 wd
  • 参数
    • fd:之前通过 inotify_init1函数获取的 inotify 文件描述符。
    • wd:要删除的监视描述符,是通过 inotify_add_watch函数返回的唯一标识符

read(...)

int read(int fd, void* event, size_t len);
# 若不在notify_init1() 指定参`IN_NONBLOCK`则默认初始换创建的监听描述符默认为阻塞模式。数读取内核返回的变化事件的字节数据。一般传入char 数组,强转inotify_event 后判断mask 打印name 信息。

提示

针对文件描述符fd调用 ioctl(fd, FIONREAD, &numbytes),会返回其所指代的inotify实例中的当前可读字节数。在非阻塞模式或阻塞模式 read(...) 前可以提前调用一下,看看有没有数据可读...

示例

#include <sys/inotify.h>
#include <err.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>

typedef struct inotify_event inotify_event;

int main (void)
{
	const char dir[] = "/home/watchdir";
	int fd_notify = inotify_init();
	if (inotify_add_watch (fd_notify, dir, IN_CREATE | IN_DELETE) == -1)
		err (-1, "inotify_add_watch() %d", __LINE__);

	char buf[sizeof (inotify_event) + NAME_MAX + 1];

	for (; ;) {
		ssize_t nread = read (fd_notify, buf, sizeof (buf));
		inotify_event *event = (inotify_event *) buf;

		if (event->mask & IN_CREATE)
			printf ("new file <%s> is created in <%s>\n", event->name, dir);
		else if (event->mask & IN_DELETE)
			printf ("file <%s> is removed\n", event->name);
	}
}

注意事项

  • inotify无法检测 /proc/sys等伪文件。
  • 当你希望使用 inotify 来监视某一个文件的创建和删除时,因为inotify 无法监视一个不存在的文件,你应该使用监视文件所在目录的方式来监听。
  • inofity 的句柄文件描述符和普通的文件描述符一样,你可以使用epoll 来监听inotify 句柄的读事件来高效的响应文件的变动事件。