文件描述符管理
- select:使用固定大小的数组来管理文件描述符,通常限制为1024个(可通过修改内核参数调整)。需要将所有关注的文件描述符集合(读、写、异常)传递给select函数,每次调用都需要重新设置。
- poll:使用链表结构来管理文件描述符,理论上对文件描述符数量没有限制。通过
pollfd
结构体数组来传递关注的文件描述符和事件,每次调用无需重新设置所有描述符。
事件通知方式
- select:通过修改传入的文件描述符集合来通知调用者哪些描述符有事件发生。调用者需要遍历整个文件描述符集合来找出具体发生事件的描述符。
- poll:在
pollfd
结构体数组中,返回时会标记出发生事件的描述符,调用者直接遍历该数组,检查每个元素的revents
字段即可知道哪些描述符发生了事件。
性能方面
- select:每次调用都需要将文件描述符集合从用户空间拷贝到内核空间,并且遍历检查所有文件描述符,随着文件描述符数量增加,性能会显著下降。
- poll:同样需要将
pollfd
结构体数组从用户空间拷贝到内核空间,但由于采用链表结构管理描述符,遍历效率相对较高,在文件描述符较多时性能优于select。
适用场景举例
- 适合select的场景:例如一个简单的服务器,预计同时连接的客户端数量较少(远小于1024),并且对性能要求不是特别高。比如一个小型的本地测试服务器,主要处理少量本地客户端的简单请求。
// 简单select示例代码
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#define FD_SETSIZE 1024
int main() {
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(0, &read_fds); // 监听标准输入
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int ret = select(1, &read_fds, NULL, NULL, &timeout);
if (ret > 0) {
if (FD_ISSET(0, &read_fds)) {
char buf[1024];
read(0, buf, sizeof(buf));
printf("Read data: %s", buf);
}
} else if (ret == 0) {
printf("Timeout\n");
} else {
perror("select error");
}
return 0;
}
- 适合poll的场景:当服务器需要处理大量并发连接时,如大型的网络聊天服务器,可能同时有成千上万的客户端连接。此时poll在管理大量文件描述符和事件通知方面性能更优。
// 简单poll示例代码
#include <stdio.h>
#include <poll.h>
#include <unistd.h>
int main() {
struct pollfd fds[1];
fds[0].fd = 0;
fds[0].events = POLLIN;
int ret = poll(fds, 1, 5000); // 5秒超时
if (ret > 0) {
if (fds[0].revents & POLLIN) {
char buf[1024];
read(0, buf, sizeof(buf));
printf("Read data: %s", buf);
}
} else if (ret == 0) {
printf("Timeout\n");
} else {
perror("poll error");
}
return 0;
}