Heim > System-Tutorial > LINUX > Hauptteil

Detaillierte Erläuterung der Linux-Inotify-Funktion und des Implementierungsprinzips

PHPz
Freigeben: 2024-02-13 19:45:30
nach vorne
1035 Leute haben es durchsucht

详解Linux inotify功能及实现原理

1. Hauptfunktionen von inotify

Im Vergleich zu MAC oder Windows muss das Linux-Desktopsystem in einigen Aspekten noch verbessert werden. Um diese Situation zu verbessern, hat die Open-Source-Community einige Mechanismen vorgeschlagen, die es dem Benutzermodus ermöglichen, rechtzeitig von Änderungen im Kernel oder zugrunde liegenden Hardwaregeräten zu erfahren, um Geräte besser verwalten und bessere Dienste bereitstellen zu können. Unter anderem ist Hotplug ein Mechanismus für den Kernel, um Anwendungen im Benutzermodus über Ereignisse zu Hot-Plug-Geräten zu benachrichtigen, was Desktop-Systemen dabei helfen kann, Geräte effektiv zu verwalten. udev verwaltet Gerätedateien dynamisch unter /dev, während inotify ein Benachrichtigungsmechanismus für Dateisystemänderungen ist, der Änderungen im Dateisystem im Benutzermodus, wie z. B. Dateiergänzungen, Löschungen und andere Ereignisse, umgehend benachrichtigen kann vom Desktop-Suchmaschinenprojekt Beagle entwickelt und wurde häufig in Projekten wie Gamin verwendet. Es ist zu beachten, dass es sich bei inotify um einen Kernel-Mechanismus handelt, der dazu dient, Benutzer-Space-Programme über Dateisystemänderungen zu informieren.

2. Benutzeroberfläche

Im Benutzermodus wird inotify über drei Systemaufrufe und Datei-I/-Operationen für den zurückgegebenen Dateideskriptor verwendet. Der erste Schritt zur Verwendung von inotify besteht darin, eine Inotify-Instanz zu erstellen:

 int fd = inotify_init ();        
Nach dem Login kopieren

Jede inotify-Instanz entspricht einer unabhängigen sortierten Warteschlange.

Das Dateisystem-Änderungsereignis ist eine Objektverwaltung, die als Uhren bezeichnet wird. Jede Überwachung ist ein Tupel (Ziel, Ereignismaske). Die Ereignismaske stellt das Inotify-Ereignis dar, auf das die Anwendung achten möchte. Jedes Watch-One-Bit entspricht einem Inotify-Ereignis. Überwachungsobjekte werden durch Überwachungsdeskriptoren referenziert und Überwachungen werden durch den Pfadnamen einer Datei oder eines Verzeichnisses hinzugefügt. Verzeichnisüberwachungen geben Ereignisse zurück, die für alle Dateien in diesem Verzeichnis aufgetreten sind.

Die folgende Funktion wird zum Hinzufügen einer Uhr verwendet:

int wd = inotify_add_watch (fd, path, mask);        
Nach dem Login kopieren

fd ist der von inotify_init() zurückgegebene Dateideskriptor, path ist der Pfadname des überwachten Ziels (d. h. Dateiname oder Verzeichnisname), mask ist die Ereignismaske, jedes Bit ist in der Header-Datei linux/inotify.h events definiert repräsentiert. Die Ereignismaske kann auf die gleiche Weise geändert werden, indem Sie die Inotify-Ereignisse ändern, über die Sie benachrichtigt werden möchten. Wd ist der Uhrendeskriptor.

Die folgende Funktion wird zum Löschen einer Uhr verwendet:

int ret = inotify_rm_watch (fd, wd);        
Nach dem Login kopieren

fd ist der von inotify_init() zurückgegebene Dateideskriptor, wd ist der von inotify_add_watch() zurückgegebene Überwachungsdeskriptor. Ret ist der Rückgabewert der Funktion.

Dateiereignisse werden durch eine inotify_event-Struktur dargestellt, die mithilfe der üblichen Dateilesefunktion erhalten wird, die über den von inotify_init() zurückgegebenen Dateideskriptor gelesen wird

:

           int inotify_init (void);
        int inotify_add_watch (int fd, const char *path, __u32 mask);
        int inotify_rm_watch (int fd, __u32 mask);
        
Nach dem Login kopieren
Nach dem Login kopieren

wd in der Struktur ist der Überwachungsdeskriptor des überwachten Ziels, mask ist die Ereignismaske, len ist die Länge der Namenszeichenfolge, name ist der Pfadname des überwachten Ziels, das Namensfeld der Struktur ist ein Stub, it ist nur für den Benutzer. Der Aspekt bezieht sich auf den Dateinamen, der eine variable Länge hat und tatsächlich der Struktur folgt. Der Dateiname wird mit Nullen aufgefüllt, um eine 4-Byte-Ausrichtung der nächsten Ereignisstruktur zu ermöglichen. Beachten Sie, dass len auch Füllbytes zählt.

Über den Leseaufruf können mehrere Ereignisse gleichzeitig abgerufen werden, sofern der bereitgestellte Puffer groß genug ist.

size_t len = read (fd, buf, BUF_LEN);        
Nach dem Login kopieren

buf ist ein Array-Zeiger der inotify_event-Struktur. Die Größe von buf muss mindestens BUF_LEN betragen. Die Anzahl der von diesem Aufruf zurückgegebenen Ereignisse hängt von BUF_LEN und der Länge der Datei ab Namen in der Veranstaltung. Len ist die Anzahl der tatsächlich gelesenen Bytes, also die Gesamtlänge des erhaltenen Ereignisses.

Sie können select() oder poll() für den Dateideskriptor fd verwenden, der von der Funktion inotify_init() zurückgegeben wird, oder Sie können den ioctl-Befehl FIONREAD für fd verwenden, um die Länge der aktuellen Warteschlange abzurufen. close(fd) löscht alle zu fd hinzugefügten Uhren und führt die erforderliche Bereinigung durch.

           int inotify_init (void);
        int inotify_add_watch (int fd, const char *path, __u32 mask);
        int inotify_rm_watch (int fd, __u32 mask);
        
Nach dem Login kopieren
Nach dem Login kopieren

3. Kernel-Implementierungsprinzip

Im Kernel entspricht jede inotify-Instanz einer inotify_device-Struktur:

struct inotify_device {
        wait_queue_head_t       wq;             /* wait queue for i/o */
        struct idr              idr;            /* idr mapping wd -> watch */
        struct semaphore        sem;            /* protects this bad boy */
        struct list_head        events;         /* list of queued events */
        struct list_head        watches;        /* list of watches */
        atomic_t                count;          /* reference count */
        struct user_struct      *user;          /* user who opened this dev */
        unsigned int            queue_size;     /* size of the queue (bytes) */
        unsigned int            event_count;    /* number of pending events */
        unsigned int            max_events;     /* maximum number of events */
        u32                     last_wd;        /* the last wd allocated */
};
Nach dem Login kopieren

d_list zeigt auf die Liste aller inotify_devices, i_list zeigt auf die Liste aller überwachten Inodes, count ist der Referenzzähler, dev zeigt auf die inotify_device-Struktur, die der Inotify-Instanz entspricht, in der sich die Uhr befindet, inode zeigt auf den Inode, der sein soll Von der Uhr überwacht, und wd ist die Zuordnung. Angesichts des Deskriptors der Uhr ist mask die Ereignismaske der Uhr, die angibt, an welchen Dateisystemereignissen sie interessiert ist.

结构 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 */
};
Nach dem Login kopieren

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

Nach dem Login kopieren

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
Nach dem Login kopieren

显示详细信息

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Linux-Inotify-Funktion und des Implementierungsprinzips. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:lxlinux.net
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!