I/O模型

I/O模型

阻塞I/O

对于一次I/O访问(如read()函数、write()函数等)来说,数据会被先复制到操作系统内核存储区域中,然后再从操作系统内核的存储区域中复制到应用程序的地址空间。因此,当一个read()函数操作发生时,通常会经历两个阶段:第一个阶段是等待数据准备;第二阶段是将数据从内核空间中复制到进程

阻塞I/O是当用户进程发起一个I/O请求操作(以读取为例),内核将会查看读取的数据是否准备就绪,如果没有准备好,则当前进程被挂起(睡眠),阻塞等待结果返回

I/O分为内存I/O,网络I/O和磁盘I/O三种。在网络I/O中,当用户进程调用recvfrom()这个系统调用,内核就开始I/O的第一阶段:准备数据。对于网络I/O来说,很多时候数据在一开始并没有到达,这个时候内核就要等待足够的数据到来。磁盘I/O的情况则是等待磁盘数据从磁盘读取到内核所访问的内存区域中。这个过程需要等待,也就是说数据被复制到操作系统内的存储区域是需要一个过程。而在用户进程这边,整个进程会被阻塞。当内核等到数据准备就绪,它会将数据从内核访问区域中复制到用户进程的内存中,之后内核返回结果。用户进程解除阻塞状态,重新运行

非阻塞I/O

非阻塞I/O与阻塞I/O不同的是,当用户进程执行读操作时,如果内核中的数据没有准备就绪,那么它并不会阻塞用户进程,而是立刻返回一个错误码。从用户进程角度讲,它发起一个读操作后,并不需要等待,而是马上得到一个结果。用户进程判断结果是一个错误码,它就知道数据还没有准备就绪,于是它可以再次发送读操作。一旦内核中的数据准备好了,并且再次收到用户进程系统调用请求,则它会立刻将数据复制到用户进程使用的内存

I/O操作函数将不断地测试数据是否已经准备就绪,如果没有准备就绪则继续,直到数据准备就绪为止。整个I/O请求的过程中,虽然用户进程每次发起I/O请求后可以立即返回,但是为了得到数据,仍需要不断地轮询、重复请求,这消耗了大量CPU的资源。所以,非阻塞I/O的特点是用户进程需要不断的主动询问内核数据是否准备就绪。在数据复制阶段,用户进程还是阻塞的

上面是网络I/O的情况。磁盘I/O思想与网络I/O完全一致。只是在磁盘I/O中,采用文件I/O的方式对硬件设备或文件进行操作时,其操作的核心为文件描述符,因此任何的读写操作都围绕文件描述符展开。当用户程序在对文件或硬件设备进行访问时,不希望采用等待(阻塞)的方式接收或发送数据,则需要通过一些设置,将访问机制设置为非阻塞

I/O多路复用

I/O多路复用指的是在一个程序中,跟踪处理多条独立的I/O流。所谓I/O流,可以理解为建立一次读或写的操作,I/O多路复用意在多个独立的输入\输出操作,其操作对象为文件描述符。I/O多路复用的出现很好地解决了服务器的吞吐能力。原因在于,当不使用I/O多路复用时,程序虽然可以处理完所有的I/O流,但是如果某个I/O流被设置为阻塞的,那么很可能会导致其他I/O操作无法执行。

假如所有的I/O流被设置为非阻塞,那么在资源并没有准备就绪的情况下,只能采用轮询的机制访问,CPU将一直被用来判断资源是否准备就绪,这无意增加了资源的浪费

I/O多路复用的特点是,并不会将I/O流(I/O操作)设置为阻塞或非阻塞。而是采用监听的方式监测所有I/O操作的对象(文件描述符),检测其是否可以被执行访问。当某一个文件描述符的状态为准备就绪时,则可以进行I/O操作。此时,并不影响其他I/O流

应用程序实现I/O多路复用可以采用select()函数实现,select()函数的工作原理为阻塞监听所有的文件描述符的状态是否发生变化。select()函数用来阻塞监听一个集合,该集合类型为fd_set。这个集合中存放的是文件描述符(文件句柄)。当集合中的文件描述符准备就绪时,函数立刻返回,其返回值为准备就绪的文件描述符的个数。select()函数做了一个很重要的工作,即将准备就绪的文件描述符留在原有监听集合中,而集合中没有就绪的文件描述符将会从集合中清除。因此,程序只需要判断文件描述符是否在集合中即可,如果文件描述符在集合中,表示该文件描述符准备就绪;反之,则未准备就绪

posted @ 2021-02-02 21:35  将来-小志  阅读(98)  评论(0编辑  收藏  举报