I/O复用:进程预先告知内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,它就通知进程。
I/O复用典型使用在下列网络应用场合
- 当客户处理多个描述符,必须使用I/O复用
- 一个客户处理多个套接字
- TCP服务器既要处理监听套接字,又要处理已连接套接字。
- 一个服务器既要处理TCP又要处理UDP
- 一个服务器要处理多个服务或多个协议。
I/O模型
Unix下可用的五种I/O模型
- 阻塞式I/O
- 非阻塞I/O
- I/O复用(select.poll)
- 信号驱动I/O
- 异步I/O
阻塞式I/O
从调用recvfrom函数开始到他返回整段时间内是被阻塞的,recvfrom成功返回后,应用进程开始处理数据报。
可能的阻塞套接字调用分为4类:
- 输入操作:read、readv、recv、recvfrom、recvmsg
- 输出操作:write writev send sendto sendmsg
- 接收外来连接:accept函数
- 发起外出连接:connect函数
非阻塞式I/O
进程把一个套接字设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时。不要把本进程投入睡眠,而是立即返回一个错误
当一个应用进程对这样一个非阻塞的描述符循环调用的时候,称为轮询。
网络编程中socket可读的情况:
- soekt内核接收缓冲区中的字节数大于或等于其低水平标记,可以无阻塞地读socket,并且返回的字节数大于0.
- socket双方关闭通信连接,返回的字节数等于0.
- 监听socket上有新的连接
- socket上有未处理的错误
网络编程中socket可写的情况 - soekt内核发送缓冲区中的字节数大于或等于其低水平标记,可以无阻塞地写socket,并且返回的字节数大于0.
- socket的写操作关闭
- socket使用非阻塞connect连接成功或者失败
- socket上有未处理的错误
I/O复用模型
可以调用select或poll,阻塞在这两个系统调用中的某一个上,而不是真正阻塞在I/O系统调用上。如果不采用并发机制,当有多个描述符就绪的时候,程序只能按顺序依次处理。
select函数
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
函数功能:允许进程指示内核等待所关注的描述符上事件发生,并且只有在一个或多个事件发生时或经历一段时间后才才唤醒进程。
参数:
maxfdp1:被监听的文件描述符总数
timeout:告知内核等待所指定的描述符中任何一个就绪可以花费多长时间,若为0,检查描述符后立即返回,称为轮询。
readset:内核测试读描述符。
writeset:内核测试写描述符。
exceptet:内核异常描述符。对于其中某一个描述符不感兴趣,可设置为空指针。
fd_set的结构体包含一个整型数组,该数组的每一元素的每一位标记一个文件描述符,fd_set能容纳的描述符总量由FD_SETSIZE指定,这也限制了select能同时处理文件描述符的总量。
可以通过一系列宏来访问fd_set结构体中的位
#include<sys/select.h>
FD_ZERO(fd_set *fdset);//清空描述符集
FD_SET(int fd,fd_set *fdset); //设置fd_set的fd位
FD_CLR(int fd,fd_set *fdset); //清除fd_set的fd位
int FD_ISSET(int fd,fd_set *fdset); //测试fd_set的fd位是否被设置
poll函数
#include <poll.h>
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);
功能:与select类似
第一个参数指向结构数组的首地址,数组中每个元素都是一个pollfd结构。
struct pollfd {
int fd; /* descriptor to check */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
信号驱动I/O模型
利用信号,让内核在描述符就绪时发送SIGIO信号通知进程开始执行操作,这种模型称为信号驱动式I/O
针对一个套接字使用信号驱动是I/O要求进程执行以下3个命令
- 建立SIGIO信号的信号处理函数
- 设置该套接字的属主,使用fcntk的F_SETOWN设置
- 开启该套接字的信号驱动I/O
在UDP上使用信号驱动,SIGIO信号在发生以下事件时产生
- 数据报到达套接字
- 套接字上发生异步错误
在TCP上使用信号驱动,SIGIO信号在发生以下事件时产生
- 监听套接字上某个连接请求已经完成
- 某个断连接请求已经发起
- 某个断连接请求已经完成
- 某个连接已经半关闭
- 数据到达套接字
- 数据已经从套接字发送走
- 发声某个异步错误
异步I/O模型:不导致请求进程阻塞
这些函数的工作机制是:告知内核启动某个动作,并且让内核在整个操作完成时通知进程。
Tips:阻塞式、非阻塞式、I/O复用、信号驱动式都是同步I/O模型,因为真正的I/O操作都是将进程阻塞。