IO复用

(1).select

#include

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout)

nfds指被监听文件描述符总数(max_fd + 1),2,3,4参数分别代表可读、可写、异常文件描述符。timeout传入等待时间,传NULL即为阻塞等待。

select成功返回fd事件就绪fd的数量。select失败则返回-1并设置errno。select调用期间进程接受到信号,则select立即返回并且errno设置为EINTR。

读事件触发条件:

1.socket内核接受缓冲区中的字节数大于或等于低水位标记SO_RCVLOWAT。可以无阻塞地read该描述符,read返回大于0

2.socket通信的对端关闭连接,此时读操作返回0.

3.socket上有新的连接请求。

4.socket上有未处理的错误。调用getsockopt和setsockopt来处理错误。

写事件触发条件:

1.可以无阻塞地写该socket,write返回大于0.

2.socket写操作被关闭。

3.socket使用非阻塞connect连接成功或者失败后。

4.socket上有未处理的错误。调用getsockopt和setsockopt来处理错误。

异常触发条件:

socket上接受带外数据。

(2).epoll(linux),kqueue(freebsd)

#include

int epoll_create(int size) // 对应kqueue中的kqueue()

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event) // 对应kqueue中的EV_SET

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout) // 对应kqueue中的kevent

通常做法是将epoll_event里面的data的ptr成员设置为自己的一组结构体指针。比如对于server而言,可以存储连接socket的具体信息。

epoll_wait的maxevents指一次最多触发多少事件。timeout设置NULL则代表阻塞等待。epoll_wait失败则返回-1并设置errno。epoll_wait调用期间进程接受到信号,则epoll_wait立即返回并且errno设置为EINTR。

通常,epoll结合非阻塞的fd使用。由于epoll_wait通常运行在一个事件驱动的单线程的环境中,阻塞的fd可能会阻塞整个线程,低效。

epoll的事件操作有LT和ET模式。对于LT模式,当事件触发,即使write/read没有处理完成,下次循环仍然会触发该事件直到该事件处理完成。

对于ET模式,触发后必须立即处理完成该事件。因此,对于ET就绪的描述符,必须使用非阻塞fd,循环recv/send/accept直到返回-1且errno为EAGAIN或者EWOULDBLOCK ,否则会丢失事件。

对于异常或关闭的fd,从epoll中移除。epoll_ctl传入的是用户空间的fd,实际上在内核中引用的是内核对象。如果在用户空间中的文件描述符close则无法通过epoll_ctl移除该fd了,可能出现事件死循环触发的情况。

(3)非阻塞的IO操作

recv:

阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,

特别:非阻塞模式下返回 值 <0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况 下认为连接是正常的,继续接收。

只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要 循环读取。

write:

阻塞与非阻塞write返回值没有区分,都是 <0:出错,=0:连接关闭,>0发送数据大小,

特别:非阻塞模式下返回值 <0时并且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的, 继续发送。

只是阻塞模式下write会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会返回,不会阻塞着 write,因此需要循环发送。

read:

阻塞与非阻塞read返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,

特别:非阻塞模式下返回 值 <0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况 下认为连接是正常的,继续接收。

只是阻塞模式下read会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要 循环读取。

send:

阻塞与非阻塞send返回值没有区分,都是 <0:出错,=0:连接关闭,>0发送数据大小,

特别:非阻塞模式下返回值 <0时并且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的, 继续发送。

只是阻塞模式下send会阻塞着发送数据,非阻塞模式下如果暂时无法发送数据会返回,不会阻塞着 send,因此需要循环发送。

(4)非阻塞connect

对于非阻塞fd的connect系统调用中,errno为EINPROGRESS则将fd加入epoll循环。

你可能感兴趣的:(IO复用)