参考:知乎
阻塞指线程被挂起,关注程序调用结果返回的状态
同步和异步指的是调用后是否立即返回,是否得到结果,关注的是消息机制
1.阻塞式IO
程序一直阻塞在读写函数的调用上,一直等待内核数据复制完成。阻塞同步
2.非阻塞式IO
进程反复调用recvform轮询,直到返回成功指示。非阻塞同步
3.IO复用
进程阻塞在select调用,但IO可读写时,调用读写处理。
4.信号驱动IO
调用前准备信号处理,在IO事件发生时递交信号,交由信号处理。
5.异步IO
异步IO是由内核通知我们IO何时完成。
select:select
#includeint pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, const struct timespec *restrict timeout, const sigset_t *restrict sigmask); int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout); void FD_CLR(int fd, fd_set *fdset); int FD_ISSET(int fd, fd_set *fdset); void FD_SET(int fd, fd_set *fdset); void FD_ZERO(fd_set *fdset);
poll:poll
#includeint poll(struct pollfd fds[], nfds_t nfds, int timeout);
#include对于select,poll何时能够让文件描述符变为就绪。SUSv3规定:如果对IO函数调用不会阻塞,无论是否能够传输数据,就指定为就绪。#include /* Open STREAMS device. */ fds[0].fd = open("/dev/dev0", ...); fds[1].fd = open("/dev/dev1", ...); fds[0].events = POLLOUT | POLLWRBAND; fds[1].events = POLLOUT | POLLWRBAND; ret = poll(fds, 2, timeout_msecs); if (ret > 0) { /* An event on one of the fds has occurred. */ for (i=0; i<2; i++) { if (fds[i].revents & POLLWRBAND) { /* Priority data may be written on device number i. */ ... } if (fds[i].revents & POLLOUT) { /* Data may be written on device number i. */ ... } if (fds[i].revents & POLLHUP) { /* A hangup has occurred on device number i. */ ... } } }... struct pollfd fds[2]; int timeout_msecs = 500; int ret; int i;
select,poll的比较:
1.select的fd_set有个1024的默认上限。
2.循环中重复调用时,select重复初始化fd_set;poll通过独立的evenets(针对输入)和revenets(针对输出)处理,避免重复调用。
3.select提供超时精度微秒,而poll为毫秒
4.当一个被检查的文件描述符关闭时,poll的revents会设置POLLNVAL标记,会准确告知那个文件关闭;select只会返回1,设置错误码为EBADF,只有自己通过在描述符上执行IO检查错误码才能判断。
select和poll存在的问题:
1.检查所有被指定的文件描述符
2.传递表示被检查的文件描述符的数据结构给内核,会随文件描述符增大而增大,占用拷贝时间等等
3.而这调用后必须检查每个元素查明文件描述符的就绪态。
epoll:epoll
epoll支持ET和LT模式,select,poll只支持LT,信号驱动IO支持ET;并且在大量描述符检查时性能比select和poll更优
#define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int listen_sock, conn_sock, nfds, epollfd; /* Code to set up listening socket, 'listen_sock', (socket(), bind(), listen()) omitted */ epollfd = epoll_create1(0);//epoll新创建方式 if (epollfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); }//防御式编程,每次调用系统函数用这种形式判断调用是否成功。 ev.events = EPOLLIN;//检测的事件 ev.data.fd = listen_sock;//检测的fd 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);//最大返回max_evenets个就绪的fd if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } for (n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_sock) { conn_sock = accept(listen_sock, (struct sockaddr *) &addr, &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); } } }这里有一篇:epoll的LT和ET模式的参考 ET和LT