ACCESS动用inotify和epoll 实现简易的tail命令

tail命令是最常用来拘禁日志改变之家伙,比如在履某任务时会见朝地面文件被打入日志,然后利用类

tail -f file_path

的指令来查最新的日记信息。

一旦实现这种效益,一般会想到轮询,也就是是连地失去读取文件然后于内容,输出最新的即可,网上搜一下啊是发成百上千这种实现(本是)。

明朗这不敷优雅。

前听河狸家的技巧总监就说到了之的化解方案,查了一晃,发现用inotify来监听文件变化并朝程序发送事件,再用select,poll,epoll来监听inotify产生的波可以做到tail命令的基本功能。

否这个,了解一下系的调用。

inotify相关

头文件
#include<sys/inotify.h>
初始化
int fd = inotify_init();

此处的fd可以理解啊inotify创建的一个通事件的公文描述符,类似网络编程中的socket,当监听文件变化时,inotify会向fd中写副事件之系信息。

增长监听
int wd = inotify_add_watch(int fd, const char *path, unit32_t mask);

这边的fd就是初始化调用inotify_init返回的文书描述符,path就是您一旦监听的文本路径,mask就是公如果监听的变迁类型,可以生出甚多种做,返回值wd在文档上就是如此一句话:

On success, inotify_add_watch() returns a nonnegative watch
descriptor. On error -1 is returned and errno is set appropriately.

吃丁摸不着头脑,不晓回的到底是哪位文件的叙述吻合(事实证明这个wd也非是path文件的讲述称)。

mask 可以是盖下值的整合

  • IN_ACCESS,文件于访
  • IN_ATTRIB,文件属性被涂改
  • IN_CLOSE_WRITE,可写文件被关门
  • IN_CLOSE_NOWRITE,不可写文件于关
  • IN_CREATE,文件/文件夹被创造
  • IN_DELETE,文件/文件夹被删去
  • IN_DELETE_SELF,被监控之目标自我吃剔除
  • IN_MODIFY,文件为改动
  • IN_MOVE_SELF,被监督的靶子自我吃移位
  • IN_MOVED_FROM,文件为转换出为监督目录
  • IN_MOVED_TO,文件给移入被监控目录
  • IN_OPEN,文件为打开

实现tail是用的IN_MODIFY

当调用添加监视目标后就可坐等事件来了,当path对应之公文给修改后,fd就变换得而读,而且转移的消息也勾勒副了fd,这里我们不怕足以据此select,poll,epoll来监听fd以变相监听文件之变更了。

epoll相关

创建
    int epfd = epoll_create(int size);

创立一个epoll,size是如监听的文本数量

注册
int epoll_ctl(int epfd, int op,int fd, struct epoll_event *event)

epfd就是创办epoll_create返回值,op表示动作,
唯独挑选的发

  • EPOLL_CTL_ADD:注册新的fd到epfd中;
  • EPOLL_CTL_MOD:修改都报之fd的监听事件;
  • EPOLL_CTL_DEL:从epfd中剔除一个fd;

fd是使监听的文书

event 是内需监听的始末,例如fd可读,可写等等。

候事件闹
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

前方少独参数都是上面的,第三单参数maxevents其实是喻本 events
有多万分,不会见过创建时之size,第四单参数timeout是凭借等待的过期时间,比如要为500,表示500毫秒不管生没有发出事件时有发生都见面回,设为0尽管直接不通直到事件产生。返回值是发出事件的个数。

介绍了几个网调用,就可以开始撸代码了。花了几乎独小时撸了一个简易版ACCESS,好当足用,只限于append。

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/inotify.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#define BUFSIZE 81

int main(int argc, char* argv[]) {
    printf("LOG:argc = %d\n", argc);
    printf("LOG:argc[1] = %s\n", argv[1]);
    int fd = inotify_init();
    printf("LOG:fd = %d\n", fd);
    if (fd < 0) {
        printf("LOG:inotify_init error\n");
        return -1;
    }
    int wd = inotify_add_watch(fd, argv[1], IN_MODIFY);
    printf("LOG:wd = %d\n", wd);
    if (wd < 0) {
        printf("LOG:inotify_add_watch error");
        return -1;
    }

    int epfd = epoll_create(1);
    printf("LOG:epfd = %d\n", epfd);
    if (epfd < 0) {
        printf("LOG:epoll_create error\n");
        return -1;
    }

    struct epoll_event event;
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = fd;

    int re = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);     
    printf("LOG:re = %d\n", re);
    if (re != 0) {
        printf("LOG:epoll_ctl error:%s\n", strerror(errno));
        return -1;
    }

    ssize_t n;
    ssize_t tmp;
    int file_fd = open(argv[1], O_RDONLY);
    printf("LOG:file_fd = %d\n", file_fd);
    if (file_fd == -1) {
        printf("LOG:open error\n");
        return -1;
    }
    char buf[BUFSIZE];

    int seek_re;    

    while (1) {
        int num = epoll_wait(epfd, &event, 1, -1);
        printf("LOG:epoll_wait num = %d\n", num);
        if (num > 0) {
            // 这里是为了读掉消息而不重复通知
            tmp = read(fd, buf, BUFSIZE);
            seek_re = lseek(file_fd, (off_t) -1, SEEK_CUR);
            n = read(file_fd, buf, BUFSIZE);
            if (n == -1) {
                printf("break\n");
                break;
            }
            if (n > 0) {
                printf("%s", buf);
            }
        }
        printf("LOG:new loop\n");
    }

    return 0;
}

编译

gcc mytail.c -o mytail

执行

touch ./test.txt
./mytail ./test.txt

其余起一个终极

echo aa > ./test.txt
echo aabb > ./test.txt
echo aabbcc > ./test.txt

好看来类似tail的输出,不过大多了一部分log。

Q&A

  • Q:最后为甚我为此vim编辑test.txt文件又保存也没有效应啊?

  • A:其实是这样的,vim这类编辑器并不曾改动文件,而是copy一份来修,编辑了了交替掉原先的文书,如果一旦贯彻此,可以就此notify监听文件的删减移动等。

  • Q:为啥是epoll而不是select,poll?

  • A:这个绝对好玩,用select,poll也会落得平等的功力,在监听少数文本下是未曾区别之,网上说的差异主要还是指向监听大量文件的场面下,通常为是网络要高并发下,epoll会骤显绝对优势,这里epoll只监听inotify的fd文件,永远只有生一个,所以更没分别了。

  • Q:既然epoll可以监听inotify的风波,为何epoll不直监听变化的文本而是使绕这么老一个变动?

  • A:linux中的epoll本身不支持监听本地文件,只能监听类似inotify和socket这种文件(可以知晓吧信息管道,并无存储信息),当起事件到这点儿栽文件,这半种植文件被描写副了音讯,并且变得可读,本地文件写副了新物并没变得可读。

相关文章