avicom의 신변잡기

inotify 본문

LiNux / sTorAge

inotify

avicom 2010. 12. 23. 16:16

개요

  • Inotify는 inode를 기반으로 파일시스템의 활동을 감시하는 커널의 이벤트 모니터 서브시스템이다. 최초의 파일시스템 이벤트 모니터인 Dnotify를 대체하고 있으며 커널 2.6.13부터 기본 제공하는 기능이다. (그 이전의 커널에서도 패치를 통해 사용할 수 있다.)
  • 커널 내에 inotify를 위한 시스템 콜이 존재하며 이 시스템 콜을 활용하여 사용할 수 있다.
  • 서브디렉토리에 대한 감시가 자동으로 이루어지지 않기 때문에 감시하고자 하는 디렉토리를 모두 watch 인스턴스에 일일이 포함시켜주어야한다. 이러한 문제점을 해결한 fanotify라는 서브 시스템이 개발진행중이며 향후 커널 mainline에 추가될 예정임.
  • watch 인스턴스의 최대 갯수는 8192개이며 커널 파라메터중 fs.inotify.max_user_watches 를 수정하여 늘릴 수 있다.

동작

대략적인 동작 방식은 다음과 같다

1. 인스턴스 생성하고 fd 반환
int inotify_init ()

2. 감시대상 추가하고 감시 시작. pathname에 대한 mask를 감시한다. 여러개의 pathname들이 동일한 inode/감시기술자(watch descriptor)를 가리킬 수 있다.

int inotify_add_watch (int fd, const char* pathname, int mask)

모니터되는 mask는 다음과 같다.
IN_ACCESS - 파일의 마지막 접근
IN_MODIFY - 파일의 마지막 수정
IN_ATTRIB - 파일 변경의 속성
IN_OPEN 및 IN_CLOSE - 파일의 열기 혹은 닫기
IN_MOVED_FROM 및 IN_MOVED_TO - 파일이 이동(move)되거나 이름이 바뀜(rename)
IN_DELETE - 파일/디렉터리의 삭제
IN_CREATE - 파일/디렉터리의 생성
IN_DELETE_SELF - 모니터되는 파일 자체의 삭제

3. 감시기술자 감시중단.
int inotify_rm_watch (int fd, int wd)

c와 perl, python, java에 대한 API를 제공한다.

예제 코드

C

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/inotify.h>

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

int main( int argc, char **argv )
{
  int length, i = 0;
  int fd;
  int wd;
  char buffer[BUF_LEN];

  fd = inotify_init();

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

  wd = inotify_add_watch( fd, "/home",
                         IN_MODIFY | IN_CREATE | IN_DELETE );
  length = read( fd, buffer, BUF_LEN );

  if ( length < 0 ) {
    perror( "read" );
  }

  while ( i < length ) {
    struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];
    if ( event->len ) {
      if ( event->mask & IN_CREATE ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "The directory %s was created.\n", event->name );
        }
        else {
          printf( "The file %s was created.\n", event->name );
        }
      }
      else if ( event->mask & IN_DELETE ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "The directory %s was deleted.\n", event->name );
        }
        else {
          printf( "The file %s was deleted.\n", event->name );
        }
      }
      else if ( event->mask & IN_MODIFY ) {
        if ( event->mask & IN_ISDIR ) {
          printf( "The directory %s was modified.\n", event->name );
        }
        else {
          printf( "The file %s was modified.\n", event->name );
        }
      }
    }
    i += EVENT_SIZE + event->len;
  }

  ( void ) inotify_rm_watch( fd, wd );
  ( void ) close( fd );

  exit( 0 );
}

perl

#!/usr/bin/perl

use strict qw(vars);
use warnings;

use Linux::Inotify2;
use File::Find;
use Event;

my %hash;
my $e;
my $watch;

my $inotify = new Linux::Inotify2 or die "unable to create new inotify object: $!";

open (FH,'<','./inotify.conf');

while (<FH>) {
    next if ($_ =~ /^#/ || $_ =~ /^$/);
    my $dir = $_;
    chomp($dir);
    find(\&watch, $dir);
}

close(FH);

sub watch {
    $watch = $inotify->watch ($File::Find::dir, IN_MODIFY | IN_CREATE | IN_DELETE | IN_ACCESS, \&call_back);
}

sub call_back {
    $e = shift;
    $hash{name} = $e->fullname;

    $hash{name}->{flag} = "modified" if ($e->IN_MODIFY);
    $hash{name}->{flag} = "accessed" if ($e->IN_ACCESS);
    $hash{name}->{flag} = "created" if ($e->IN_CREATE );
    $hash{name}->{flag} = "deleted" if ($e->IN_DELETE );

    if (!defined($hash{name}->{p_flag}) || $hash{name}->{p_flag} ne $hash{name}->{flag} || $hash{name} ne $e->fullname) {
        if ($e->name =~ /^\w+(.+\w+)?$/ && $e->name !~ /^\d+\d+$/) {
            print $hash{name} . " : " . $hash{name}->{flag} ."\n";
            $hash{name}->{p_flag} = $hash{name}->{flag};
        }
    }
    else { %hash = (); }
}

Event->io (fd =>$inotify->fileno, poll => 'r', cb => sub { $inotify->poll });

while(1) {

    if (!defined($hash{name}) || !defined($hash{name}->{p_flag}) || $hash{name}->{flag} eq $hash{name}->{p_flag}) {
        print "polling.... \n";
        $inotify->poll;
    }
    else {
        next;
    }
}


참고

http://www.linuxjournal.com/article/8478
http://www.ibm.com/developerworks/kr/library/l-inotify.html
http://search.cpan.org/~mlehmann/Linux-Inotify2-1.21/Inotify2.pm
https://fedoraproject.org/wiki/Features/fanotify
http://kernelnewbies.org/Linux_2_6_31#head-082d9a34a245a3a3211244078f276bf3348bf0d6
http://lwn.net/Articles/339253/