文件读写监控(inotify, systemtap)

一、inotify
     inotify是内核的一个特性,可以用来监控目录、文件的读写等事件,当监控目标是目录时,inotify除了会监控目录本身,还会监控目录中的文件。inotify的监控功能由如下的几个系统调用完成:inotify_init(2) (or inotify_init1(2)), inotify_add_watch(2), inotify_rm_watch(2), read(2), and close(2).
     inotify的主要操作基于inotify_init 返回的inotify文件描述符,该描述符的作用类似于epoll用到的epoll_fd。inotify在监控目录的时候,不支持对目录的递归监控,即只能监控一层目录,如果需要递归监控的,就需要将这些目录通过inotify_add_watch添加进来。
     inotify目前只能检测到目标文件(夹)发生了什么操作,无法检查出是哪个进程触发的这项操作。(注:fanotify函数接口支持获取触发操作的进程pid,但是fanotify支持的检测类型比inotify少太多,使用者也比较少)
     
      需求:监控/home/sxhlinux/test/test.txt的状态
     
  C语言实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include
#include
#include
#include
#include
#include
  
int  main( void )
{
     char  *filename =  "./log.txt" ;
  
     int  inoti_fd = inotify_init1(IN_CLOEXEC);
     if  (inoti_fd == -1) {
         printf ( "inotify_init failed, %s\n" strerror ( errno ));
         exit (-1);
     }
  
     int  file_fd = inotify_add_watch(inoti_fd, filename, IN_ALL_EVENTS);
     if  (file_fd == -1) {
         printf ( "inotify_add_watch failed, %s\n" strerror ( errno ));
         exit (-1);
     }
  
     int  epoll_fd = epoll_create(5);
     if  (epoll_fd == -1) {
         printf ( "epoll_create failed, %s\n" strerror ( errno ));
         goto  end;
     }
  
     struct  epoll_event ev, event[5];
  
     memset (&ev, 0,  sizeof (ev));
     ev.events = EPOLLIN | EPOLLET;
     ev.data.fd = inoti_fd;
     if  (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, inoti_fd, &ev) == -1) {
         printf ( "epoll_ctl failed, %s\n" strerror ( errno ));
         goto  end;
     }
  
     int  buf_size =  sizeof ( struct  inotify_event) + 64 *  sizeof ( char );
     struct  inotify_event * instance =  malloc (buf_size);
     memset (instance, 0, buf_size);
  
     int  value = 0;
     while  ((value = epoll_wait(epoll_fd, event, 5, -1)) != -1) {
         int  i = 0;
         for  (i = 0; i < value; i ++) {
             if  (event[i].data.fd == inoti_fd) {
                 if  (read(inoti_fd, instance, buf_size) > 0)
                     printf ( "file_id is %d, event is %u, cookie is %u, name is %s\n" ,
                             instance->wd, instance->mask, instance->cookie, instance->name);
                 else
                     printf ( "read inoti_fd failed, %s\n" strerror ( errno ));
             }
             else
                 printf ( "unknown file_fd %d\n" , event[i].data.fd);
         }
     }
  
end:
     inotify_rm_watch(inoti_fd, file_fd);
  
     return  0;

 

     Python实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
  
import  logging
from  inotify  import  adapters
  
_DEFAULT_LOG_FORMAT  =  '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  
_LOGGER  =  logging.getLogger(__name__)
  
def  _configure_logging():
     _LOGGER.setLevel(logging.DEBUG)
     ch  =  logging.FileHandler( './record' 'a' )
     formatter  =  logging.Formatter(_DEFAULT_LOG_FORMAT)
     ch.setFormatter(formatter)
     _LOGGER.addHandler(ch)
  
def  _main():
     =  adapters.Inotify()
     i.add_watch( '/var/lib/logrotate.status' )
  
     for  event  in  i.event_gen():
         if  event:
             (header, type_names, watch_path, filename)  =  event
             print  ( "WD=(%d) MASK=(%d) COOKIE=(%d) LEN=(%d) MASK->NAMES=%s WATCH-PATH=[%s] FILENAME=[%s]" ,\
             header.wd, header.mask, header.cookie, header. len , type_names, watch_path.decode( 'utf-8' ), filename.decode( 'utf-8' ))
             _LOGGER.info( "WD=(%d) MASK=(%d) COOKIE=(%d) LEN=(%d) MASK->NAMES=%s WATCH-PATH=[%s] FILENAME=[%s]" ,\
             header.wd, header.mask, header.cookie, header. len , type_names, watch_path.decode( 'utf-8' ), filename.decode( 'utf-8' ))
  
if  __name__  = =  '__main__' :
     _configure_logging()
     _main()

 

     shell命令(依赖inotify-tools软件包):
1
2
3
4
5
#监控log.txt发生的操作,并输出到record文件中,另外inotifywait命令的-r选项支持递归遍历
$ inotifywait -m log.txt -o record
  
#inotifywatch 命令则用于统计一段时间内,某个文件所发生操作的统计数据
#上述的两个命令的具体用法参照man手册

 

二、systemtap监控
     systemtap可以监控系统调用,我们使用stap带的一个监控inode的插件inodewatch.stp 来实现对文件的监控。
1
2
3
4
5
6
7
$ yum  install  systemtab -y
$ yum  install  kernel-debug-debuginfo      #stap监控系统调用需要
$ stap -ve  'probe begin { log("hello world") exit() }'      #测试stap是否安装成功
ls  -i log.txt       #查看要监控文件的inode号 2360637
df  -h       #查看log.txt所在的磁盘分区
cat  /proc/partitions      #查看各个磁盘分区的major、minor号 8 2
$ stap  /usr/share/doc/systemtap-1 .6 /examples/io/inodewatch .stp 0x8 0x2 2360637      #监控log.txt即(0x8 0x2 2360637)所进行的操作,同时会显示出执行操作的进程ID

你可能感兴趣的:(文件读写监控(inotify, systemtap))