使用timerfd、inotify与epoll实现的高性能文件定时删除工具(日志文件清理工具)

在实际场景中,我们往往需要定期清理日志文件。最近我在调试过程中,需要打印的日志比较多,甚至导致每调试完几次就要动手清理一次日志文件,这让人很不爽。于是上网搜有没有相关工具可以辅助我清理日志文件,然而找到的都是要币的,而我没有... ...

于是只好自己动手写了个日志文件清理工具了。

本工具使用inotify监控指定路径,用timerfd进行延时,使用用epoll驱动事件,没有任何主动轮询操作,故效率极高,占用cpu极低。封装到了类里,可集成至项目中。

支持动态添加指定路径,不支持删除指定路径

想学习这三者的也可以看看下面的代码。

本代码免费,拿完别忘了回来点个赞~

 

 

 

FileCleaner.h

//
// 一个定期删除制定路径下文件的工具
// 文件在创建后的指定时间后会被删除(暂不支持文件夹)
// 若指定的路径下的文件读写需要管理员权限,使用管理员权限执行。
// Created by lijiahao on 19-6-26.
// email: 1281253728@qq.com
//

#ifndef FILECLEANER_FILECLEANER_H
#define FILECLEANER_FILECLEANER_H

#include <iostream>
#include <stdio.h>
#include <errno.h>
#include <sys/inotify.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
#include <string.h>
#include <unistd.h>
#include <map>
#include <sys/epoll.h>
#include <vector>

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


struct myevent_s {
    int fd;
    int event_type;
    char buf[256];
    long delay_seconds;
    int wd;
};



class FileCleaner {

public:

    FileCleaner();
    ~FileCleaner();
    bool AddWatchBySeconds(const char* path, int seconds);
    bool AddWatchByMinutes(const char* path, int minutes);
    bool AddWatchByHours(const char* path, int minutes);
    bool AddWatchByDays(const char* path, int minutes);

protected:

    bool path_check(const char *path);
    static void *WatchThread(void *arg);
    void handle_read(myevent_s* event);

private:

    int wakeup_fd;
    int close_fd;
    int epoll_fd;
    myevent_s* wakeup_event;
    std::vector<myevent_s*> watch_events;
    bool m_bExit;
};


#endif //FILECLEANER_FILECLEANER_H

 

FileCleaner.cpp

//
// Created by lijiahao on 19-6-26.
// email: 1281253728@qq.com
//

#include "FileCleaner.h"
#include <pthread.h>

using namespace std;
#define WAKEUP  2
#define TIMER_EVENT 10
#define INOTIFY_EVENT 100
FileCleaner::FileCleaner() {
    m_bExit = false;
    //初始化
    wakeup_fd = eventfd(0, EFD_NONBLOCK);//用来唤醒epoll
    if(wakeup_fd == -1){
        printf("create fd failed\n");
        exit(-1);
    }
    close_fd = eventfd(0, EFD_NONBLOCK);//用来结束对象
    if(close_fd == -1){
        printf("create fd failed\n");
        exit(-1);
    }

    epoll_fd = epoll_create1(0); //创建epoll实例
    if (epoll_fd == -1) {
        printf("create epoll fail \r\n");
        close(epoll_fd);
        close(wakeup_fd);
        exit(-1);
    }
    wakeup_event = new myevent_s;
    memset(wakeup_event, 0, sizeof(myevent_s));
    wakeup_event->fd = wakeup_fd;
    wakeup_event->event_type = WAKEUP;
    struct epoll_event ev = {0};
    ev.events = EPOLLIN;
    ev.data.ptr = wakeup_event;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, wakeup_fd, &ev); //添加到epoll监听队列中

    pthread_t th;
    pthread_create(&th, NULL, WatchThread, this);
}

FileCleaner::~FileCleaner() {
    m_bExit = true;
    uint64_t umsg = WAKEUP;
    int nret = write(wakeup_fd, &umsg, sizeof(uint64_t));
    umsg = 8;
    //usleep(100);//如果析构异常,尝试添加这句
    myevent_s *myeventS;
    while(!watch_events.empty()){
        myeventS = watch_events.back();
        inotify_rm_watch(myeventS->fd, myeventS->wd);
        close(myeventS->fd);
        delete(myeventS);
        watch_events.pop_back();
    }
    read(close_fd, &umsg, sizeof(uint64_t));
    printf("File Cleaner exit: %d", umsg);
    close(wakeup_fd);
    close(epoll_fd);
    close(close_fd);
}
bool FileCleaner::AddWatchBySeconds(const char *path, int seconds) {
    if(!path_check(path)) return false;
    int watch_fd = inotify_init();
    if ( watch_fd < 0 ) {
        perror( "inotify_init" );
        return false;
    }
    int wd = inotify_add_watch( watch_fd, path, IN_CREATE | IN_DELETE );

    myevent_s *watch_event = new myevent_s;
    watch_events.push_back(watch_event);
    memset(watch_event, 0, sizeof(watch_event));
    watch_event->fd = watch_fd;
    watch_event->wd = wd;
    watch_event->delay_seconds = seconds;
    watch_event->event_type = INOTIFY_EVENT;
    memcpy(watch_event->buf, path, strlen(path));
    struct epoll_event ev = {0};
    ev.events = EPOLLIN; //监听inotify读事件,描述符可读。
    ev.data.ptr=watch_event;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, watch_fd, &ev); //添加到epoll监听队列中

    uint64_t umsg = 1;
    int nret = write(wakeup_fd, &umsg, sizeof(uint64_t));
    if (nret!= sizeof(uint64_t))
    {
        printf("write failed\n");
        return -1;
    }
    return true;
}

bool FileCleaner::AddWatchByMinutes(const char *path, int minutes) {
    return AddWatchBySeconds(path, minutes * 60);
}
bool FileCleaner::AddWatchByHours(const char *path, int hours) {
    return AddWatchByMinutes(path, hours * 60);
}
bool FileCleaner::AddWatchByDays(const char *path, int days) {
    return AddWatchByHours(path, days * 24);
}

void *FileCleaner::WatchThread(void *arg) {
    FileCleaner *pThis = (FileCleaner*)arg;
    int ret;
    int nready;
    uint64_t value;
    myevent_s *event;
    struct epoll_event *evptr = (struct epoll_event *)calloc(1024, sizeof(struct epoll_event));
    if (evptr == NULL) {
        printf("epoll event calloc fail\n");
        delete(pThis);
        //return NULL;
    }
    while(!pThis->m_bExit)
    {

        nready = epoll_wait(pThis->epoll_fd, evptr, 1, -1); //阻塞监听,直到有事件发生
        if(nready == -1)
        {
            if(errno != EINTR) {
                perror("error epoll !");
                delete(pThis);
                break;
            }
            //printf("Interrupted system call\n");
            continue;
        }else if(nready == 0){
            printf("timeout!\n");
            continue;
        }
        int i = 0;
        for(; i < nready; ++i){
            if(evptr[i].events & EPOLLIN){
                event = (myevent_s*)(evptr[i].data.ptr);
                switch (event->event_type){//如果是唤醒信号,则读,并continue
                    case WAKEUP:
                        printf("detect WAKEUP signal\n");
                        ret = read(event->fd, &value, sizeof(uint64_t));
                        if (sizeof(uint64_t) != ret) {
                            perror("read error");
                            break;
                        }
                        break;
                    case TIMER_EVENT://如果定时器到时
                        printf("detect TIMER_EVENT signal\n");
                        ret = read(event->fd, &value, sizeof(uint64_t));
                        close(event->fd);
                        if (sizeof(uint64_t) != ret) {
                            perror("read error");
                            break;
                        }
                        if( remove(event->buf) == 0 )
                            printf("remove:[%s] success!\n", event->buf);
                        else{
                            printf("remove: [%s] failed\n", event->buf);
                            perror("failed to remove the file");
                        }
                        delete event;
                        break;
                    case INOTIFY_EVENT://如果是inotify事件
                        printf("detect INOTIFY_EVENT signal\n");
                        pThis->handle_read((myevent_s*)evptr->data.ptr);
                        break;
                }
            }
        }
    }

    uint64_t umsg = 666666;
    write(pThis->close_fd, &umsg, sizeof(uint64_t));
    printf("WorkThread Exit\n");
}

void FileCleaner::handle_read(myevent_s* event)
{
    char buffer[BUF_LEN] = { 0 };
    int fd = event->fd;
    char *file_path = event->buf;
    int check_seconds = event->delay_seconds;
    int length = read( fd, buffer, BUF_LEN );

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

    int i = 0;
    time_t now;
    time (&now);
    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( "Directory [ %s/%s ] was created.\n", file_path, event->name );
                }
                else {
                    printf( "File [ %s/%s ] was created.\n", file_path, event->name );

                    struct itimerspec time_intv = {0}; //用来存储时间
                    time_intv.it_value.tv_sec = check_seconds; //设定2s超时
                    time_intv.it_value.tv_nsec = 0;
                    int tfd = timerfd_create(CLOCK_MONOTONIC, 0); //创建定时器
                    if(tfd == -1) {
                        printf("create timer fd fail \r\n");
                        return ;
                    }
                    timerfd_settime(tfd, 0, &time_intv, NULL);//启动定时器
                    myevent_s *timer_event = new myevent_s;
                    memset(timer_event, 0, sizeof(timer_event));
                    timer_event->fd = tfd;
                    timer_event->event_type = TIMER_EVENT;
                    sprintf(timer_event->buf, "%s/%s", file_path, event->name);
                    //memcpy(timer_event->buf, file_path, strlen(file_path));
                    //timer_event.
                    struct epoll_event ev = {0};
                    ev.events = EPOLLIN; //监听定时器读事件,定时器描述符可读。
                    ev.data.ptr=timer_event;
                    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tfd, &ev); //添加到epoll监听队列中

                    uint64_t umsg = 1;
                    int nret = write(wakeup_fd, &umsg, sizeof(uint64_t));
                    if (nret!= sizeof(uint64_t))
                    {
                        printf("write failed\n");
                        return ;
                    }

                }
            }
            else if ( event->mask & IN_DELETE ) {
                if ( event->mask & IN_ISDIR ) {
                    printf( "Directory [ %s/%s ] has been removed.\n", file_path, event->name );
                }
                else {
                    printf( "File [ %s/%s ] has been removed.\n", file_path, event->name );
                }
            }
            else if ( event->mask & IN_MODIFY ) {
                if ( event->mask & IN_ISDIR ) {
                    printf( "Directory [ %s/%s ] was modified.\n", file_path,event->name );
                }
                else {
                    printf( "File [ %s/%s ] was modified.\n", file_path, event->name);
                }
            }
        }
        i += EVENT_SIZE + event->len;
    }
}

bool FileCleaner::path_check(const char *path) {
    if( (access(path, F_OK )) != -1 ) {
        printf( "Path [ %s ] exists\n", path);
        /* Check for write permission */
        if( (access(path, W_OK )) != -1 ) {
            printf( "Path [ %s ] has write permission\n", path);
        }
        else {
            printf( "Path [ %s ] has not write permission\n", path);
        }
        /* Check for read permission */
        if( (access(path, R_OK )) != -1 ) {
            printf( "Path [ %s ] has read permission\n", path);
        }
        else {
            printf( "Path [ %s ] has not read permission\n", path);
        }
        /* Check for execute permission */
        if( (access(path, X_OK )) != -1 ) {
            printf( "Path [ %s ] has execute permission\n", path);
        }
        else {
            printf( "Path [ %s ] has not execute permission\n", path);
        }
    }
    else {
        printf( "Path [ %s ] don't exists\n", path);
        return false;
    }
    return true;
}



实例    main.cpp

#include "FileCleaner.h"
using namespace std;

int main( int argc, char **argv )
{
    FileCleaner *F = new FileCleaner;
    string a;
    //方法1 直接代码中指定路径
    F->AddWatchBySeconds("./logs", 5);
    while(1){//方法2 在控制台输入路径
        cin >> a;
        bool res = F->AddWatchBySeconds(a.c_str(), 5);
        if(res)
            cout << "AddWatchBySeconds succeed" << endl;
        else
            cout << "AddWatchBySeconds failed" << endl;
    }
    delete F;
    return 0;
}

 

其中有一个小问题:析构函数中,如果

write(wakeup_fd, &umsg, sizeof(uint64_t));

之后不调用usleep, 直接接下面语句的话,

read(close_fd, &umsg, sizeof(uint64_t));

read函数会立即返回,读到的值就是写入wakeup_fd的值,但是这明明是两个不同的描述符啊,close_fd是工作线程在退出时会write的,我在中间加了usleep(100)后便能正常析构。目前没找到原因。

。。。。。。。。。。。。。。。

刚刚试了下,那个问题又没有出现了。。。。。

。。。。。

。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值