>시스템 튜토리얼 >리눅스 >Linux inotify 기능 및 구현 원리에 대한 자세한 설명

Linux inotify 기능 및 구현 원리에 대한 자세한 설명

PHPz
PHPz앞으로
2024-02-13 19:45:301099검색

详解Linux inotify功能及实现原理

1. inotify의 주요 기능

MAC 또는 Windows와 비교할 때 Linux 데스크탑 시스템은 여전히 ​​일부 측면에서 개선이 필요합니다. 이러한 상황을 개선하기 위해 오픈 소스 커뮤니티는 사용자 모드가 커널 또는 기본 하드웨어 장치의 변경 사항을 적시에 학습하여 장치를 더 잘 관리하고 더 나은 서비스를 제공할 수 있는 몇 가지 메커니즘을 제안했습니다. 그중 핫플러그는 커널이 핫플러그 장치에 대한 이벤트를 사용자 모드 애플리케이션에 알리는 메커니즘으로, 데스크탑 시스템이 장치를 효과적으로 관리하는 데 도움이 될 수 있습니다. udev는 /dev에서 장치 파일을 동적으로 유지 관리하는 반면, inotify는 파일 추가, 삭제 및 기타 이벤트와 같은 사용자 모드 파일 시스템 변경 사항을 즉시 알릴 수 있는 파일 시스템 변경 알림 메커니즘입니다. 이 메커니즘은 원래 유명한 곳에서 사용되었습니다. 데스크톱 검색 엔진 프로젝트 beagle에 의해 개발되었으며 Gamin과 같은 프로젝트에서 널리 사용되었습니다. inotify는 사용자 공간 프로그램에 파일 시스템 변경 사항을 알리는 데 사용되는 커널 메커니즘이라는 점에 유의해야 합니다.

2. 사용자 인터페이스

사용자 모드에서 inotify는 반환된 파일 설명자에 대한 세 가지 시스템 호출과 파일 I/작업을 통해 사용됩니다. inotify를 사용하는 첫 번째 단계는 inotify 인스턴스를 만드는 것입니다.

으아악

각 inotify 인스턴스는 독립적으로 정렬된 대기열에 해당합니다.

파일 시스템 변경 이벤트는 시계라는 개체 관리입니다. 각 시계는 튜플(대상, 이벤트 마스크)입니다. 이벤트 마스크는 애플리케이션이 주의를 기울이고 싶은 inotify 이벤트를 나타냅니다. 각 시계 1비트는 하나의 inotify 이벤트에 해당합니다. Watch 개체는 Watch 설명자에 의해 참조되며 Watch는 파일이나 디렉터리의 경로 이름으로 추가됩니다. 디렉터리 감시는 해당 디렉터리의 모든 파일에서 발생한 이벤트를 반환합니다.

시계를 추가하는 데는 다음 기능이 사용됩니다:

으아악

fd는 inotify_init()에서 반환된 파일 설명자이고, path는 모니터링 대상의 경로 이름(예: 파일 이름 또는 디렉터리 이름)이고, 마스크는 이벤트 마스크이며, 각 비트는 헤더 파일 linux/inotify.h events에 정의되어 있습니다. 대표. 알림을 받으려는 inotify 이벤트를 변경하여 동일한 방식으로 이벤트 마스크를 수정할 수 있습니다. Wd는 시계 설명자입니다.

시계를 삭제하는 데는 다음 기능이 사용됩니다:

으아악

fd는 inotify_init()가 반환한 파일 설명자이고, wd는 inotify_add_watch()가 반환한 감시 설명자입니다. Ret는 함수의 반환 값입니다.

파일 이벤트는 inotify_init()에 의해 반환된 파일 설명자를 통해 읽은 일반적인 파일 읽기 함수를 사용하여 얻은 inotify_event 구조로 표시됩니다.

으아악

wd 구조의 wd는 모니터링 대상의 감시 설명자이고, 마스크는 이벤트 마스크, len은 이름 문자열의 길이, 이름은 모니터링 대상의 경로 이름, 구조의 이름 필드는 스텁입니다. 이는 단지 사용자를 위한 것입니다. 이 측면은 가변 길이이고 실제로 구조를 따르는 파일 이름을 참조합니다. 파일 이름은 다음 이벤트 구조가 4바이트로 정렬될 수 있도록 0으로 채워집니다. len은 패딩 바이트도 계산합니다.

제공된 버퍼가 충분히 크면 읽기 호출을 통해 여러 이벤트를 한 번에 얻을 수 있습니다.

으아악

buf는 inotify_event 구조의 배열 포인터입니다. BUF_LEN은 읽을 전체 길이를 지정합니다. buf의 크기는 최소한 BUF_LEN 이상이어야 합니다. 이 호출에서 반환되는 이벤트 수는 BUF_LEN 및 파일 길이에 따라 달라집니다. 이벤트에 이름을 입력하세요. Len은 실제로 읽은 바이트 수, 즉 얻은 이벤트의 전체 길이입니다.

inotify_init() 함수에 의해 반환된 파일 설명자 fd에서 select() 또는 poll()을 사용하거나 fd에서 ioctl 명령 FIONREAD를 사용하여 현재 대기열의 길이를 가져올 수 있습니다. close(fd)는 fd에 추가된 모든 watch를 삭제하고 필요한 정리 작업을 수행합니다.

으아악

3. 커널 구현 원리

커널에서 각 inotify 인스턴스는 inotify_device 구조에 해당합니다.

으아악

d_list는 모든 inotify_device의 목록을 가리키고, i_list는 모니터링되는 모든 inode의 목록을 가리키고, count는 참조 횟수, dev는 watch가 위치한 inotify 인스턴스에 해당하는 inotify_device 구조를 가리키고, inode는 감시할 inode를 가리킵니다. 시계에 의해 모니터링되고 wd는 할당입니다. 시계 설명자가 주어지면 마스크는 관심 있는 파일 시스템 이벤트를 나타내는 시계의 이벤트 마스크입니다.

结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

无论是目录还是文件,在内核中都对应一个 inode 结构,inotify 系统在 inode 结构中增加了两个字段:

struct inotify_watch {
        struct list_head        d_list; /* entry in inotify_device's list */
        struct list_head        i_list; /* entry in inode's list */
        atomic_t                count;  /* reference count */
        struct inotify_device   *dev;   /* associated device */
        struct inode            *inode; /* associated inode */
        s32                     wd;     /* watch descriptor */
        u32                     mask;   /* event mask for this watch */
};

d_list 指向所有 inotify_device 组成的列表的,i_list 指向所有被监视 inode 组成的列表,count 是引用计数,dev 指向该 watch 所在的 inotify 实例对应的 inotify_device 结构,inode 指向该 watch 要监视的 inode,wd 是分配给该 watch 的描述符,mask 是该 watch 的事件掩码,表示它对哪些文件系统事件感兴趣。

结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

无论是目录还是文件,在内核中都对应一个 inode 结构,inotify 系统在 inode 结构中增加了两个字段:

#ifdef CONFIG_INOTIFY
 struct list_head inotify_watches; /* watches on this inode */
 struct semaphore inotify_sem; /* protects the watches list */
#endif

inotify_watches 是在被监视目标上的 watch 列表,每当用户调用 inotify_add_watch()时,内核就为添加的 watch 创建一个 inotify_watch 结构,并把它插入到被监视目标对应的 inode 的 inotify_watches 列表。inotify_sem 用于同步对 inotify_watches 列表的访问。当文件系统发生第一部分提到的事件之一时,相应的文件系统代码将显示调用fsnotify_* 来把相应的事件报告给 inotify 系统,其中*号就是相应的事件名,目前实现包括:

fsnotify_move,文件从一个目录移动到另一个目录fsnotify_nameremove,文件从目录中删除fsnotify_inoderemove,自删除fsnotify_create,创建新文件fsnotify_mkdir,创建新目录fsnotify_access,文件被读fsnotify_modify,文件被写fsnotify_open,文件被打开fsnotify_close,文件被关闭fsnotify_xattr,文件的扩展属性被修改fsnotify_change,文件被修改或原数据被修改有一个例外情况,就是 inotify_unmount_inodes,它会在文件系统被 umount 时调用来通知 umount 事件给 inotify 系统。

以上提到的通知函数最后都调用 inotify_inode_queue_event(inotify_unmount_inodes直接调用 inotify_dev_queue_event ),该函数首先判断对应的inode是否被监视,这通过查看 inotify_watches 列表是否为空来实现,如果发现 inode 没有被监视,什么也不做,立刻返回,反之,遍历 inotify_watches 列表,看是否当前的文件操作事件被某个 watch 监视,如果是,调用 inotify_dev_queue_event,否则,返回。函数inotify_dev_queue_event 首先判断该事件是否是上一个事件的重复,如果是就丢弃该事件并返回,否则,它判断是否 inotify 实例即 inotify_device 的事件队列是否溢出,如果溢出,产生一个溢出事件,否则产生一个当前的文件操作事件,这些事件通过kernel_event 构建,kernel_event 将创建一个 inotify_kernel_event 结构,然后把该结构插入到对应的 inotify_device 的 events 事件列表,然后唤醒等待在inotify_device 结构中的 wq 指向的等待队列。想监视文件系统事件的用户态进程在inotify 实例(即 inotify_init() 返回的文件描述符)上调用 read 时但没有事件时就挂在等待队列 wq 上。

4. 使用示例

下面是一个使用 inotify 来监视文件系统事件的例子:

#include 
#include 
#include 

_syscall0(int, inotify_init)
_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask)
_syscall2(int, inotify_rm_watch, int, fd, __u32, mask)

char * monitored_files[] = {
 "./tmp_file",
 "./tmp_dir",
 "/mnt/sda3/windows_file"
};

struct wd_name {
 int wd;
 char * name;
};

#define WD_NUM 3
struct wd_name wd_array[WD_NUM];

char * event_array[] = {
 "File was accessed",
 "File was modified",
 "File attributes were changed",
 "writtable file closed",
 "Unwrittable file closed",
 "File was opened",
 "File was moved from X",
 "File was moved to Y",
 "Subfile was created",
 "Subfile was deleted",
 "Self was deleted",
 "Self was moved",
 "",
 "Backing fs was unmounted",
 "Event queued overflowed",
 "File was ignored"
};
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024
 
int main(void)
{
 int fd;
 int wd;
 char buffer[1024];
 char * offset = NULL;
 struct inotify_event * event;
 int len, tmp_len;
 char strbuf[16];
 int i = 0;
 
 fd = inotify_init();
 if (fd printf("Fail to initialize inotify.\n");
  exit(-1);
 }

 for (i=0; i"inotify_add_watch(fd," add (event- if { len) while *)buffer; inotify_event event len); len='%d.\n",' happens, printf(?Some offset="buffer;" MAX_BUF_SIZE)) buffer, while(len="read(fd," } wd_array[i].wd="wd;" exit(-1); wd_array[i].name); %s.\n?, for watch printf(?Can?t 0) (wd IN_ALL_EVENTS); wd_array[i].name, wd_array[i].name="monitored_files[i];" i++)>mask & IN_ISDIR) {
    memcpy(strbuf, "Direcotory", 11);
   }
   else {
    memcpy(strbuf, "File", 5);
   }
   printf("Object type: %s\n", strbuf);
   for (i=0; iwd != wd_array[i].wd) continue;
    printf("Object name: %s\n", wd_array[i].name);
    break;
   }
   printf("Event mask: %08X\n", event->mask);
   for (i=0; imask & (1

显示详细信息

위 내용은 Linux inotify 기능 및 구현 원리에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제