备注:三种模型的对比参考:点这里
select
-
函数原型
int
select
( int maxfdp, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout); -
参数
- maxfdp:数值为三个集(readfds,writefds,errorfds)中最大的描述符的值+1
- readfds:用来检查可读性的一组文件描述字,可为 NULL
- writefds:用来检查可写性的一组文件描述字,可为 NULL
- errorfds:用来检查是否有异常条件出现的文件描述字,可为 NULL
- timeout:计时设置,超时则返回值为0(注意:超时之后会将timeout初始化为0)
- timeout = NULL 时,无限阻塞,直到描述符状态改变或者收到信号,轮询返回对应值
- timeout.tv_sec = 0 且 timeout.tv_usec = 0 时,无限轮询,每次都返回对应值(需注意)
- 其他,等到一定时间后超时,轮询检查描述符状态,返回对应值
-
返回值
- 成功:返回检测到的事件个数(可为0)
- 失败:返回 -1
-
头文件
#include <sys/select.h>
-
相关函数
FD_ZERO
(fd_set * fdset); //置0文件描述符集fdsetFD_SET
(int fd, fd_set * fdset); //将 fd 放入 fdset 集FD_CLR
(int fd, fd_set * fdset); //从 fdset 集删除 fdFD_ISSET
(int fd, fd_set * fdset); //测试文件描述符fd是否在fdset集
-
man 中的示例
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int main(void) { fd_set rfds; struct timeval tv; int retval; /* Watch stdin (fd 0) to see when it has input. */ FD_ZERO(&rfds); FD_SET(0, &rfds); /* Wait up to five seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(1, &rfds, NULL, NULL, &tv); /* Don't rely on the value of tv now! */ if (retval == -1) perror("select()"); else if (retval) printf("Data is available now.\n"); /* FD_ISSET(0, &rfds) will be true. */ else printf("No data within five seconds.\n"); exit(EXIT_SUCCESS); }
poll
-
函数原型
int
poll
(struct pollfd* fds, nfds_t nfds, int timeout); -
参数
- fds,I/O描述符集
- nfds,待检测I/O数量
- timeout,计时设置
-
返回值
- 成功:返回检测到的事件个数(可为0)
- 失败:返回 -1
-
头文件
#include <poll.h>
-
相关结构体
- struct pollfd
struct pollfd { int fd; // 文件描述符 short events; // 等待事件 short revents; // 发生事件 };
- struct pollfd
-
事件(events)
值 含义 POLLIN 有数据可读 POLLPRI 有紧急数据可读 POLLOUT 数据可写 OLLRDHUP 流式套接字半关闭 POLLERR 错误事件 POLLHUP 挂起事件 POLLNVAL 描述符非法 POLLRDNORM 有普通数据可读 POLLRDBAND 有优先数据可读 POLLWRNORM 普通数据可写 POLLWRBAND 优先数据可写
epoll
-
相关函数
- epoll_create
- epoll_create1
- epoll_ctl
- epoll_wait
-
相关函数详解
- int
epoll_create
(int size);- 参数
- size:监测的描述符最大数量
- 返回值
- 正值表示的一个 epoll 文件描述符
- 参数
- int
epoll_create1
(int flags);- 参数
- flags:可为 0 或者 EPOLL_CLOEXEC(详情参考该博客)
- 返回值
- 正值表示的一个 epoll 文件描述符
- 参数
- int
epoll_ctl
(int epfd, int op, int fd, struct epoll_event *event);- 参数
- epfd:epoll 文件描述符(epoll_create() 的返回值)
- op:表示动作
- EPOLL_CTL_ADD:注册 fd 到 epfd;
- EPOLL_CTL_MOD:修改监听事件;
- EPOLL_CTL_DEL:从 epfd 中删除 fd;
- fd:需要监听的fd
- event:告诉内核需要监听什么事件
- 返回值
- 成功:返回 0
- 失败:返回 -1
- 相关结构体
struct epoll_event { __uint32_t events; //事件 epoll_data_t data; }; typedef union epoll_data { void *ptr; int fd; //文件描述符 uint32_t u32; uint64_t u64; } epoll_data_t;
- 参数
- int
epoll_wait
(int epfd, struct epoll_event * events, int maxevents, int
timeout);- 参数
- epfd:epoll 文件描述符( epoll_create() 的返回值)
- events:用来从内核得到事件的集合
- maxevents:表示每次能处理的最大事件数,告之内核 events 大小,maxevents 的值不能大于创建 epoll_create() 的 size
- timeout:计时设置
- 毫秒级
- 0:会立即返回
- -1:永久阻塞直到事件产生
- 返回值
- 成功:返回需要处理的事件个数(可为0)
- 失败:返回 -1
- 备注
- 使用时直接遍历 0至返回值 即可(由于注册到了 epfd 中,因此将 struct epoll_event 交于 epoll 管理,将发生事件的 struct epoll_event 提到 events 集的首部)
- man 中的示例
#define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int listen_sock, conn_sock, nfds, epollfd; /* Set up listening socket, 'listen_sock' (socket(), bind(), listen()) */ epollfd = epoll_create(10); if (epollfd == -1) { perror("epoll_create"); exit(EXIT_FAILURE); } ev.events = EPOLLIN; ev.data.fd = listen_sock; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) { perror("epoll_ctl: listen_sock"); exit(EXIT_FAILURE); } for (;;) { nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_pwait"); exit(EXIT_FAILURE); } for (n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_sock) { conn_sock = accept(listen_sock, (struct sockaddr *) &local, &addrlen); if (conn_sock == -1) { perror("accept"); exit(EXIT_FAILURE); } setnonblocking(conn_sock); ev.events = EPOLLIN | EPOLLET; ev.data.fd = conn_sock; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) { perror("epoll_ctl: conn_sock"); exit(EXIT_FAILURE); } } else { do_use_fd(events[n].data.fd); } } }
- 参数
- int
-
头文件
#include <sys/epoll.h>