Monitore eventos do sistema de arquivos Linux com o inotify

Monitoramento de sistemas de arquivos eficiente e efetivo no kernel 2.6

Use o inotify quando precisar de um monitoramento eficiente, assíncrono e de baixa granularidade para os eventos do sistema de arquivo de Linux®. Use-o para o monitoramento do espaço do usuário visando segurança, desempenho e outros propósitos.

Ian Shields, Senior Programmer, IBM

Ian Shields trabalha em vários dos projetos Linux para a zona Linux do developerWorks. Ele é um programador senior da IBM em Research Triangle Park, NC. Ele iniciou na IBM em Canberra, Austrália, como um Engenheiro de Sistemas em 1973 e, desde então, trabalhou em sistemas de comunicações e computação disseminada em Montreal, no Canadá, e em RTP na Carolina do Norte, nos Estados Unidos. Ele possui diversas patentes. Sua graduação é em matemática pura e filosofia na Australian National University. Ele possui mestrado em ciências e é doutor em ciências da computação na Universidade do Estado da Carolina do Norte.


nível de autor Contribuidor do
        developerWorks

26/Abr/2010

Entre em contato com Ian

Ian é um dos nossos autores mais populares e produtivos. Pesquise todos os artigos do Ian no developerWorks. Confira o perfil de Ian e entre em contato com ele, com outros autores e leitores no My developerWorks.

Sou grato a Eli Dow, da IBM, que escreveu uma versão anterior deste artigo, antes da integração do inotify ao kernel Linux. Em particular, o código de amostra disponível na seção Download ainda é fortemente baseado no código de amostra original de Eli.

Apresentando o inotify

O monitoramento de eventos do sistema de arquivo é essencial para muitos tipos de programas, desde gerenciador de arquivos até ferramentas de segurança. Desde o kernel Linux 2.6.13, o Linux inclui o inotify, que permite que um programa de monitoramento abra um único descritor de arquivo e procure em um ou mais arquivos ou diretórios por um conjunto de eventos específico, como abrir, fechar, mover/renomear, deletar, criar ou alterar atributos. Alguns aprimoramentos foram feitos nos kernels futuros, assim, verifique o nível do seu kernel antes de depender destes recursos.

Neste artigo, você aprenderá como usar as funções do inotify para um simples aplicativo de monitoramento. Faça o download do código de amostra e compile-o no seu sistema para explorá-lo mais a fundo.


Um pouco de história

Antes do inotify existia o dnotify. Infelizmente, o dnotify tinha limitações que deixaram os usuários esperando algo melhor. Algumas das vantagens do inotify são:

  • O inotify usa um único descritor de arquivo, enquanto o dnotify requer que seja aberto um descritor de arquivo para cada diretório em que você deseja procurar alterações. Isto pode ser realmente custoso quando você está monitorando vários diretórios ao mesmo tempo, e você pode até mesmo acabar atingindo um limite do descritor de arquivo por processo.
  • O descritor de arquivo usado pelo inotify é obtido usando uma chamada de sistema, e não contém um dispositivo ou arquivo associado. Com o dnotify, o descritor de arquivo fixa o diretório, prevenindo que o dispositivo auxiliar seja desmontado, o que é um problema particular com a mídia removível. Com o inotify, um arquivo ou diretório monitorado em um sistema de arquivos desmontado gera um evento, e a procura é automaticamente removida.
  • O inotify pode procurar em arquivos ou diretórios. O dnotify monitora diretórios, assim, os programadores tinham que manter estruturas stat, ou uma estrutura de dados equivalente, refletindo os arquivos nos diretórios sendo monitorados, para então comparar estes com o estado atual após um evento ocorrer, para saber o que aconteceu com a entrada no diretório.
  • Como mencionado acima, o inotify usa um descritor de arquivo, o que permite que os programadores usem as funções padrão select ou poll para procurar por eventos. Isto permite uma E/S multiplexadora eficiente ou a integração com o mainloop de Glib. Em contrapartida, o dnotify usa sinais, que frequentemente são considerados complicados e menos elegantes pelos programadores. A notificação I.O de unidade de sinal também foi adicionada ao inotify no kernel 2.6.25.

A API para o inotify

O inotify fornece uma API simples, que usa descritores de arquivos mínimos e permite uma boa granularidade de monitoramento. A comunicação com o inotify é estabelecida através de uma chamada de sistema. As funções disponíveis são as seguintes:

inotify_init
é a chamada do sistema que cria uma ocorrência do inotify e retorna um descritor de arquivo que faz referência à ocorrência.
inotify_init1
é similar a inotify_init com sinalizadores adicionais. Se os sinalizadores não estão especificados, esta função comporta-se como a inotify_init.
inotify_add_watch
adiciona uma procura por um arquivo ou diretório e especifica quais eventos devem ser procurados. Os sinalizadores controlam se os eventos devem ser adicionados a uma procura existente, se a procura deve ser feita somente se o caminho representa um diretório, se os links simbólicos devem seguidos ou não e se a procura é única e deve ser interrompida após o primeiro evento.
inotify_rm_watch
remove um item procurado da lista de procura.
read
lê um buffer contendo informações sobre um ou mais eventos.
close
fecha o descritor de arquivo e remove quaisquer procuras remanescentes neste descritor. Quando todos os descritores de arquivos para uma ocorrência estão fechados, os recursos e o objeto subjacente são liberados, de modo que o kernel pode reutilizá-los.

Logo, um típico programa de monitoramento fará o seguinte:

  1. Usa inotify_init para abrir um descritor de arquivo
  2. Adiciona uma ou mais procuras
  3. Aguarda os eventos
  4. Processa os eventos e retorna a aguardar mais eventos
  5. Quando não há mais procuras ativas, ou em determinado sinal, fecha o descritor de arquivo, faz a limpeza e sai.

Na próxima seção, você verá os efeitos que pode procurar, e como eles funcionam no nosso programa de amostra. Finalmente, você verá como o monitoramento de eventos funciona.


Notificações

Quando o seu aplicativo lê uma notificação, uma sequência de um ou mais eventos é lida em um buffer que você fornece. Eventos são retornados em uma estrutura de comprimento variável, como exibido na Listagem 1. Se a quantidade de dados preenche o seu buffer, você provavelmente terá que lidar com o caso de informações parciais de eventos ou um nome parcial para a última entrada.

Listagem 1. A estrutura de eventos para o 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.  */
  };

Observe que o campo de nome é apresentado somente se o item procurado é um diretório e o evento é para um item no diretório, que não o próprio diretório. O cookie é usado para correlacionar um evento IN_MOVED_FROM com o evento correspondente IN_MOVED_TO, no caso de ambos estarem relacionados com itens sendo procurados. O tipo de evento é retornado no campo de máscara , junto com sinalizadores que podem ser definidos no kernel. Por exemplo, o sinalizador IN_ISDIR é definido pelo kernel se o evento é para um diretório.


Eventos que podem ser procurados

Há muitos eventos que podem ser procurados. Alguns como, IN_DELETE_SELF aplicam-se somente para o item sendo procurado, enquanto outros como, IN_ATTRIB ou IN_OPEN podem ser aplicados para itens sendo procurados ou, se este item é um diretório, para um diretório ou arquivo dentro deste.

IN_ACCESS
O item procurado ou uma entrada em um diretório sendo procurado foi acessado. Por exemplo, um arquivo aberto foi lido.
IN_MODIFY
O item procurado ou uma entrada em um diretório sendo procurado foi modificado. Por exemplo, um arquivo aberto foi atualizado.
IN_ATTRIB
Os metadados são alterados no item procurado ou em uma entrada em um diretório procurado. Por exemplo, quando os registros de data e hora ou as permissões foram alteradas.
IN_CLOSE_WRITE
Um arquivo ou diretório que foi aberto para gravação foi fechado.
IN_CLOSE_NOWRITE
Um arquivo ou diretório que foi aberto somente para leitura foi fechado.
IN_CLOSE
Uma máscara de conveniência, que é o OR lógico dos dois eventos de encerramento precedentes (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE).
IN_OPEN
Um arquivo ou diretório foi aberto.
IN_MOVED_FROM
Um item procurado ou uma entrada em um diretório procurado foi movida do local procurado. O evento também inclui um cookie que lhe permite correlacionar IN_MOVED_FROM e IN_MOVED_TO.
IN_MOVED_TO
Um arquivo ou diretório foi movido para um local procurado. O evento inclui um cookie para IN_MOVED_FROM. Se um arquivo ou diretório é simplesmente renomeado, você veria ambos os eventos. Se foi movido de ou para um local em que você não está procurando, você verá somente um evento. Se você move ou renomeia um item procurado, a procura continua. Veja IN_MOVE-SELF abaixo.
IN_MOVE
Uma máscara de conveniência, que é o OR lógico dos dois eventos de movimentação precedentes (IN_MOVED_FROM | IN_MOVED_TO).
IN_CREATE
O subdiretório ou arquivo foi criado em um diretório procurado.
IN_DELETE
O subdiretório ou arquivo foi excluído em um diretório procurado.
IN_DELETE_SELF
O próprio item procurado foi excluído. A procura é concluída e você também receberá um evento IN_IGNORED.
IN_MOVE_SELF
O próprio item procurado foi movido.

Além dos sinalizadores de eventos, há muitos outros sinalizadores que você pode encontrar no cabeçalho do inotify (/usr/include/sys/inotify.h). Por exemplo, se você somente deseja procurar o primeiro evento, é possível definir o sinalizador IN_ONESHOT ao adicionar a procura.


Um aplicativo inotify simples

O nosso aplicativo (consulte a seção Download) segue a lógica geral acima. Nós usamos um manipulador de sinalizador para capturar ctrl-c (SIGINT) e reiniciar um sinalizador (keep_running), de modo que o aplicativo saiba quando terminar. As chamadas do inotify efetivo são feitas em rotinas de utilidade. Observe que nós também criamos uma fila, de modo que os eventos podem ser limpos do objeto do inotify subjacente e processados mais tarde. Em um aplicativo real, você provavelmente desejará fazer isso em um encadeamento diferente (e com prioridade mais alta) do que o que usa para processar os eventos. Para este aplicativo, a fila simplesmente ilustra o princípio geral. Nós usamos uma lista vinculada de eventos muito simples, onde cada uma das nossas entradas na fila consiste do evento original e de espaço para um ponteiro para o próximo evento na fila.

O programa principal

O manipulador e a rotina principal do sinalizador são exibidos na Listagem 2. Para este exemplo, nós configuramos uma procura para cada arquivo ou diretório aprovado na linha de comando, e procuramos todos os eventos para cada um destes, usando a máscara de evento IN_ALL_EVENTS. Em um aplicativo real, você provavelmente desejará controlar somente os eventos de criação e exclusão de arquivos e diretórios, assim, deverá remover a máscara de eventos abertos e fechados, assim como de alterações de atributos. Caso você não esteja interessado em movimentos e renomeações de arquivos e diretórios, pode também remover a máscara dos vários eventos de movimento. Consulte a man page do inotify para mais detalhes.

Listagem 2. Amostra da rotina principal para 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;
}

Abrindo o descritor de arquivo usando inotify_init

A Listagem 3 exibe a nossa simples função de utilitário para criar uma ocorrência de inotify e adquirir um descritor de arquivo para esta. O descritor de arquivo é retornado para o responsável pela chamada. Se há um erro, é valor retornado é negativo.

Listagem 3. Usando o 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;
  }

Adicionando uma procura usando inotify_add_watch

Uma vez que temos um descritor de arquivo para a ocorrência do inotify, precisamos adicionar uma ou mais procuras. Você usa a máscara para definir eventos particulares que deseja procurar. No nosso exemplo, usamos a máscara IN_ALL_EVENTS, que procura por todos os eventos disponíveis.

Listagem 4. Usando o 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;
}

O ciclo de processamento de eventos

Agora que definimos algumas procuras, o próximo passo é aguardar os eventos. Nós continuamos a fazer o ciclo enquanto ainda há procuras definidas e o nosso sinalizador keep_running não foi reiniciado pelo manipulador de sinal. O ciclo espera que algum evento ocorra, enfileira os eventos disponíveis e, a seguir, processa a fila antes de retornar a aguardar por mais eventos. Em um aplicativo real, você provavelmente postaria os eventos para a fila em um encadeamento, enquanto os processaria em outro encadeamento. O ciclo é exibido na Listagem 5.

Listagem 5. O ciclo de processamento de eventos
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;
  }

Aguardando eventos

No nosso aplicativo de exemplo, nós aguardamos indefinidamente, e acordamos somente se um evento procurado ocorre ou um sinal interrompe o processamento. O código é exibido na Listagem 6.

Listagem 6. Aguardando eventos ou interrupções
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);
  }

Com relação aos eventos

Quando um evento ocorre, nós lemos o máximo possível de eventos que caberão em um buffer grande e, a seguir, os colocamos em uma fila para o nosso manipulador de eventos processar. O código de amostra não trata do caso onde há mais eventos disponíveis do que o que caberá no nosso buffer de 16.384-bytes. O código precisa ser capaz de lidar com um evento parcial no final do buffer. Os limites atuais para o comprimento do nome não devem ser um problema, mas uma boa programação de defesa pode verificar que os nomes não podem estourar o buffer.

Listagem 7. Lendo e enfileirando eventos
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;
}

Processando eventos

Finalmente! Agora temos alguns eventos para processar. Para este aplicativo, nós simplesmente relatamos qual evento ocorreu. Se um nome é apresentado na estrutura do evento, nós relatamos se ele é um arquivo ou diretório. No caso de um movimento, nós também relatamos a informação do cookie que permite que você correlacione eventos de movimentação e renomeação. A Listagem 8 exibe parte do código, incluindo a manipulação de alguns dos eventos. Consulte a seção Download para o código completo.

Listagem 8. Processando os eventos
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);
    }
    }

Este simples exemplo é projetado para ilustrar como o inotify funciona e quais eventos podem ser procurados. Suas próprias necessidades ditarão quais eventos você procurará e como lidará com eles.


Exemplos de uso

Nesta seção, criamos uma estrutura de diretório simples, de dois níveis, com um arquivo no diretório, e, a seguir, executamos o aplicativo de exemplo para ilustrar alguns dos eventos que o inotify pode monitorar. Nós vamos iniciar o programa de amostra do inotify a partir de uma sessão do terminal, mas o executaremos em segundo plano (usando &), de modo que a saída do programa esteja intercalada com os nossos comandos. Você também pode executar o programa em uma janela do terminal, enquanto executa comandos em outra(s) janela(s). A Listagem 9 exibe a criação da nossa estrutura de diretório de amostra e de um arquivo vazio, junto com a saída quando iniciamos o programa de amostra.

Listagem 9. Criando o ambiente de exemplo.
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$

Na Listagem 10, nós mostramos a saída a partir da listagem dos conteúdos do dir2. O primeiro evento é relatado para o dir1, mostrando que algo, a saber o dir2, no diretório sendo procurado no descritor de procura 1 foi aberto. A segunda entrada é para o descritor de procura 2, mostrando que o item sendo procurado (neste caso, o dir2) foi aberto. Se você está procurando muitos itens em uma árvore de diretório, provavelmente verá este tipo de saída dupla frequentemente.

Listagem 10. Listando os conteúdos do 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

Na Listagem 11, nós adicionamos texto ao file1. Observe novamente os eventos duplos de abertura, fechamento e alteração para o arquivo e seu diretório. Observe também que nem todos os eventos foram lidos ao mesmo tempo. A nossa rotina de enfileiramento foi chamada três vezes diferentes com dois eventos de cada vez. Se você executar o aplicativo novamente e realizar o mesmo procedimento todas as vezes, pode ou não repetir este comportamento particular.

Listagem 11. Adicionando texto ao 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

Na Listagem 12, nós alteramos os atributos em file1. Mais uma vez, temos uma saída dupla para o item procurado e seu diretório.

Listagem 12. Alterando os atributos de um arquivo.
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

Agora, vamos mover o file1 um nível de diretório acima no dir1. A saída resultante é exibida na Listagem 13. Dessa vez, não temos entradas duplas. Temos sim três entradas, uma para cada diretório e uma para cada arquivo. Observe que o cookie (569) permite que nós correlacionemos o evento MOVED-FROM com o evento MOVED_TO.

Listagem 13. Movendo o file1 para o 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

Agora, vamos criar um link físico do file1 para o file2. Como o número de links para os inodes se altera, nós temos um evento ATTRIB no file1, e temos também um evento CREATE para o file2.

Listagem 14. Criando um link físico
ian@attic4:~/inotify-sample$ ln dir1/file1 dir1/file2

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

Agora, vamos mover o file1 para o nosso diretório atual, renomeando-o como file3. O diretório atual não está sendo procurado, logo, não há um evento MOVED_TO para correlacionar com o evento MOVED_FROM.

Listagem 15. Movendo o file1 para um diretório que não está sendo procurado.
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

Neste ponto, o dir2 está vazio, assim, vamos removê-lo. Observe que obtemos um evento IGNORED para o descritor de procura 2 e, consequentemente, estamos agora procurando somente dois itens.

Listagem 16. Removendo o 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

Vamos remover o file3. Observe que não obtemos um evento IGNORED desta vez. Por que não? E por que obtemos um evento ATTRIB para o file3 (que foi originalmente dir1/dir2/file1)?

Listagem 16. Excluindo o file3.
ian@attic4:~/inotify-sample$ rm file3

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

Lembre-se que criamos um link físico do file1 para o file2. A Listagem 17 mostra que ainda estamos procurando o file2 no descritor de procura 3, mesmo o file2 não existindo quando começamos!

Listagem 17. Ainda estamos procurando o 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

Agora vamos excluir o dir1 e observar a cascata de eventos, culminando com o nosso programa concluindo a si mesmo, pois já não estará procurando por mais nada.

Listagem 18. Excluindo o 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

Possibilidades de uso do inotify

É possível usar o inotify para muitos propósitos. Aqui estão algumas das possibilidades:

Monitoramento de desempenho
Você pode desejar determinar quais arquivos um aplicativo abre com mais frequência. Caso descubra que um arquivo pequeno é aberto e fechado repetidamente, pode considerar usar uma versão na memória, ou alterar o aplicativo, de modo que os dados são compartilhados de algum outro modo.
Meta informações
Você pode desejar registrar informações adicionais sobre os arquivos, como a data de criação original, ou o id do usuário que alterou o arquivo pela última vez.
Segurança
Você pode desejar monitorar todos os acessos a um determinado arquivo ou diretório por razões de segurança.

O nosso código de amostra procura por todos os eventos e relata todos eles. Na prática, você provavelmente desejará ver somente um subconjunto específico de eventos, de acordo com as suas necessidades. Você também poderá procurar por diferentes eventos em diferentes itens sendo procurados. Por exemplo, talvez você deseje procurar por eventos de abertura e fechamento em arquivos, mas somente criação e exclusão em diretórios. Sempre que possível, você deverá limitar suas procuras ao menor conjunto possível de eventos de seu interesse.


Conclusão

Quando aplicado a certas áreas como monitoramento do desempenho, depuração e automação, o inotify é um mecanismo muito poderoso e altamente granular para o monitoramento de sistema de arquivos do Linux. Use o código de amostra fornecido neste artigo para começar a escrever seus próprios aplicativos que respondem a, ou registrem, eventos do sistema de arquivos em tempo real com o mínimo de gasto adicional de desempenho.


Download

DescriçãoNomeTamanho
Sample code used in this articleinotify-sample.tgz4 KB

Recursos

Aprender

Obter produtos e tecnologias

  • Avalie os produtos da IBM da forma que melhor lhe convém: Faça o download de uma versão de teste de produto, experimente um produto on-line, use um produto em um ambiente de nuvem ou passe algumas horas no SOA Sandbox aprendendo como implementar Arquitetura Orientada a Serviço de forma eficiente.

Discutir

  • Envolva-se na comunidade My developerWorks. Entre em contato com outros usuários do developerWorks enquanto explora os blogs, fóruns, grupos e wikis dos desenvolvedores.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Linux, Software livre
ArticleID=485689
ArticleTitle=Monitore eventos do sistema de arquivos Linux com o inotify
publish-date=04262010