IO多路复用中select、poll、epoll
select
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout); maxfdp1:最大fd的index + 1,例如有3个文件描述符,分别是10,11,19, 这里maxfdp1 就是19+1 = 20 readset:读set,一个bitmap的数据结构,最长1024个元素,元素只有0、1,0代表的是未监听该fd,1代表的监听了该fd
writeset:写事件,如果不关心的话,可设置为NULL exceptset: timeout:超时时间
select函数是阻塞的,如果没有任何的文件描述符接受事件的话,会一直阻塞,直到有一个或者多个文件描述符有事件
readset会从用户态复制到内核态,内核态来判断是否有事件触发
poll
struct pollfd {
int fd;
short events; // 在意的事件。读事件POLLIN、写事件:POLLOUT、读和写:POLLIN&POLLOUT
short revents; //
}
// 初始化5个文件描述符
for(i = 0; i < 5; i++) {
memset(&client, 0, sizeof(client));
addrlen = sizeof(client);
pollfds[i].fd = accept(sockfd, (struct socketaddr*) &client, &addrlen);
pollfds[i].events = POLLIN;
}
sleep(1);
// 循环判断文件描述符是否发生变更
while(1) {
puts("round again");
poll(pollfds, 5, 50000); // 阻塞函数,如果接收到事件,则将对应的文件描述符的revents置位,置位为PULLIN
for(i = 0; i < 5; i++) { // 还是需要循环数组判断
if(pollfds[i].events & POLLIN) { // 判断revents是否被置位为POLLIN,true的话代表有数据
pollfds[i].revents = 0; // 恢复为0
memset(buffer, 0, MAXBUF);
read(pollfds[i].fd, buffer, MAXBUF); // 读取数据并处理
puts(buffer);
}
}
}
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
fds:文件描述符数组
nfds:数组个数
timeout:超时时间
epoll
最新的一种多路IO复用的函数
struct epoll_event events[5];
int epfd = epoll_creat(10);
for(i = 0; i < 5; i++) {
static struct epoll_event ev;
memset(&client, 0, sizeof(client));
addrlen = sizeof(client);
ev.data.fd = accept(sockfd, (struct socketaddr*) &client, &addrlen);
ev.events = EPOLLIN; // 指定fd监听的事件类型
epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); // 配置epollfd,初始化epfd结构,里面是一批fd_events的结构
}
while(1) {
puts("round again");
// 用户态和内核态共享epfd的这块内存,如果有某个fd有数据进来,则会进行epfd中数据的重排,有数据的排在前面,没数据的排在后面
// 返回的nfds是有数据触发的fd的总数
nfds = epoll_wait(epfd, events, 5, 10000);
for(i = 0; i < nfds; i++) { // 只需要遍历前面几个有数据的fd
memset(buffer, 0, MAXBUF);
read(events[i], data.fd, buffer, MAXBUF);
puts(buffer);
}
}