首頁 > 系統教程 > Linux > 主體

詳解Linux inotify功能及實作原理

PHPz
發布: 2024-02-13 19:45:30
轉載
1031 人瀏覽過

详解Linux inotify功能及实现原理

1. inotify主要功能

Linux 桌面系統相對於 MAC 或 Windows,有些方面還需要改進。為了改善這種情況,開源社群提出了一些機制,讓用戶態能夠及時得知核心或底層硬體設備發生的變化,以便更好地管理設備、提供更好的服務。其中,hotplug 是一種核心向用戶態應用通報關於熱插拔設備的事件的機制,能夠幫助桌面系統對設備進行有效的管理。 udev 動態地維護/dev 下的設備文件,而inotify 則是一種文件系統的變化通知機制,能夠及時通知用戶態文件系統的變化,如文件的增加、刪除等事件,這種機制最初被著名的桌面搜尋引擎項目beagle 引入,並在Gamin 等項目中廣泛應用。需要注意的是,inotify 是一個核心機制,用於通知用戶空間程式檔案系統變化的情況。

2. 使用者介面

#在使用者狀態,inotify 透過三個系統呼叫和在傳回的檔案描述符上的檔案 I/ 操作來使用,使用 inotify 的第一步是建立 inotify 實例:

 int fd = inotify_init ();        
登入後複製

每一個 inotify 實例對應一個獨立的排序的佇列。

檔案系統的變化事件被稱為watches 的一個物件管理,每一個watch 是一個二元群組(目標,事件遮罩),目標可以是檔案或目錄,事件遮罩表示應用希望關注的inotify 事件,每一個位元對應一個inotify 事件。 Watch 物件透過 watch描述符引用,watches 透過檔案或目錄的路徑名來新增。目錄 watches 將傳回在該目錄下的所有檔案上面發生的事件。

下面函數用來新增一個 watch:

int wd = inotify_add_watch (fd, path, mask);        
登入後複製

fd 是inotify_init() 傳回的檔案描述符,path 是被監視的目標的路徑名(即檔案名稱或目錄名稱),mask 是事件掩碼, 在頭檔linux/inotify.h 中定義了每一位代表的事件。可以使用同樣的方式來修改事件掩碼,也就是改變希望被通知的inotify 事件。 Wd 是 watch 描述符。

下面的函數用來刪除一個 watch:

int ret = inotify_rm_watch (fd, wd);        
登入後複製

fd 是 inotify_init() 傳回的檔案描述符,wd 是 inotify_add_watch() 傳回的 watch 描述符。 Ret 是函數的回傳值。

檔案事件以 inotify_event 結構表示,它透過由 inotify_init() 傳回的檔案描述符使用通常檔案讀取函數 read 來獲得

           int inotify_init (void);
        int inotify_add_watch (int fd, const char *path, __u32 mask);
        int inotify_rm_watch (int fd, __u32 mask);
        
登入後複製
登入後複製

結構中的wd 為被監視目標的watch 描述符,mask 為事件掩碼,len 為name字串的長度,name 為被監視目標的路徑名,該結構的name 欄位為一個樁,它只是為了使用者方面引用檔案名,檔案名稱是變長的,它實際緊跟在該結構的後面,檔案名稱將被0 填充以使下一個事件結構能夠4 個位元組對齊。注意,len 也把填充位元組數統計在內。

透過 read 呼叫可以一次獲得多個事件,只要提供的 buf 夠大。

size_t len = read (fd, buf, BUF_LEN);        
登入後複製

buf 是一個 inotify_event 結構的陣列指針,BUF_LEN 指定要讀取的總長度,buf 大小至少要不小於 BUF_LEN,而呼叫傳回的事件數取決於 BUF_LEN 以及事件中檔案名稱的長度。 Len 為實際讀去的位元組數,即所得到的事件的總長度。

可以在函數 inotify_init() 傳回的檔案描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 指令 FIONREAD 來得到目前佇列的長度。 close(fd)將刪除所有加入 fd 中的 watch 並做必要的清理。

           int inotify_init (void);
        int inotify_add_watch (int fd, const char *path, __u32 mask);
        int inotify_rm_watch (int fd, __u32 mask);
        
登入後複製
登入後複製

3. 核心實作原理

在核心中,每一個 inotify 實例對應一個 inotify_device 結構:

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 */
};
登入後複製

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 结构中增加了两个字段:

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
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!