MST

星途 面试题库

面试题:C语言Linux多路复用事件监听的性能优化

在Linux下用C语言使用epoll实现多路复用事件监听,相较于select和poll,epoll在高并发场景下有哪些性能优势?在代码实现中,如何设置epoll的参数以优化性能?
47.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

epoll在高并发场景下相较于select和poll的性能优势

  1. 连接数限制
    • select:单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,在高并发场景下远远不够。
    • poll:本质上和select没有区别,只是没有了文件描述符数量的限制,但随着文件描述符的增多,性能会急剧下降。
    • epoll:没有文件描述符数量的限制,可以轻松处理大量连接,非常适合高并发场景。
  2. 事件通知方式
    • select:采用轮询的方式扫描文件描述符集合,时间复杂度为O(n),随着文件描述符数量的增加,效率会越来越低。
    • poll:同样采用轮询方式,时间复杂度也是O(n)。
    • epoll:采用回调机制,只有活跃的文件描述符才会被回调,时间复杂度为O(1),在高并发且连接活跃度较低的场景下,性能优势明显。
  3. 内存拷贝
    • select:每次调用select时,需要将用户态的文件描述符集合拷贝到内核态,返回时又要将结果从内核态拷贝回用户态,开销较大。
    • poll:与select类似,也存在大量的内存拷贝操作。
    • epoll:通过epoll_ctl注册文件描述符到内核态,之后内核维护这些信息,不需要每次都进行用户态和内核态之间的大量数据拷贝,减少了性能开销。

在代码实现中设置epoll参数以优化性能

  1. epoll创建
    int epollfd = epoll_create1(0);
    if (epollfd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
    • 使用epoll_create1epoll_create更简洁,epoll_create1的参数若为0表示与epoll_create相同的行为。若设置为EPOLL_CLOEXEC,可以使创建的epoll文件描述符在execve系统调用后自动关闭,防止文件描述符泄露。
  2. 添加监控事件
    struct epoll_event event;
    event.data.fd = sockfd;
    event.events = EPOLLIN | EPOLLET;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
        perror("epoll_ctl: add");
        close(sockfd);
        close(epollfd);
        exit(EXIT_FAILURE);
    }
    
    • 事件类型
      • EPOLLIN表示对应的文件描述符可以读(包括对端SOCKET正常关闭)。
      • EPOLLOUT表示对应的文件描述符可以写。
      • EPOLLERR表示对应的文件描述符发生错误。
      • EPOLLHUP表示对应的文件描述符被挂断。
    • 触发模式
      • 水平触发(LT,默认):只要文件描述符对应的缓冲区还有未读数据,就会一直触发读事件;只要缓冲区还有可写空间,就会一直触发写事件。
      • 边缘触发(ET):只有在文件描述符状态发生变化时才会触发事件,在高并发场景下可以减少事件触发次数,提高效率,但编程难度相对较高,需要确保一次性读完或写完数据。
  3. 等待事件
    struct epoll_event events[EPOLL_MAX_EVENTS];
    int nfds = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_wait");
        close(epollfd);
        exit(EXIT_FAILURE);
    }
    
    • 事件数组events数组用于存放epoll_wait返回的事件,EPOLL_MAX_EVENTS定义了该数组的大小,应根据实际情况合理设置,既要避免过小导致事件丢失,又要避免过大浪费内存。
    • 超时时间epoll_wait的最后一个参数是超时时间,单位为毫秒。若设置为 -1,表示永久等待,直到有事件发生;若设置为0,则立即返回,不等待;设置为大于0的值,则等待指定的毫秒数,若超时仍无事件发生则返回。在实际应用中,应根据业务需求合理设置超时时间,避免过长等待导致响应不及时或过短等待导致无效轮询增加开销。