JulianaEgg 发表于 2025-5-28 22:18:24

select和poll

select函数

原理

select通过一个文件描述符集合(fd_set)来监控多个文件描述符。它会检查这些文件描述符是否准备好进行读、写或异常操作。fd_set是一个位数组,每个位对应一个文件描述符。select会扫描这个数组,检查每个文件描述符的状态。
使用方法

使用FD_SET宏将文件描述符添加到fd_set中,然后调用select函数。select函数会返回准备好操作的文件描述符的数量,并更新fd_set,将未准备好的文件描述符从集合中移除。
优点


[*]select的API相对简单,容易理解和使用。
[*]几乎所有操作系统都支持select,具有很好的兼容性。
缺点


[*]select的最大文件描述符数量通常受限于系统定义的FD_SETSIZE(通常是1024)。当监控大量文件描述符时,性能会显著下降。
[*]select需要扫描整个fd_set来确定哪些文件描述符准备好,这在文件描述符数量较多时效率较低。
[*]select需要为每个文件描述符分配一个位,当文件描述符数量较多时,会浪费较多的内存资源。
[*]不适用于需要处理大量文件描述符的场景。
#include <sys/select.h>
#include <sys/time.h>

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
//nfds:需要监控的文件描述符的最大值加1
//readfds:指向可读文件描述符集合的指针
//writefds:指向可写文件描述符集合的指针
//exceptfds:指向异常条件文件描述符集合的指针
//timeout:超时时间
//ret:返回集合中文件描述符的数量,调用完成后没有改变的文件描述符会被清除#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>

int main() {
    // 创建两个文件描述符,用于示例
    int pipefds;
    if (pipe(pipefds) == -1) {
      perror("pipe");
      exit(EXIT_FAILURE);
    }

    // 初始化文件描述符集合
    fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(pipefds, &readfds); // 读端
    FD_SET(STDIN_FILENO, &readfds); // 标准输入

    // 设置超时时间
    struct timeval timeout;
    timeout.tv_sec = 5; // 5秒超时
    timeout.tv_usec = 0;

    // 调用 select
    int ret = select(pipefds + 1, &readfds, NULL, NULL, &timeout);
    if (ret == -1) {
      perror("select");
      exit(EXIT_FAILURE);
    } else if (ret == 0) {
      printf("Select timeout\n");
    } else {
      if (FD_ISSET(pipefds, &readfds)) {
            printf("Pipe read end is ready for reading\n");
      }
      if (FD_ISSET(STDIN_FILENO, &readfds)) {
            printf("Standard input is ready for reading\n");
      }
    }

    // 关闭文件描述符
    close(pipefds);
    close(pipefds);

    return 0;
}poll函数

原理

poll使用一个pollfd结构数组来监控文件描述符。每个pollfd结构包含一个文件描述符、要监控的事件(如读、写)和事件状态。poll函数会检查这些结构,确定哪些文件描述符已经准备好指定的事件。
使用方法

创建一个pollfd数组,为每个文件描述符设置要监控的事件,然后调用poll函数。poll函数会返回准备好操作的文件描述符的数量,并更新每个pollfd结构的事件状态。
优点


[*]poll没有像select那样的文件描述符数量限制,可以处理更多的文件描述符。
[*]poll直接返回准备好操作的文件描述符,不需要像select那样扫描整个集合,效率相对较高。
缺点


[*]poll需要为每个文件描述符分配一个pollfd结构,当文件描述符数量较多时,会占用较多的内存资源。
[*]虽然poll没有文件描述符数量限制,但在文件描述符数量非常多时,性能也会受到影响,因为poll需要逐个检查每个pollfd结构。
[*]不适用于高并发的场景
struct pollfd {
    int fd;      // 文件描述符
    short events;// 需要监控的事件
    short revents; // 实际发生的事件
};
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//fds:指向 pollfd 结构数组的指针
//nfds:pollfd 结构数组的大小。
//timeout:超时时间(毫秒)。如果设置为 -1,则表示阻塞直到有文件描述符准备好。
//ret:>0 状态改变的文件描述符的数量=0 超时   -1,表示调用发生错误。#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <unistd.h>

int main() {
    // 创建两个文件描述符,用于示例
    int pipefds;
    if (pipe(pipefds) == -1) {
      perror("pipe");
      exit(EXIT_FAILURE);
    }

    // 初始化 pollfd 结构数组
    struct pollfd fds;
    fds.fd = pipefds; // 读端
    fds.events = POLLIN;
    fds.fd = STDIN_FILENO; // 标准输入
    fds.events = POLLIN;

    // 调用 poll
    int ret = poll(fds, 2, -1); // 阻塞等待
    if (ret == -1) {
      perror("poll");
      exit(EXIT_FAILURE);
    }

    // 检查结果
    if (ret > 0) {
      if (fds.revents & POLLIN) {
            printf("Pipe read end is ready for reading\n");
      }
      if (fds.revents & POLLIN) {
            printf("Standard input is ready for reading\n");
      }
    }

    // 关闭文件描述符
    close(pipefds);
    close(pipefds);

    return 0;
}
来源:新程序网络收集,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: select和poll