zoukankan      html  css  js  c++  java
  • Linux企业级项目实践之网络爬虫(19)——epoll接口

    由于要实现爬虫程序的快速抓取,显然如果采用阻塞型的I/O方式,那么系统可能很长时间都处在等待内核响应的状态中,这样爬虫程序将大大地降低效率。然而,如果采用非阻塞I/O,那么就要一直调用应用进程,反复对内核进行轮询。为了实现发送出系统调用请求,而不必一直返回进行查询,最合适的方案应该是采用poll函数,对系统调用实行轮询,即I/O复用模式。

    epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。


    epoll的相关系统调用
    epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用。
     
    1. int epoll_create(int size);
    创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
     
    2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
    第一个参数是epoll_create()的返回值。
    第二个参数表示动作,用三个宏来表示:
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;
     
    第三个参数是需要监听的fd。
    第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

    //保存触发事件的某个文件描述符相关的数据(与具体使用方式有关)  
      
    typedef union epoll_data {  
        void *ptr;  
        int fd;  
        __uint32_t u32;  
        __uint64_t u64;  
    } epoll_data_t;  
     //感兴趣的事件和被触发的事件  
    struct epoll_event {  
        __uint32_t events; /* Epoll events */  
        epoll_data_t data; /* User data variable */  
    };  


    events可以是以下几个宏的集合:
    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


    3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。


    int attach_epoll_task()
    {
        struct epoll_event ev;
        int sock_rv;
        int sockfd;
        Url * ourl = pop_ourlqueue();
        if (ourl == NULL) {
            SPIDER_LOG(SPIDER_LEVEL_WARN, "Pop ourlqueue fail!");
            return -1;
        }
    
        /* connect socket and get sockfd */
        if ((sock_rv = build_connect(&sockfd, ourl->ip, ourl->port)) < 0) {
            SPIDER_LOG(SPIDER_LEVEL_WARN, "Build socket connect fail: %s", ourl->ip);
            return -1;
        }
    
        set_nonblocking(sockfd);
    
        if ((sock_rv = send_request(sockfd, ourl)) < 0) {
            SPIDER_LOG(SPIDER_LEVEL_WARN, "Send socket request fail: %s", ourl->ip);
            return -1;
        } 
    
        evso_arg * arg = (evso_arg *)calloc(1, sizeof(evso_arg));
        arg->fd = sockfd;
        arg->url = ourl;
        ev.data.ptr = arg;
        ev.events = EPOLLIN | EPOLLET;
        if (epoll_ctl(g_epfd, EPOLL_CTL_ADD, sockfd, &ev) == 0) {/* add event */
            SPIDER_LOG(SPIDER_LEVEL_DEBUG, "Attach an epoll event success!");
        } else {
            SPIDER_LOG(SPIDER_LEVEL_WARN, "Attach an epoll event fail!");
            return -1;
        }
    
        g_cur_thread_num++; 
        return 0;
    }


  • 相关阅读:
    CodeForces 288A Polo the Penguin and Strings (水题)
    CodeForces 289B Polo the Penguin and Matrix (数学,中位数)
    CodeForces 289A Polo the Penguin and Segments (水题)
    CodeForces 540C Ice Cave (BFS)
    网站后台模板
    雅图CAD
    mbps
    WCF学习-协议绑定
    数据库建表经验总结
    资源位置
  • 原文地址:https://www.cnblogs.com/new0801/p/6176992.html
Copyright © 2011-2022 走看看