IO多路复用
select系统调用
维护的是一个文件描述符集合,监测这些fd集合。
#include <sys/select.h> // 头文件
运行机制
将文件描述符集合复制到内核空间,然后对其进行遍历,查看可读,可写,错误事件,返回就绪事件总数。
select函数
select函数调用时需要五个参数,包括文件描述符集合总数nfds(最大是1024个),底层fd集合是数组实现的,fdset是比特位的集合。可读集合read_fds,可写集合write_fds,异常集合excep_fds,超时时间timeout。返回值是所有就绪事件的数量和。
fd_set fds, read_fds, write_fds, excep_fds; // fds是应用层,而其它三个都是内核层要进行操作的。
struct timeval timeout;
FD_ZERO(fds); // 首先所有标志位置0
FD_SET(listenfd, fds); // 将代表listenfd的进行置1
int maxfd = listenfd;
struct time_val timeout; // 如果为NULL,设定一直阻塞等待
timeout.tv_sec = 5; // 等待5s, 5s事件就继续执行
timeout.tv_usec = 0;
struct sockaddr_in client;
bezero(&client, sizeof(client));
socklen_t len = sizeof(client);
while(1){
read_fds = fds;
int nready = select(nfds, &read_fds, &write_fds, &excep_fds, &timeout); // 首先拿到这个,接下来就是处理这些集合,select会修改可读可写异常标志位
if(FD_ISSET(listenfd, &read_fds)) { // 考虑是否listenfds可读
int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
FD_SET(clientfd, fds); // 设置标志位,fds
if(clientfd > maxfd) maxfd = clientfd; // 这一步问题是解决回收连接之后的问题。
}
// 循环对fd集合进行读
for(int i = listenfd + 1; i < maxfd+1; ++i){
char buffer[1024] = {0};
int n = recv(i , buffer, 1024, 0);
if(n == 0){
close(i);
FD_CLR(i, &fds);
break;
}
}
}
注意
- fd阻塞和select阻塞不同,select只是分辨fd不会阻塞于读和写就行。
- select一般限制最多fd大小为1024