IO模型介绍
1、阻塞IO:scanf/printf fread/fwrite read/write cin/cout
2、非阻塞IO:recv/send QT中的read、write
3、多路复用IO:使用单进程(单线程)监控多个文件的读写模型,更多情况下使用在网络通信上
除此之外还有信号驱动IO、异步IO
多路复用
使用单进程(单线程)监控多个文件描述符的IO模型,多用于网络编程时服务端程序为若干客户端提供服务。
优点:
1、不需要频繁创建线程、进程和销毁线程、进程,节约时间和资源
2、由于只有单进程(单线程),节约的任务调度时间
缺点:
1、编程难度大
2、单个客户端的请求如果比较耗费时间,其他客户端会有阻塞的感受
因此它只适合并发高但响应时间短的情况,如:web服务器
使用select实现多路复用
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:监控多个文件描述符的读、写、异常等操作
nfds:被监控的文件描述符中最大的描述+1
readfds:需要监控读操作的描述符的集合
writefds:需要监控写操作的描述符的集合
exceptfds:需要监控异常的描述符的集合
timeout:倒计时时间
返回值:发生操作的描述符的个数,超时返回0,出错返回-1
注意:select监控的结果存储在readfds、writefds、exceptfds中
void FD_ZERO(fd_set *set);
功能:清空集合
void FD_CLR(int fd, fd_set *set);
功能:从集合中删除描述符
int FD_ISSET(int fd, fd_set *set);
功能:测试描述符是否在集合中
void FD_SET(int fd, fd_set *set);
功能:从集合中添加描述符
使用过程
设置文件描述符
指定监控范围
设置超时
调用select函数
查看监控结果
select设计不合理的地方
select在使用前,要先设置需要被监控的描述符,然后将其传给select,当有任何一个事件发生时,select将会返回所有的描述符,需要在应用程序自己去遍历检查哪个描述符上有时间发生,效率很低,并且不断在内核态和用户态进行描述符的拷贝,开销很大。
1、所有被监控的描述符都被检查
2、每次调用时都需要重新传递被监控描述符和超时时间
select优点
几乎所有的操作系统都支持,程序的兼容性高
pselect与select的区别
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
功能:与select基本一致
区别:
1、超时时间的格式不同
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
不需要每次都重新设置超时时间,也就是pselect不会修改timeout的参数
2、pselect在监控时可以通过sigmask设置屏蔽指定的信号
使用poll函数使用多路复用
通过一个可变长的数组解决了select文件描述符受限的问题。数组中元素是结构体,该结构体保存描述符的信息,每增加一个文件描述符就像数组中加 一个结构体,结构体只需要拷贝一次到内核态。
解决了select重复初始化的问题。但轮寻排查问题没有解决。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds:要监控的文件描述符的结构体数组
nfds:数组的长度
timeout:超时时间,单位是毫秒
struct pollfd {
int fd; 要监控的文件描述符
short events; 等待监控的事件
POLLIN 读事件
POLLPRI 高优先级的读事件
POLLOUT 写事件
POLLRDHUP 写操作关闭事件
POLLERR 错误事件
POLLHUP 关闭事件
POLLNVAL 描述符不是个打开的文件
short revents; 实际发生的事件,也就是返回结果
};
基于epoll的多路复用
epoll:由于轮寻排查所有描述符的效率不高,使服务器并发能力受限。因此epoll采用只返回状态发生变化的文件描述符,便解决了轮寻的问题。
int epoll_create(int size);
功能:创建用于存储被监控描述符的空间
size:要监控的描述符的数量
返回值:代表epoll空间的描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:向epoll添加、删除描述符
epfd:epoll_create的返回值
op:
EPOLL_CTL_ADD 添加描述符
EPOLL_CTL_MOD 修改要监控的事件
EPOLL_CTL_DEL 删除描述符
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; 要监控的事件
epoll_data_t data; 传递给内核的数据
};
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:监控描述符,并获取事件发生的描述符
epfd:epoll_create的返回值
events:用于存储监控的结果
maxevents:events数组的长度
timeout:超时时间
返回值:返回事件发生的描述符的数量
与其他多路复用相比的优点
1、文件描述符没有最大限制
2、只需要拷贝一次文件描述符到内核(速度快)
3、只返回事件发生的描述,使用方便