用 inotify 監控 Linux 檔案系統事件

idaretobe發表於2015-01-14

在 Linux 核心中對 inotify 進行最後整合之前,我要感謝 IBM 公司 Eli Dow,他寫了本文較早的版本。特別要提出的是,在 下載 部分所提供的示例程式碼,大部分還是基於 Eli 最初的樣例程式碼。

inotify 介紹

從檔案管理器到安全工具,檔案系統監控對於的許多程式來說都是必不可少的。從 Linux 2.6.13 核心開始,Linux 就推出了 inotify,允許監控程式開啟一個獨立檔案描述符,並針對事件集監控一個或者多個檔案,例如開啟、關閉、移動/重新命名、刪除、建立或者改變屬性。在後期的核心中有了很多增強,因此在依賴這些特性之前,請先檢查您的核心版本。

在本文中,您將會學習如何在簡單的監控應用程式中使用 inotify 功能。下載樣例程式碼,在您的系統上編譯,進一步探索。

歷史簡介

在 inotify 之前有 dnotify。不幸的是,dnotify 有侷限性,使用者需要更好的產品。和 dnotify 相比 inotify 的優勢如下:

  • Inotify 使用一個獨立的檔案描述符,而 dnotify 需要為每個受監控的目錄開啟一個檔案描述符。當您同時監控多個目錄時成本會非常高,而且還會遇到每程式檔案描述符限制。
  • Inotify 所使用的檔案描述符可以通過系統呼叫獲得,並且沒有相關裝置或者檔案。而使用 dnotify,檔案描述符就固定了目錄,妨礙備用裝置解除安裝,這是可移動媒體的一個典型問題。對於 inotify,解除安裝的檔案系統上的監視檔案或目錄會產生一個事件,而且監視也會自動移除。
  • Inotify 能夠監視檔案或者目錄。Dnotify 則只監視目錄,因此程式設計師還必須保持 stat 結構或者一個等效的資料結構,來反映該被監視目錄中的檔案,然後在一個事件發生時,將其與當前狀態進行對比,以此瞭解當前目錄中的條目發生了什麼情況。
  • 如上所述,inotify 使用檔案描述符,允許程式設計師使用標準 select 或者 poll 函式來監視事件。這允許高效的多路複用 I/O 或者與 Glib 的mainloop 的整合。相比之下,dnotify 使用訊號,這使得程式設計師覺得比較困難或者不夠流暢。在 2.6.25 核心中 inotify 還新增了 Signal-drive I.O 通知功能。

用於 inotify 的 API

Inotify 提供一個簡單的 API,使用最小的檔案描述符,並且允許細粒度監控。與 inotify 的通訊是通過系統呼叫實現。可用的函式如下所示:

inotify_init
是用於建立一個 inotify 例項的系統呼叫,並返回一個指向該例項的檔案描述符。
inotify_init1
與 inotify_init 相似,並帶有附加標誌。如果這些附加標誌沒有指定,將採用與 inotify_init 相同的值。
inotify_add_watch
增加對檔案或者目錄的監控,並指定需要監控哪些事件。標誌用於控制是否將事件新增到已有的監控中,是否只有路徑代表一個目錄才進行監控,是否要追蹤符號連結,是否進行一次性監控,當首次事件出現後就停止監控。
inotify_rm_watch
從監控列表中移出監控專案。
read
讀取包含一個或者多個事件資訊的快取。
close
關閉檔案描述符,並且移除所有在該描述符上的所有監控。當關於某例項的所有檔案描述符都關閉時,資源和下層物件都將釋放,以供核心再次使用。

因此,典型的監控程式需要進行如下操作:

  1. 使用 inotify_init 開啟一個檔案描述符
  2. 新增一個或者多個監控
  3. 等待事件
  4. 處理事件,然後返回並等待更多事件
  5. 當監控不再活動時,或者接到某個訊號之後,關閉檔案描述符,清空,然後退出。

在下一部分中,您將看到可以監控的事件,它們如何在簡單的程式中執行。最後,您將看到事件監控如何進行。

通告

當您的應用程式讀取到一個通告時,事件的順序也被讀取到您提供的快取中。事件在一個變長結構中被返回,如清單 1 所示。如果資料佔滿了您的快取,您可能需要對最後一個條目進行區域性事件資訊或者區域性名處理。

清單 1. 用於 inotify 的事件結構體
struct inotify_event
{
  int wd;               /* Watch descriptor.  */
  uint32_t mask;        /* Watch mask.  */
  uint32_t cookie;      /* Cookie to synchronize two events.  */
  uint32_t len;         /* Length (including NULs) of name.  */
  char name __flexarr;  /* Name.  */
  };

請注意,只有當監控物件是一個目錄並且事件與目錄內部相關專案有關,而與目錄本身無關時,才提供 name 欄位。如果 IN_MOVED_FROM 事件與相應的 IN_MOVED_TO 事件都與被監控的專案有關,cookie 就可用於將兩者關聯起來。事件型別在掩碼欄位中返回,並伴隨著能夠被核心設定的標誌。例如,如果事件與目錄有關,則標誌 IN_ISDIR 將由核心設定。

可監控的事件

有幾種事件能夠被監控。一些事件,比如 IN_DELETE_SELF 只適用於正在被監控的專案,而另一些,比如 IN_ATTRIB 或者 IN_OPEN 則只適用於監控過的專案,或者如果該專案是目錄,則可以應用到其所包含的目錄或檔案。

IN_ACCESS
被監控專案或者被監控目錄中的條目被訪問過。例如,一個開啟的檔案被讀取。
IN_MODIFY
被監控專案或者被監控目錄中的條目被修改過。例如,一個開啟的檔案被修改。
IN_ATTRIB
被監控專案或者被監控目錄中條目的後設資料被修改過。例如,時間戳或者許可被修改。
IN_CLOSE_WRITE
一個開啟的,等待寫入的檔案或目錄被關閉。
IN_CLOSE_NOWRITE
一個以只讀方式開啟的檔案或目錄被關閉。
IN_CLOSE
一個掩碼,可以很便捷地對前面提到的兩個關閉事件(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)進行邏輯操作。
IN_OPEN
檔案或目錄被開啟。
IN_MOVED_FROM
被監控專案或者被監控目錄中的條目被移出監控區域。該事件還包含一個 cookie 來實現 IN_MOVED_FROM 與 IN_MOVED_TO 的關聯。
IN_MOVED_TO
檔案或目錄被移入監控區域。該事件包含一個針對 IN_MOVED_FROM 的 cookie。如果檔案或目錄只是被重新命名,將能看到這兩個事件,如果它只是被移入或移出非監控區域,將只能看到一個事件。如果移動或重新命名一個被監控專案,監控將繼續進行。參見下面的 IN_MOVE-SELF。
IN_MOVE
可以很便捷地對前面提到的兩個移動事件(IN_MOVED_FROM | IN_MOVED_TO)進行邏輯操作的掩碼。
IN_CREATE
在被監控目錄中建立了子目錄或檔案。
IN_DELETE
被監控目錄中有子目錄或檔案被刪除。
IN_DELETE_SELF
被監控專案本身被刪除。監控終止,並且將收到一個 IN_IGNORED 事件。
IN_MOVE_SELF
監控專案本身被移動。

除了事件標誌以外,還可以在 inotify 標頭檔案(/usr/include/sys/inotify.h)中找到其他幾個標誌。例如,如果只想監控第一個事件,可以在增加監控時設定 IN_ONESHOT 標誌。

一個簡單 inotify 應用程式

這裡的簡單應用程式(參見 下載 部分)遵循以上的通用邏輯。我們使用一個訊號處理程式來監控 ctrl-c(SIGINT)並且重置一個標誌(keep_running)使應用程式瞭解終止操作。真實的 inotify 呼叫在 utility 例程當中完成。注意,我們還建立了一個佇列,這樣能夠將事件從 inotify 底層物件中清除,留著稍後處理。在真實的應用程式中,您可能想用一個不同於您處理事件所用的執行緒(具有更高優先順序)來完成這一操作。對於該應用程式,只是為了對一般原理進行舉例說明。我們採用了一個簡單的事件連結串列,在其中我們佇列中的每個條目都包含原始事件以及指向佇列中下一事件指標的空間。

主程式

清單 2 中展示了訊號處理例程和主例程。在這個簡單示例中,在命令列對每個傳遞進來的檔案會目錄建立監控,並利用事件掩碼 IN_ALL_EVENTS 來監控每個物件的所有事件。在真實的應用程式中,您可能只希望跟蹤檔案與目錄的建立或刪除事件,因此您可以遮蔽開啟、關閉以及屬性改變事件。如果您對檔案或目錄的重新命名和移動不感興趣,您也可以遮蔽各種移動事件。關於更多細節,參見 inotify 幫助資訊。

清單 2. inotify-test.c 的簡單主程式
/* Signal handler that simply resets a flag to cause termination */
void signal_handler (int signum)
{
  keep_running = 0;
}

int main (int argc, char **argv)
{
  /* This is the file descriptor for the inotify watch */
  int inotify_fd;

  keep_running = 1;

  /* Set a ctrl-c signal handler */
  if (signal (SIGINT, signal_handler) == SIG_IGN)
    {
      /* Reset to SIG_IGN (ignore) if that was the prior state */
      signal (SIGINT, SIG_IGN);
    }

  /* First we open the inotify dev entry */
  inotify_fd = open_inotify_fd ();
  if (inotify_fd > 0)
    {

      /* We will need a place to enqueue inotify events,
         this is needed because if you do not read events
         fast enough, you will miss them. This queue is 
         probably too small if you are monitoring something
         like a directory with a lot of files and the directory 
         is deleted.
       */
      queue_t q;
      q = queue_create (128);

      /* This is the watch descriptor returned for each item we are 
         watching. A real application might keep these for some use 
         in the application. This sample only makes sure that none of
         the watch descriptors is less than 0.
       */
      int wd;


      /* Watch all events (IN_ALL_EVENTS) for the directories and 
         files passed in as arguments.
         Read the article for why you might want to alter this for 
         more efficient inotify use in your app.      
       */
      int index;
      wd = 0;
      printf("\n");
      for (index = 1; (index < argc) && (wd >= 0); index++) 
	{
	  wd = watch_dir (inotify_fd, argv[index], IN_ALL_EVENTS);
	}

      if (wd > 0) 
	{
	  /* Wait for events and process them until a 
         termination condition is detected
 	  */
	  process_inotify_events (q, inotify_fd);
	}
      printf ("\nTerminating\n");

      /* Finish up by closing the fd, destroying the queue,
         and returning a proper code
       */
      close_inotify_fd (inotify_fd);
      queue_destroy (q);
    }
  return 0;
}

使用 inotify_init 開啟檔案描述符

清單 3 展示了用於建立 inotify 例項以及獲取其檔案描述符的簡單實用工具函式。檔案描述符返回給了呼叫者。如果出現錯誤,返回值將為負。

清單 3. 使用 inotify_init
/* Create an inotify instance and open a file descriptor
   to access it */
int open_inotify_fd ()
{
  int fd;

  watched_items = 0;
  fd = inotify_init ();

  if (fd < 0)
    {
      perror ("inotify_init () = ");
    }
  return fd;
  }

使用 inotify_add_watch 新增一個監控

一旦我們有了用於 inotify 例項的檔案描述符之後,就需要增加一個或多個監控。您可以使用掩碼來設定想要監控的事件。在本例中,採用掩碼 IN_ALL_EVENTS,來監控所有可用事件。

清單 4. 使用 inotify_add_watch
int watch_dir (int fd, const char *dirname, unsigned long mask)
{
  int wd;
  wd = inotify_add_watch (fd, dirname, mask);
  if (wd < 0)
    {
      printf ("Cannot add watch for \"%s\" with event mask %lX", dirname,
	      mask);
      fflush (stdout);
      perror (" ");
    }
  else
    {
      watched_items++;
      printf ("Watching %s WD=%d\n", dirname, wd);
      printf ("Watching = %d items\n", watched_items); 
    }
  return wd;
}

事件處理迴圈

現在我們已經設定了一些監控,接下來就要等待事件。如果還存在監控,並且 keep_running 標誌沒有被訊號處理程式重置,則迴圈會一直進行。迴圈程式等待事件的發生,對有效事件進行排隊,並在返回等待狀態之前處理佇列。在真實應用程式當中,您可能會將事件放入執行緒佇列中,而在另一個執行緒中處理它們,清單 5 展示了該迴圈。

清單 5. 事件處理迴圈
int process_inotify_events (queue_t q, int fd)
{
  while (keep_running && (watched_items > 0))
    {
      if (event_check (fd) > 0)
	{
	  int r;
	  r = read_events (q, fd);
	  if (r < 0)
	    {
	      break;
	    }
	  else
	    {
	      handle_events (q);
	    }
	}
    }
  return 0;
  }

等待事件

在我們的示樣例應用程式中,迴圈會不停地進行下去,直至監控事件出現或者收到了中斷訊號。清單 6 展示了相關程式碼。

清單 6. 等待事件或者中斷
int event_check (int fd)
{
  fd_set rfds;
  FD_ZERO (&rfds);
  FD_SET (fd, &rfds);
  /* Wait until an event happens or we get interrupted 
     by a signal that we catch */
  return select (FD_SETSIZE, &rfds, NULL, NULL, NULL);
  }

讀取事件

當事件發生時,程式會依照快取區的大小來讀取儘量多的事件,然後把這些事件放入佇列等待事件處理程式來處理。樣例程式碼不能處理這種情況 — 可用事件超出 16.384 byte 快取中可儲存的事件。要處理這類情況,需要在快取末端處理部分事件。目前,對名字長度進行限制不成問題,但是優秀的防禦式程式設計會檢查名字,來確保不會溢位快取。

清單 7. 讀取事件並排隊
int read_events (queue_t q, int fd)
{
  char buffer[16384];
  size_t buffer_i;
  struct inotify_event *pevent;
  queue_entry_t event;
  ssize_t r;
  size_t event_size, q_event_size;
  int count = 0;

  r = read (fd, buffer, 16384);
  if (r <= 0)
    return r;
  buffer_i = 0;
  while (buffer_i < r)
    {
      /* Parse events and queue them. */
      pevent = (struct inotify_event *) &buffer[buffer_i];
      event_size =  offsetof (struct inotify_event, name) + pevent->len;
      q_event_size = offsetof (struct queue_entry, inot_ev.name) + 
                                  pevent->len;
      event = malloc (q_event_size);
      memmove (&(event->inot_ev), pevent, event_size);
      queue_enqueue (event, q);
      buffer_i += event_size;
      count++;
    }
  printf ("\n%d events queued\n", count);
  return count;
}

處理事件

最後,我們需要對事件做處理了。對於該應用程式,我們只簡單地報告所發生的事件。如果一個名字出現在事件結構中,我們就報告它是一個檔案或目錄。發生移動時,還會報告與移動或重新命名事件相關的 cookie 資訊。清單 8 展示了部分程式碼,包括對一些事件的處理。參見 下載 部分可獲取完整程式碼。

清單 8. 處理事件
void handle_event (queue_entry_t event)
{
  /* If the event was associated with a filename, we will store it here */
  char *cur_event_filename = NULL;
  char *cur_event_file_or_dir = NULL;
  /* This is the watch descriptor the event occurred on */
  int cur_event_wd = event->inot_ev.wd;
  int cur_event_cookie = event->inot_ev.cookie;

  unsigned long flags;

  if (event->inot_ev.len)
    {
      cur_event_filename = event->inot_ev.name;
    }
  if ( event->inot_ev.mask & IN_ISDIR )
    {
      cur_event_file_or_dir = "Dir";
    }
  else 
    {
      cur_event_file_or_dir = "File";
    }
  flags = event->inot_ev.mask & 
    ~(IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED );

  /* Perform event dependent handler routines */
  /* The mask is the magic that tells us what file operation occurred */
  switch (event->inot_ev.mask & 
	  (IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED))
    {
      /* File was accessed */
    case IN_ACCESS:
      printf ("ACCESS: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File was modified */
    case IN_MODIFY:
      printf ("MODIFY: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File changed attributes */
    case IN_ATTRIB:
      printf ("ATTRIB: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File open for writing was closed */
    case IN_CLOSE_WRITE:
      printf ("CLOSE_WRITE: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File open read-only was closed */
    case IN_CLOSE_NOWRITE:
      printf ("CLOSE_NOWRITE: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File was opened */
    case IN_OPEN:
      printf ("OPEN: %s \"%s\" on WD #%i\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd);
      break;

      /* File was moved from X */
    case IN_MOVED_FROM:
      printf ("MOVED_FROM: %s \"%s\" on WD #%i. Cookie=%d\n",
	      cur_event_file_or_dir, cur_event_filename, cur_event_wd, 
              cur_event_cookie);
      break;
.
. (other cases)
.
      /* Watch was removed explicitly by inotify_rm_watch or automatically
         because file was deleted, or file system was unmounted.  */
    case IN_IGNORED:
      watched_items--;
      printf ("IGNORED: WD #%d\n", cur_event_wd);
      printf("Watching = %d items\n",watched_items); 
      break;

      /* Some unknown message received */
    default:
      printf ("UNKNOWN EVENT \"%X\" OCCURRED for file \"%s\" on WD #%i\n",
	      event->inot_ev.mask, cur_event_filename, cur_event_wd);
      break;
    }
  /* If any flags were set other than IN_ISDIR, report the flags */
  if (flags & (~IN_ISDIR))
    {
      flags = event->inot_ev.mask;
      printf ("Flags=%lX\n", flags);
    }
    }

這個簡單示例用於說明 inotify 如何工作,以及您可以監控什麼事件。您的實際需求將決定對哪些事件進行監控以及如何處理這些事件。

用法舉例

在本部分中,我們建立一個帶有檔案的簡單雙級目錄結構,然後執行簡單程式來舉例說明 inotify 所能監控的一些事件。我們將在終端會話中啟動 inotify 示例程式,但是在後臺執行(使用 &)因此程式的輸出與我們輸入的命令會交替出現。您可以在一個終端視窗執行該程式,而在其他一個或多個視窗執行指令。清單 9 展示了簡單檔案結構和空檔案的建立,以及最初啟動該示例程式時的輸出。

清單 9. 建立一個樣例環境
ian@attic4:~/inotify-sample$ mkdir -p dir1/dir2
ian@attic4:~/inotify-sample$ touch dir1/dir2/file1
ian@attic4:~/inotify-sample$ ./inotify_test dir1/ dir1/dir2/ dir1/dir2/file1&
[2] 8733
ian@attic4:~/inotify-sample$ 
Watching dir1/ WD=1
Watching = 1 items
Watching dir1/dir2/ WD=2
Watching = 2 items
Watching dir1/dir2/file1 WD=3
Watching = 3 items

ian@attic4:~/inotify-sample$

在清單 10 中,展示了來自 dir2 內容清單的輸出。第一個事件報告是關於 dir1 的,展示了一些內容,即 dir2 ,在被監控描述符 1 監控的目錄當中被開啟了。第二個條目是關於監控描述符 2 的,顯示出被監控專案(在本例中為 dir2 )被開啟了。如果正在監控目錄樹中的多個專案,可能會經常遇到這種雙重輸出。

清單 10. 列舉 dir2 的內容
ian@attic4:~/inotify-sample$ ls dir1/dir2
file1

4 events queued
OPEN: Dir "dir2" on WD #1
OPEN: Dir "(null)" on WD #2
CLOSE_NOWRITE: Dir "dir2" on WD #1
CLOSE_NOWRITE: Dir "(null)" on WD #2

在清單 11 中,我們在 file1 新增了一些文字。請再次注意對檔案以及該檔案所在目錄的雙重開啟、關閉和修改事件。還請注意所有的事件並非立刻讀取。排隊例程被呼叫了3次,每次有兩個事件。如果再次執行該程式,並且每次操作相同,您未必會再次遇到這一特別情況。

清單 11. 在 file1 中新增文字
ian@attic4:~/inotify-sample$ echo "Some text" >> dir1/dir2/file1

2 events queued
OPEN: File "file1" on WD #2
OPEN: File "(null)" on WD #3

2 events queued
MODIFY: File "file1" on WD #2
MODIFY: File "(null)" on WD #3

2 events queued
CLOSE_WRITE: File "file1" on WD #2
CLOSE_WRITE: File "(null)" on WD #3

在清單 12 中,我們改變了 file1 的屬性。我們再次得到有關被監控專案以及其所在目錄的雙重輸出。

清單 12. 改變檔案屬性
ian@attic4:~/inotify-sample$ chmod a+w dir1/dir2/file1

2 events queued
ATTRIB: File "file1" on WD #2
ATTRIB: File "(null)" on WD #3

現在將檔案 file1 移動到上一級目錄 dir1 當中。在清單 13 中顯示了輸出結果。這次沒有雙重條目。我們實際上得到了 3 個條目,每個目錄一個,檔案本身一個。請注意 cookie (569) 允許將 MOVED-FROM 事件與 MOVED_TO 事件關聯起來。

清單 13. 將 file1 移動到 dir1
ian@attic4:~/inotify-sample$ mv dir1/dir2/file1 dir1

3 events queued
MOVED_FROM: File "file1" on WD #2. Cookie=569
MOVED_TO: File "file1" on WD #1. Cookie=569
MOVE_SELF: File "(null)" on WD #3

現在建立一個 file1 到 file2 的硬連結。當到 inode 的連結數量該變時,我們會有一個關於 file1 的 ATTRIB 事件,還會有一個關於 file2 的 CREATE 事件。

清單 14. 建立硬連結
ian@attic4:~/inotify-sample$ ln dir1/file1 dir1/file2

2 events queued
ATTRIB: File "(null)" on WD #3
CREATE: File "file2" on WD #1

現在將檔案 file1 移入當前目錄,將其重新命名為 file3 。當前目錄沒有被監控,因此不存在與 MOVED_FROM 事件相關聯的 MOVED_TO 事件。

清單 15. 將 file1 移入不受監控的目錄當中
ian@attic4:~/inotify-sample$ mv dir1/file1 ./file3

2 events queued
MOVED_FROM: File "file1" on WD #1. Cookie=572
MOVE_SELF: File "(null)" on WD #3

此時,dir2 是空的,因此可以移動它。注意我們得到一個關於監控描述符 2 的 IGNORED 事件,可見現在我們只監控兩個專案。

清單 16. 移除 dir2
ian@attic4:~/inotify-sample$ rmdir dir1/dir2

3 events queued
DELETE: Dir "dir2" on WD #1
DELETE_SELF: File "(null)" on WD #2
IGNORED: WD #2
Watching = 2 items

移除檔案 file3。注意這次我們沒有得到 IGNORED 事件。為什麼呢?為什麼得到了關於 file 3 的 ATTRIB 事件(就是原來的 dir1/dir2/file1)?

清單 16. 刪除 file3
ian@attic4:~/inotify-sample$ rm file3

1 events queued
ATTRIB: File "(null)" on WD #3

記住我們建立了 file1 到 file2 的硬連結。清單 17 顯示我們還在通過監控描述符 3 來監控 file2,儘管最開始不存在檔案 2!

清單 17. 我們仍在監視 file2!
ian@attic4:~/inotify-sample$ touch dir1/file2

6 events queued
OPEN: File "file2" on WD #1
OPEN: File "(null)" on WD #3
ATTRIB: File "file2" on WD #1
ATTRIB: File "(null)" on WD #3
CLOSE_WRITE: File "file2" on WD #1
CLOSE_WRITE: File "(null)" on WD #3

因此,現在讓我們來刪除 dir1,並監控事件級聯,因為它不再監控任何事情,不得不結束了自己。

清單 18. 刪除 dir1
ian@attic4:~/inotify-sample$ rm -rf dir1

8 events queued
OPEN: Dir "(null)" on WD #1
ATTRIB: File "(null)" on WD #3
DELETE_SELF: File "(null)" on WD #3
IGNORED: WD #3
Watching = 1 items
DELETE: File "file2" on WD #1
CLOSE_NOWRITE: Dir "(null)" on WD #1
DELETE_SELF: File "(null)" on WD #1
IGNORED: WD #1
Watching = 0 items

Terminating

inotify 的可能使用

您可以將 inotify 用於多種目標。下面列舉一些可能的情況:

效能監控
您可能想確定應用程式開啟最頻繁的檔案是哪個。如果發現一個小檔案被頻繁開啟與關閉,您可能會考慮採用記憶體版,或者改變應用程式來採取其他方式共享該資料。
元資訊
您可能想記錄檔案的附加資訊,例如起始建立時間或者最後改變該檔案的使用者 id。
安全
您可能會因為安全原因,需要對特定檔案或目錄的所有訪問進行監控。

我們的示例程式碼監控所有事件並進行報告。實際上,您可能想依據您的需要,來檢視這些事件的特定子集。您可能想監控不同被監控專案的不同事件。例如,您可能想監控檔案的開啟與關閉事件,但對於目錄只想監控建立與刪除事件。在任何可能的時候,您可以監控您所感興趣的最小事件集。

結束語

應用到效能監控、程式除錯、以及自動化等領域時,inotify 是監控 Linux 檔案系統的功能強大的、高粒度的機制。利用本文提供的樣例程式碼,您可以開始編寫用來實時記錄檔案系統事件並最小化效能開銷的應用程式。

下載

描述 名字 大小
本文使用的樣例程式碼 inotify-sample.tgz 4 KB

相關文章