高级IO

高级IO:

五种IO模型:阻塞IO; 非阻塞IO; 信号驱动IO;异步IO;多路转接IO

IO操作分为两个过程:等待/数据拷贝

阻塞IO:

发起IO调用后,若不具备IO条件,则等待IO条件具备,拷贝数据后返回

非阻塞IO:

发起IO调用后,若不具备IO条件,则立即报错返回,若具备IO条件则立即拷贝数据后返回

信号驱动IO:

先定义IO信号处理,若IO条件具备则直接信号通知进程,发起调用,拷贝数据后返回

异步IO:

先定义IO信号处理,发起异步IO调用,直接返回。让别人进行IO等待,等待IO条件具备后拷贝数据

(与信号驱动的区别是拷贝数据是由别人来完成的)信号通知进程IO完成

**

阻塞:为了完成功能发起调用,如果当前不具备完成条件,则一直等待,直到完成后返回。

非阻塞:为了完成功能发起调用,如果当前不具备完成条件,直接报错返回;通常需要循环处理

阻塞与非阻塞:调用功能当前不具备完成条件是否立即返回。

同步:为了完成功能发起调用,如果当前不具备完成条件,则自己等待完成功能后返回

异步:为了完成功能发起调用,但是功能由别人来完成

同步与异步的区别:功能是否由自己来完成

同步操作通常都是阻塞操作

异步包含两种:

异步阻塞操作:等待别人完成操作

异步非阻塞操作:不等待别人完成操作

同步与异步的优缺点:

同步:流程控制简单,但是效率相对较低

异步:流程控制较难,但是效率相对较高

多路转接IO:(多路复用IO)

功能:是一种IO事件的监控,同时对大量的描述符进行事件监控,监控描述符是否具备IO条件;

作用:通过IO多路转接技术监控描述符,就可以得到描述符的状态

适用场景:有大量的客户端连接但是同一时间只有少量活跃的情景,并且每一个客户端的请求处理时间不会很长

模型:select模型/poll模型(被淘汰)/epoll模型

都是实现对大量描述符进行事件监控的操作

就绪:

对于可读事件来说,缓冲区中有数据到来就是读就绪;

(接收缓冲区中的数据大小大于低水位标记(一个字节),就会触发刻可读事件)

对于可写事件来说,缓冲区中有空闲空间就会写就绪;

(发送缓冲区中的空闲空间大小大于低水位标记(一个字节),就会触发刻可写事件)

select模型:三个功能(监控/添加描述符/移除描述符)

通过对大量事件集合中的描述符阻塞进行各自的事件监控,当对应集合中有描述符事件就绪/超时则返回

事件就绪表示:描述符当前可读/可写/异常

(返回之前将集合中没有就绪的描述符全部删除)

(描述符从小到大排列,包含了系统中所有的描述符)

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

nfds:等于监控的描述符中,最大的那个描述符+1;

readfds:读事件集合

writefds:写事件集合

exceptfds:异常事件集合

fd_set:描述符集合

timeout:select等待的超时时间

返回值:小于0—>监控出错        等于0—>等待超时        大于0—>当前有多少个描述符就绪

1.定义fd_set:描述符集合(是一个位图,位图大小取决于_FD_SETSIZE = 1024)

(由select完成)

2.将集合拷贝到内核进行监控,监控的原理是对所有的描述符进行轮询遍历状态

3.当有描述符就绪的时候,在调用返回之前将集合中没有就绪的描述符剔除出去

4.用户操作:对所有的描述符进行遍历,查看哪一个还在集合中,那么这个描述符就已经就绪

void FD_CLR(int fd, fd_set* set);    //将指定的描述符在集合中移除

int FD_ISSET(int fd, fd_set* set);    //判断指定的描述符是否在集合中

void FD_SET(int fd, fd_set* set);    //将指定的描述符添加到监控集合中

void FD_ZERO(fd_set* set);            //清空描述符集合

(使用C++封装select类)——代码实现

优点:

1.select遵循POSIX,可以跨平台——移植性强

2.监控的超时时间更加精细——微妙

缺点:

1.所能监控的描述符是有上限的(默认:1024,取决于_FD_SETSIZE = 1024)

2.select实现监控原理是在内核中进行轮询遍历状态,因此性能会随着描述符增多而下降

3.select每次监控返回时会修改描述符集合(移除未就绪的描述符),需要每次监控时重新添加到描述符集合中

4.select要监控的集合中的描述符数据,需要每次重新向内核中拷贝

5.不会告诉用户哪一个描述符就绪,只是告诉用户有就绪事件,需要用户遍历查找

poll模型:(被淘汰的技术)

int poll(struct pollfd *fds, nfds_t nfds, int timeout)

fds:事件数组

nfds:监控事件个数

timeout:超时等待时间

监控实现原理:

1.用户定义一个事件数组,对描述符可以添加关心的事件,进行监控

// pollfd结构

struct pollfd {

int   fd;            /* 用户监控的文件描述符 */

short events;         /* 保存用户关心的事件 */

/*(POLLIN / POLLOUT)*/

short revents;        /* 保存当前就绪事件 */

};

2.poll实现监控的原理也是将事件结构拷贝到内核,然后进行轮询遍历监控,性能随着描述符的增多而下降

3.若有描述符就绪,则修改这个响应描述符事件结构中的实际就绪事件

(用户完成:)

4.用户根据返回的revents判断哪一个事件就绪,然后进行操作即可

5.poll也不会告诉用户哪一个描述符就绪,只是告诉用户有就绪事件,需要用户遍历查找

优点:

1.采用事件结构的方式对描述符进行监控,简化了多个事件集合的监控方式

2.没有描述符的具体监控上限

缺点:

1.不能跨平台

2.poll采用轮询遍历的方式判断就绪,性能随着描述符的增多而下降

3.不会告诉用户哪一个描述符就绪,只是告诉用户有就绪事件,需要用户遍历查找

epoll模型;

int epoll_create(int size);//创建epoll

功能:在内核中创建一个结构体

struct eventpoll{

rbr-红黑树

rdlist

};

size:—能监控的描述符上限自从linux2.6.8之后,size参数是被忽略的.只要大于0就可以

返回值:文件描述符-非负整数-epoll的操作句柄

int epoll_ctl();

功能:向内核中epfd对应的eventpoll结构体中进行添加/移除/修改一个fd描述符所关心的事件.

也是采用事件结构体的形式监控描述符

epfd: 返回的epoll操作句柄

EPOLL_CTL_ADD:从内核中的evnetpoll中添加要监控的事件

EPOLL_CTL_MOD:修改内核中要监控的事件结构体

EPOLL_CTL_DEL:从内核中的evnetpoll中移除要监控的事件

fd:        用户索要监控的描述符

event:    描述符对应所要监控的事件

EPOLLLT(边缘触发):

EPOLLET(水平触发:默认):

data:描述符就绪后会返回事件结构体,用户可以获得这个数据

int epoll_wait();

功能:开始监控

返回值:大于0-就绪的事件个数    等于0-超时等待        小于0-出错

eventpoll{

rbr-红黑树-保存用户添加的事件结构结点

rdlist-双向列表-

};

原理:

1.告诉内核要开始监控了(异步操作)

2.操作系统对描述符进行监控—采用的是事件触发方式进行监控,为每一个监控的描述符都定义

了一个事件,并且对这个事件定义了一个事件回调函数

3.这个事件回调函数做的事就是:将就绪的描述符所对应的epoll_event事件结构添加到双向链表rdlist中

4.epoll_wait并没有立即返回,而是每个一会就看一下内核中的eventpoll中的双向链表

(保存的都是就绪的描述符所对应的事件)是否为空(查看是否就绪)

5.若链表不为空,则表示描述符就绪,epoll_wait即将返回

(在返回之前,将就绪的描述符对应事件结构向用户的结构体数组(epoll_wait的第二个参数)拷贝一份)

6.epoll会将对应的就绪描述符拷贝一份到用户态直接告诉用户有哪些描述符就绪,进而可以直接对就绪的描述符进行操作

epoll的触发方式:

1.水平触发:

是要缓冲区中的数据大小大小低水位标记,就会触发可读/可写就绪事件

2.边缘触发:

可读事件:每次只有新数据到来的时候才会触发移除可读事件(不关注缓冲区中数据有多少

要求用户一次将缓冲区中的数据读取完全)

可写事件:每次只有缓冲区空闲空间从0变为大于低水位标记时才会触发可写事件

通常读写事件混合监控的时候对于可写事件就会使用边缘触发,

防止可写事件每次在不写如数据但是有空闲空间都会触发事件(但是又没有数据可写)

若是可读事件被设置为边缘触发 ,需要用户一次将所有的数据读取完毕,但是因为不知道数据还有有多少

因此只能循环从缓冲区读取数据,当循环读取但是缓冲区没有数据的时候,recv就会阻塞;

因此边缘触发的可读事件的描述符通常需要被设置为非阻塞

(设置非阻塞:)

int fcntl(int fd, int cmd, .../* arg */);

cmd:

F_GETFL    获取描述符状态属性信息

F_SETFL    设置

优点:

1.没有监控的描述符上限

2.采用事件结构的方式对描述符进行监控,简化了多个事件集合的监控方式

3.epoll是一个异步阻塞操作,发起调用让操作系统进行监控,操作系统采用事件回调的方式

对描述符进行监控,避免了遍历轮询,所以性能不会随描述符上增多而降低

4.epoll发起调用后进行等待—循环判断内核中的就绪的事件是否为空来确定是否有事件就绪

若有事件就绪,就将对应事件拷贝到用户态供用户操作

直接告诉用户哪些描述符就绪了,直接操作,没有空遍历,提高性能,简化代码

5.epoll描述符的事件结构只需要向内核拷贝一次(内核eventpoll结构体的红黑树中),不要要每次拷贝

缺点:

1.不能跨平台

2.超时等待时间只能精确到毫秒

原文地址:https://www.cnblogs.com/cuckoo-/p/11397193.html

时间: 2024-08-30 14:54:38

高级IO的相关文章

高级IO编程

一.5种I/O模型 一个I/O可以分为两个过程:等待和数据搬迁. 1.阻塞I/O 两个过程都有自己来完成,其他什么事也不做. 2.非阻塞I/O 在等的过程中可以干一些其他的事,等到数据准备好再进行搬迁 3.信号驱动I/O 等数据准备好的过程由信号来触发,在这期间自己可以干一些别的事 4.I/O复用 同时阻塞多个I/O操作,谁数据准备好就对其进行数据搬迁. 5.异步I/O 将任务派发,不用自己来完成.完成之后通知自己就行. 二.高级I/O 非阻塞IO,纪录锁,系统V流机制,I/O多路转接(sele

(十一) 一起学 Unix 环境高级编程 (APUE) 之 高级 IO

. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编程 (APUE) 之 文件和目录 (四) 一起学 Unix 环境高级编程 (APUE) 之 系统数据文件和信息 (五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境 (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制 (七) 一起学 Unix 环境高级编程 (APUE)

java之十 高级IO流

java.io定义的输入/输出类列于下表: ObjectInputStream.GetField和ObjectOutputStream.PutField是Java2新添的内部类. java.io包还包含两个不受java2欢迎的类,这两个类没有在上表中列出:LineNumberInputStream和StringBufferInputStream.新代码不应该使用两个类. 下面是由java.io定义的接口: FileFilter接口是Java2新增的. java.io包中有很多类和接口.包括字节和

高级IO函数

fcntl函数 fcntl函数提供了对文件描述符的各种控制操作. 另一个常见的控制文件描述符属性和行为的系统调用是ioctl,ioctl比fcntl能够执行更多的控制. 但是,对于控制文件描述符常用的属性和行为,fcntl函数是由POSIX规范指定的首选方法. #include <fcntl.h> int fcntl(int fd, int cmd, ...); fd参数是被操作的文件描述符,cmd参数指定执行何种类型的操作.根据操作类型的不同,可能还需要第三个可选参数. 高级IO函数

高级IO,阻塞于非阻塞

1.1.非阻塞IO 1.阻塞与非阻塞 (1)阻塞:就是当前的函数要执行的话,需要某些条件,但是没有达到,就被阻塞住,内核挂起,当前进程暂停.CPU被拿去运行别的进程了.比如父进程执行wait这个阻塞函数,等待子进程结束后,去回收子进程剩余的8KB内存资源,如果这个时候子进程没有结束,父进程的wait就会被阻塞,因为条件没有达到,这个时候父进程就暂停了,内核就挂起了,CPU的时间就去执行别的进程了,不在父进程这里耗了,当子进程结束的时候,OS会给子进程的父进程发送一个SIGCHILD信号,这时父进

(51)LINUX应用编程和网络编程之六Linux高级IO

3.6.1.非阻塞IO 3.6.1.1.阻塞与非阻塞 阻塞:阻塞具有很多优势(是linux系统的默认设置),单路IO的时候使用阻塞式IO没有降低CPU的性能 补充:阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态. 阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. 有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的. 对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已. 非阻塞和阻塞的概念相对应,指在不能立刻得到

linux高级IO

背景: write函数把内存中的数据写入文件描述符,通常这些数据是内存中的连续区域,如果我们应用程序需要把内存中多个非连续区域的数据写入文件描述符,那么需要多次调用write函数,如果文件描述符是socket,那么需要发送多个数据包.这种情况下,write函数效率比较低. writev将多个数据存储在一起,将驻留在两个或更多的不连接的缓冲区中的数据一次写出去. writev函数声明: #include <sys/uio.h> ssize_t writev( int fd, const stru

Arduino101学习笔记(六)&mdash;&mdash; 高级IO

1.位移输出函数(8位) 输入value数据后Arduino会自动把数据移动分配到8个并行输出端. 其中dataPin为连接DS的引脚号, clockPin为连接SH_CP的引脚号, bitOrder为设置数据位移顺序, 分别为高位先入MSBFIRST或者低位先入LSBFIRST. //********************************************************************************************* //函数名称:void

Unix网络编程 高级IO套接字设置超时

我们知道,对于一个套接字的读写(read/write)操作默认是阻塞的,如果当前套接字还不可读/写,那么这个操作会一直阻塞下去,这样对于一个需要高性能的服务器来说,是不能接受的.所以,我们可以在进行读写操作的时候可以指定超时值,这样就读写操作就不至于一直阻塞下去. 在涉及套接字的I/O操作上设置超时的方法有三种: 1:调用alarm,它在指定的超时期满时产生SIGALRM信号.这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm调用. 2:在select中阻