IO读写在用户缓冲区和内核缓冲区交互

阻塞IO在两个阶段都会阻塞等待:数据就绪,数据拷贝(内核到用户)
1、用户调用
recvfrom发起系统调用请求内核2、等待数据就绪(没有准备好数据则会阻塞)
3、从内核拷贝数据到用户(没有完成则会阻塞等待)
4、拷贝完成发送标记给用户,处理数据

相较于阻塞IO,非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户

如果数据没有准备就绪,那么不会阻塞用户而是返回block标记,反复请求recvfrom
1、进程需要反复调用
recvfrom,等待数据准备、拷贝完成后结束2、频繁的
recvfrom相较于阻塞IO并没有提高效率,并且盲等机制会造成CPU空转
无论是阻塞IO还是非阻塞IO,差别在于第一阶段准备数据就绪的处理不同;
比如服务器处理客户端Socket请求时,在单线程情况下,一次只能处理一个Socket,一旦正在处理的Socket未就绪(不可读或者不可写)线程便会阻塞,这是所有的客户端Socket都必须等待,性能自然会很差;

类比用户排队点餐,阻塞IO即第一个用户在思考要吃什么,后续的用户都会阻塞等待,而非阻塞IO的区别在于前台会一直询问你想好了吗
解决方案
1、增加更多的服务员(多线程,资源消耗大)
2、不排队,谁想好了吃什么(数据就绪),服务员就给谁点餐(处理数据)
用户进程如何知道资源准备就绪?
文件描述符简称
FD是一个从0开始递增的无符号整数,用来关联Linux中的每一个文件,在Linux中一切皆文件,如常规文件,音频硬件设备,当然也包括Socket网络套接字
多路复用是利用单个线程来同时监听多个
FD,从而在某个FD可读或者可写时得到通知,避免CPU进行无效的等待

执行
1、进程调用
select同时监听多个Sockets,数据准备就绪后返回readbale可读标记2、进程反复调用
recvfrom等待返回成功标志
三者的区别在于
select和poll只知道通知用户进程有FD就绪,而不知道具体是哪个FD而epoll则会通知用户进程FD就绪的同时,把已经就绪的FD写入用户空间
select
这里的FD存储单位是1比特,可以存储1024个比特位的FD标识;
参数包括:
1、监听的
FD集合(读写和异常集合)2、监听的最大fd+1
3、超时时间

流程
1、创建
fd_set即1024比特的监听数组2、标记监听的
fd(由低位到高位)3、执行
select函数参数有读写异常数组、标记个数、最大fd+14、将
fd_set拷贝到内核空间进行监听,等待超时后清除未就绪的标记,将fd_set拷贝回用户空间5、用户空间仍然不知道那些是标记的,需要遍历得到结果
存在的问题
1、需要在用户态拷贝到内核态后,再次拷贝回用户态,且用户态无法得知被标记的
fd,需要遍历2、监听的数量只有1024
poll性能提升相较于select仍然没有较大提升

IO流程
1、创建
pollfd数组,向其添加关注的fd信息,数组大小自定义2、调用
poll函数,将pollfd数组拷贝到内核态,链表存储无上限3、遍历fd,判断是否就绪
4、数据就绪或者超时后,拷贝
pollfd数组到用户空间,返回就绪fd的数量n4、用户进程判断n是否大于0
5、大于0则遍历pollfd数组,找到就绪的
fd
存在的问题
相较于
select监听,监听的数量提升了,但是遍历fd效率会降低
epoll
流程
1、
epoll_create会在内核创建
eventpoll结构体(存储fd集合的红黑树,存储就绪fd集合的链表)2、
epoll_ctl(添加监听的fd,关联callback事件)callback触发,将就绪的fd添加到
redlist3、
epoll_wait检查redlist不为空,返回个数并拷贝就绪的fd集合到events中
