메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Inotify를 이용한 Linux 파일 시스템 모니터

Linux 2.6 커널에서 효과적인 파일 시스템 이벤트 모니터하기

Ian Shields, 선임 프로그래머, IBM Japan
Ian Shields 사진
Ian Shields는 developerWorks 리눅스 영역을 위한 리눅스 프로젝트 다수를 수행하고 있다. Shields는 노스 캐롤라이나 주 소재 IBM 리서치 트라이앵글 파크에서 선임 프로그래머로 일한다. Shields는 1973년 시스템 엔지니어로 오스트레일리아, 캔베라에 있는 IBM 사무실에 들어갔으며 캐나다 몬트리얼과 노스 캐롤라이나 주 RTP에서 통신 시스템과 배포 컴퓨팅 부문에서 일해왔다. Shields는 특허 여러 건을 획득했으며, 논문 여러 건을 발표했다. Shields는 순수 수학과 철학 학사 학위를 오스트레일리안 국립 대학에서 받았다. 노스 캐롤라이나 주립 대학에서 컴퓨터 과학 분야를 대상으로 석사와 박사 학위를 받았다. 전자편지 주소는 ishields@us.ibm.com이다.

요약:  Linux® 파일 시스템 이벤트를 효과적으로 세밀하게 비동기적으로 모니터해야 하는 경우에는 Inotify를 사용할 수 있습니다. Inotify를 사용하면 보안, 성능이나 기타 다른 목적으로 사용자 공간을 모니터할 수 있습니다.

원문 게재일:  2010 년 4 월 06 일 번역 게재일:   2010 년 5 월 04 일
난이도:  초급 영어로:  보기 PDF:  A4 and Letter (62KB | 18 pages)Get Adobe® Reader®
페이지뷰:  5039 회
의견:  


Ian과 연락하기

Ian은 가장 인기있고 많은 글을 쓰는 저자 중 한 명이다. developerWorks에 실린Ian의 모든 기사를 살펴보자. My developerWorks에서 Ian의 프로파일을 볼 수 있으며 Ian과 다른 저자 및 그를 좋아하는 여러 독자와 연락할 수 있다.

Inotify가 Linux 커널에 최종적으로 통합되기 전에 이 기사의 초기 버전을 작성한 IBM의 Eli Dow에게 감사드린다. 특히, 다운로드 섹션에 있는 샘플 코드는 여전히 Eli의 원본 샘플 코드를 기반으로 작성되었다.

Inotify 소개

파일 시스템 이벤트 모니터링은 파일 관리자에서 보안 도구에 이르는 다양한 유형의 프로그램에 필수적인 기능이다. Linux에는 Linux 2.6.13 커널부터 Inotify가 포함되었으며 이 도구를 이용하면 모니터링 프로그램에서 하나의 파일 디스크립터를 열어서 하나 이상의 파일이나 디렉토리에서 열기, 닫기, 이동/이름 변경, 삭제, 작성이나 속성 변경과 같은 지정된 이벤트가 발생하는지 감시할 수 있다. 이후 커널에서는 몇 가지 기능이 개선되었으므로 이러한 기능을 사용하기 전에 커널 레벨을 확인한다.

이 기사에서는 간단한 모니터링 애플리케이션에서 Inotify 기능을 사용하는 방법을 살펴본다. 샘플 코드를 다운로드한 다음, 진행을 위해 해당 시스템에서 컴파일한다.


간단한 히스토리

Inotify가 있기 전에는 dnotify가 있었다. 불행히도 dnotify에는 제한사항이 있었으며 이 때문에 사용자들은 좀 더 기능이 개선되기를 원했다. Inotify의 장점은 다음과 같다.

  • Inotify는 하나의 파일 디스크립터를 사용하는 데 비해 dnotify에서는 변화를 감시할 각 디렉토리마다 하나의 파일 디스크립터를 열어야 한다. 이렇게 되면 한 번에 여러 개의 디렉토리를 모니터할 경우, 자원의 손실이 커지게 되며 프로세스당 파일 디스크립터 제한에 걸릴 수도 있다.
  • Inotify에서 사용하는 파일 디스크립터는 시스템 호출을 사용하여 얻게 되며 연관된 장치나 파일이 없다. dnotify를 사용하면 파일 디스크립터가 디렉토리에 고정되고 지원 장치가 마운트 해제되지 않게 되어 이동식 매체에 특수한 문제를 일으킨다. Inotify를 사용하는 경우에는 마운트 해제된 파일 시스템에 있는 감시 파일이나 디렉토리에서 이벤트가 생성되면 감시가 자동으로 제거된다.
  • Inotify는 파일이나 디렉토리를 감시할 수 있다. Dnotify는 디렉토리를 모니터하며 따라서 프로그래머는 감시할 디렉토리에 있는 파일이 반영된 동등한 데이터 구조나 상태 구조를 유지한 다음, 이벤트가 발생한 이후에 현재의 상태와 비교하여 디렉토리에 있는 항목에서 발생한 변화를 확인해야 했다.
  • 위에서 설명한 바와 같이 Inotify는 파일 디스크립터를 사용하며 프로그래머는 표준 select 함수나 poll 함수를 사용하여 이벤트를 감시할 수 있다. 그 결과 효과적으로 I/O를 다중화하거나 Glib의 mainloop와 통합할 수 있다. 이와는 대조적으로 dnotify는 때로는 개발자들이 다소 확인하기 어려운 신호를 사용한다. 또한, 커널 2.6.25에서는 Signal-drive I.O 알림이 Inotify에 추가되었다.

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. 더 이상 활성화된 감시가 없거나 어떤 신호를 수신하는 경우에는 파일 디스크립터를 닫고 정리한 후 종료된다.

다음 섹션에서는 감시할 수 있는 이벤트와 샘플 프로그램에서 이 이벤트가 어떻게 작동하는지를 살펴본다. 마지막에는 이벤트 모니터링이 어떻게 작동하는지 확인한다.


알림

애플리케이션이 알림을 읽으면 하나 이상의 이벤트로 구성된 시퀀스가 사용자가 제공한 버퍼로 읽혀진다. Listing 1에 표시된 바와 같이 이벤트는 가변 길이 구조로 리턴된다. 모든 데이터가 버퍼에 채워지면 마지막 항목의 부분 이름이나 부분 이벤트 정보와 같은 사항을 처리해야 한다.


Listing 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 이벤트가 감시할 항목과 관련된 경우에는 두 이벤트의 연관성을 확인하기 위해 쿠키를 사용한다. mask 필드에는 커널에 의해 설정되는 플래그와 함께 이벤트 유형이 리턴된다. 예를 들면, 이벤트 대상이 디렉토리인 경우에는 커널에서 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)를 논리적 OR 연산을 하는 편리한 마스크이다.
IN_OPEN
파일이나 디렉토리가 열렸다.
IN_MOVED_FROM
감시 디렉토리에 있는 감시 항목이 감시 위치에서 이동되었다. 또한, 이 이벤트에는 쿠키가 포함되어 있으며 사용자는 이 쿠키를 이용하여 IN_MOVED_FROM와 IN_MOVED_TO의 연관성을 확인할 수 있다.
IN_MOVED_TO
파일이나 디렉토리가 감시 위치에서 이동되었다. 이 이벤트에는 IN_MOVED_FROM과의 연관성을 확인할 수 있는 쿠키가 포함되어 있다. 파일이나 디렉토리의 이름이 변경되면 두 가지 이벤트가 모두 표시된다. 파일이나 디렉토리가 감시하고 있지 않은 위치로 이동하거나 이 위치에서 이동되는 경우에는 한 가지 이벤트만 표시된다. 사용자가 감시 항목을 이동하거나 이름을 변경하는 경우에도 감시는 계속된다. 아래에 있는 IN_MOVE-SELF를 살펴보자.
IN_MOVE
이전의 두 가지 이동 이벤트(IN_MOVED_FROM | IN_MOVED_TO)를 논리적 OR 연산을 하는 편리한 마스크이다.
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 호출은 유틸리티 루틴에서 수행된다. 또한, 기본 Inotify 오브젝트에서 이벤트를 지운 다음 나중에 처리할 수 있도록 큐를 작성한다. 실제 애플리케이션에서는 큐를 사용하여 이벤트를 처리하기보다는 다양한 스레드(우선순위가 높은 스레드)에서 이벤트가 처리되기를 원할 수도 있다. 이 애플리케이션에서는 단순히 일반적인 원칙을 설명한다. 여기에서는 이벤트로 구성된 매우 간단한 연결 목록을 사용하며 여기서 각 큐 항목은 원본 이벤트와 큐에 있는 다음 이벤트를 가리키는 포인터를 위한 공간으로 구성된다.

기본 프로그램

신호 핸들러와 기본 루틴은 Listing 2에 표시되어 있다. 이 간단한 예제에서는 명령행에서 전달되는 각 파일이나 디렉토리에 감시를 설정하고 이벤트 마스크 IN_ALL_EVENTS를 사용하여 각 파일이나 디렉토리에 대한 모든 이벤트를 감시한다. 실제 애플리케이션에서는 파일 및 디렉토리 작성 이벤트나 삭제 이벤트만을 추적하고자 할 수 있으며 이런 경우에는 속성 변경뿐만 아니라 열기와 닫기 이벤트 마스크를 제거할 수 있다. 파일이나 디렉토리가 이동되거나 이름이 변경되는 것을 추적할 필요가 없는 경우에도 다양한 이동 이벤트 마스크를 제거할 수 있다. 더 자세한 사항은 Inotify man 페이지를 참조한다.


Listing 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를 사용하여 파일 디스크립터 열기

Listing 3에는 Inotify 인스턴스를 작성하고 이 인스턴스에 해당하는 파일 디스크립터를 가져오는 데 필요한 간단한 유틸리티 함수가 있다. 이 파일 디스크립터는 호출자에게 리턴된다. 오류가 발생하는 경우에는 리턴값이 음수가 된다.


Listing 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 마스크를 사용한다.


Listing 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 플래그를 다시 설정하지 않는 한 루프를 반복한다. 루프는 이벤트가 발생하기를 기다리다가 이벤트가 발생하면 큐로 보낸 다음, 대기 상태로 돌아가 다른 이벤트를 처리하기 전에 큐를 처리한다. 실제 애플리케이션에서는 하나의 스레드에서는 이벤트를 큐로 보내고 다른 스레드에서는 이 이벤트를 처리하게 된다. 이 루프는 Listing 5에 있다.


Listing 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;
  }

이벤트 대기하기

샘플 애플리케이션은 기한 없이 대기하며 감시 이벤트가 발생하거나 신호에 의해 처리가 중단되는 경우에만 시작된다. 이 코드는 Listing 6에 있다.


Listing 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바이트 버퍼를 다 채우고도 남을 정도로 많은 이벤트가 있는 경우를 처리하지 못한다. 버퍼의 끝에 있는 부분 이벤트를 처리할 수 있어야 한다. 이름의 길이에 대한 현재의 한계가 문제가 되지는 않지만 문제를 사전에 예방하려면 이름으로 인해 버퍼가 오버플로우되지 않도록 확인해야 한다.


Listing 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;
}

이벤트 처리

마침내 처리할 이벤트가 몇 개 발생했다. 이 애플리케이션에서는 어떤 이벤트가 발생했는지 간단히 보고한다. 이벤트 구조에 이름이 있으면 그것이 파일이나 디렉토리 중 어느 것인지 보고한다. 또한, 파일이나 디렉토리가 이동한 경우에는 사용자가 이동이나 이름 바꾸기 이벤트의 연관성을 확인할 수 있도록 쿠키 정보를 보고한다. Listing 8에는 코드의 일부가 있으며 이 코드에는 일부 이벤트를 처리하는 코드가 포함되어 있다. 완전한 코드는 다운로드 섹션을 참조한다.


Listing 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 샘플 프로그램을 시작하겠지만 이 프로그램의 결과물에 명령이 삽입될 수 있도록 &를 사용하여 백그라운드에서 실행한다. 또한, 하나 이상의 다른 창에서 명령을 실행하면서 하나의 터미널 창에서 이 프로그램을 실행할 수도 있다. Listing 9에는 샘플 디렉토리 구조와 빈 파일을 작성하는 과정이 샘플 프로그램을 처음 시작할 때 발생하는 결과물과 함께 표시되어 있다.


Listing 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$ 
            

Listing 10에는 dir2의 내용이 표시되어 있다. 첫 번째 이벤트는 dir1에 대한 보고 내용이며 감시 디스크립터 1에서 감시되고 있는 디렉토리인 dir2가 열려 있다고 표시되어 있다. 두 번째 항목은 감시 디스크립터 2에 대한 내용이며 감시되고 있는 항목(이 경우에는 dir2)이 열려 있다고 표시되어 있다. 디렉토리 트리에 있는 많은 항목을 감시하는 경우에는 이러한 형태의 결과물이 이중으로 표시될 때도 있다.


Listing 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

Listing 11에서는 file1에 약간의 텍스트를 추가한다. 이 파일과 이 파일을 포함하고 있는 디렉토리에 대한 이중의 열기, 닫기 및 수정 이벤트에 다시 한 번 주목하자. 또한, 한 번에 모든 이벤트가 읽히지 않았다는 점에 유념하자. 이 애플리케이션의 큐잉 루틴은 비동기적으로 세 번 호출되며 매번 두 개의 이벤트를 사용한다. 다시 애플리케이션을 실행하여 동일한 작업을 매번 반복하면 이러한 특수한 작동이 반복될 수 있으며 또는 그렇지 않을 수도 있다.


Listing 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

Listing 12에서는 file1의 속성을 변경한다. 그러면 감시 항목과 이 항목을 포함하고 있는 디렉토리에 대한 이중 결과물이 다시 한 번 표시된다.


Listing 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으로 이동시키자. 해당 결과물은 Listing 13에 표시되어 있다. 이번에는 항목이 이중으로 표시되지 않았다. 실제로 각 디렉토리와 파일 자체에 하나씩 세 개의 항목이 표시되었다. MOVED-FROM 이벤트와 MOVED_TO 이벤트의 연관성을 확인할 수 있도록 쿠키(569)가 표시되었다.


Listing 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 이벤트가 발생한다.


Listing 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 이벤트는 발생하지 않는다.


Listing 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 이벤트가 발생하며 따라서 이제는 두 개의 항목만 감시 상태에 있게 된다.


Listing 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 이벤트가 발생하지 않는다. 왜 그럴까? 그리고 왜 file3(이 파일은 원래 dir1/dir2/file1이었음)에 대해 ATTRIB 이벤트가 발생하지 않을까?


Listing 16. file3 삭제
ian@attic4:~/inotify-sample$ rm file3

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

file1에서 file2로 하드 링크를 작성했다는 점을 기억하자. 애플리케이션을 시작했을 때 file2가 없었음에도 감시 디스크립터 3에서 여전히 file2를 감시하고 있다는 것을 Listing 17에서 확인할 수 있다.


Listing 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을 삭제한다. 그리고 다수의 이벤트를 감시하다가 결국에는 더 이상 감시할 대상이 없어서 프로그램이 스스로 종료하도록 하자.


Listing 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 파일 시스템을 모니터링 할 수 있는 매우 세분화된 강력한 메커니즘이 된다. 이 기사에서 제공된 샘플 코드를 사용하여 성능상의 오버헤드를 최소로 하면서 파일 시스템 이벤트를 실시간으로 기록하거나 이 이벤트에 응답하는 자체 애플리케이션을 작성해 보자.



다운로드 하십시오

설명이름크기다운로드 방식
Sample code used in this articleinotify-sample.tgz4 KBHTTP

다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

  • 자신에게 가장 적합한 방법으로 IBM 제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나, SOA Sandbox에서 SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.

토론

  • developerWorks community에 참여하자. 개발자 중심 블로그, 포럼, 그룹 및 Wiki 검색 중에 다른 developerWorks 사용자와 의견을 교환해 보자.

필자소개

Ian Shields 사진

Ian Shields는 developerWorks 리눅스 영역을 위한 리눅스 프로젝트 다수를 수행하고 있다. Shields는 노스 캐롤라이나 주 소재 IBM 리서치 트라이앵글 파크에서 선임 프로그래머로 일한다. Shields는 1973년 시스템 엔지니어로 오스트레일리아, 캔베라에 있는 IBM 사무실에 들어갔으며 캐나다 몬트리얼과 노스 캐롤라이나 주 RTP에서 통신 시스템과 배포 컴퓨팅 부문에서 일해왔다. Shields는 특허 여러 건을 획득했으며, 논문 여러 건을 발표했다. Shields는 순수 수학과 철학 학사 학위를 오스트레일리안 국립 대학에서 받았다. 노스 캐롤라이나 주립 대학에서 컴퓨터 과학 분야를 대상으로 석사와 박사 학위를 받았다. 전자편지 주소는 ishields@us.ibm.com이다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=리눅스, 오픈 소스
ArticleID=487028
ArticleTitle=Inotify를 이용한 Linux 파일 시스템 모니터
publish-date=04062010
author1-email=ishields@us.ibm.com
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.