五种I/O模式 — 阻塞I/O 非阻塞I/O I/O多路复用 信号驱动I/O 异步I/O

五种I/O模式
●阻塞I/O
●非阻塞I/O 
●I/O多路复用  
●信号驱动I/O
●异步I/O

程序进行输入操作的步骤:
一般情况:

1、等待有数据可以读;
2、将数据从系统内核中拷贝到程序的数据区
sock编程来说:
1、一般来说是等待数据从网络上传到本地。当数据包到达的时候,数据将会从网络层拷贝到内核的缓存中;
2、是从内核中把数据拷贝到程序的数据区;

阻塞I/O模式
阻塞I/O是指,进程会一直阻塞,直到数据拷贝完成;阻塞I/O模式时最普遍使用的I/O模式;
当调用recv()函数时,系统首先查是否有准备好的数据。如果数据没有准备好,那么系统就处于等待状态。当数据准备好后,将数据从系统缓冲区复制到用户空间,然后该函数返回。在套接应用程序中,当调用recv()函数时,未必用户空间就已经存在数据,那么此时recv()函数就会处于等待状态。

当使用socket()函数和WSASocket()函数创建套接字时,默认的套接字都是阻塞的。这意味着当调用Windows Sockets API不能立即完成时,线程处于等待状态,直到操作完成。并不是所有Windows Sockets API以阻塞套接字为参数调用都会发生阻塞。例如,以阻塞模式的套接字为参数调用bind()、listen()函数时,函数会立即返回。将可能阻塞套接字的Windows Sockets API调用分为以下四种:
 1.输入操作: recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。
2.输出操作: send()、sendto()、WSASend()和WSASendto()函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。
3.接受连接:accept()和WSAAcept()函数。以阻塞套接字为参数调用该函数,等待接受对方的连接请求。如果此时没有连接请求,线程就会进入睡眠状态。
4.外出连接:connect()和WSAConnect()函数。对于TCP连接,客户端以阻塞套接字为参数,调用该函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。

优点:使用阻塞模式的套接字,开发网络程序比较简单,容易实现。当希望能够立即发送和接收数据,且处理的套接字数量比较少的情况下,使用阻塞模式来开发网络程序比较合适。

缺点:阻塞模式套接字的不足表现为,在大量建立好的套接字线程之间进行通信时比较困难。当使用“生产者-消费者”模型开发网络程序时,为每个套接字都分别分配一个读线程、一个处理数据线程和一个用于同步的事件,那么这样无疑加大系统的开销。其最大的缺点是当希望同时处理大量套接字时,将无从下手,其扩展性很差

在调用recv()/recvfrom()函数时,发生在内核中等待数据和复制数据的过程:

 

非阻塞模式I/O
非阻塞模式的使用并不普通,因为非阻塞模式会浪费大量的资源

         当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:当我请求的I/O操作不能够马上完成,请马上返回一个错误给我。我们开始对recvfrom的三次调用,因为系统还没有接收到网络数据,所以内核马上返回一个EWOULDBLOCK的错误,直到数据包到达,然后recvfrom正常返回,我们就可以读接收的数据进行处理了。

         当一个应用程序使用了非阻塞模式的套接字,它需要一个循环不断的测试一个文件描述符有数据可读(称做polling)应用程序不停的polling内核来检查是否I/O操作已经就绪。这将是一个极其浪费CPU资源的操作。这种模式使用中不太常见。


对管道的操作时,最好使用非阻塞的方式

I/O多路复用
        在使用I/O多路技术的时候,我们调用select()、poll()、epoll()(2.6内核开始支持),在调用它们的时候阻塞,而不是我们调用recvfrom(或recv)的时候阻塞。
        当我们调用select函数阻塞的时候,select函数等待数据报套接字进入读就绪状态。当select函数返回的时候,也就是套接字可以读取数据的时候。这时候我们就可以调用recvfrom函数来将数据拷贝到我们的程序缓冲区中。
        对于单个I/O操作,和阻塞模式相比较select()、poll()、epoll()并没有什么高级的地方。而且在阻塞模式在只需要调用一个函数,在使用了多路复用技术后,就需要先调用select()函数或poll()函数,然后才能进行真正的读写。
        优点:它能同时等待多个文件描述符其中的任意一个进入就绪状态,select()函数就可以返回了


I/O 多路技术一般在下面这些情况中被使用:
1、当一个客户端需要同时处理多个文件描述符的输入输出操作的时候(一般来说是标准的输入输出和网络套接字),I/O 多路复用技术将会有机会得到使用。
2、当程序需要同时进行多个套接字的操作的时候
3、如果一个 TCP 服务器程序同时处理正在侦听网络连接的套接字和已经连接好的套接字
4、如果一个服务器程序同时使用 TCP 和 UDP 协议
5、如果一个服务器同时使用多种服务并且每种服务可能使用不同的协议(比如 inetd就是这样的)。

信号驱动I/O模式
我们可以使用信号,让内核在文件描述符就绪的时候使用SIGIO信号来通知我们。我们将这种模式称为信号驱动I/O模式

为了在一个套接字上使用信号驱动 I/O 操作,下面这三步是所必须的。
(1)一个和 SIGIO信号的处理函数必须设定。
(2)套接字的拥有者必须被设定。一般来说是使用 fcntl 函数的 F_SETOWN 参数来
进行设定拥有者。
(3)套接字必须被允许使用异步 I/O。一般是通过调用 fcntl 函数的 F_SETFL 命令,O_ASYNC为参数来实现。

1.UDP 套接字的 SIGIO 信号                    (比较简单)
在 UDP 协议上使用异步 I/O 非常简单.这个信号将会在这个时候产生:
1、套接字收到了一个数据报的数据包。
2、套接字发生了异步错误;当我们在使用 UDP 套接字异步 I/O 的时候,我们使用 recvfrom()函数来读取数据报数据或是异步 I/O 错误信息。
2.TCP 套接字的 SIGIO 信号                   (不会使用)
          不幸的是,异步 I/O 几乎对 TCP 套接字而言没有什么作用。因为对于一个 TCP 套接字来说,SIGIO 信号发生的几率太高了,所以 SIGIO 信号并不能告诉我们究竟发生了什么事情。
在 TCP 连接中, SIGIO 信号将会在这个时候产生:
●  在一个监听某个端口的套接字上成功的建立了一个新连接。
● 一个断线的请求被成功的初始化。
●  一个断线的请求成功的结束。
● 套接字的某一个通道(发送通道或是接收通道)被关闭。
● 套接字接收到新数据。
● 套接字将数据发送出去。
● 发生了一个异步 I/O 的错误。
一个对信号驱动 I/O 比较实用的方面是 NTP(网络时间协议 Network Time Protocol)服务器,它使用 UDP。这个服务器的主循环用来接收从客户端发送过来的数据报数据包,然后再发送请求。对于这个服务器来说,记录下收到每一个数据包的具体时间是很重要的。因为那将是返回给客户端的值,客户端要使用这个数据来计算数据报在网络上来回所花费的时间。

异步I/O模式 (比如写操作,只需用写,不一定写入磁盘(这就是异步I/O)的好处。异步IO的好处效率高。)
      当我们运行在异步 I/O 模式下时,我们如果想进行 I/O 操作,只需要告诉内核我们要进行 I/O 操作,然后内核会马上返回。具体的 I/O 和数据的拷贝全部由内核来完成,我们的程序可以继续向下执行。当内核完成所有的 I/O 操作和数据拷贝后,内核将通知我们的程序。是一种非阻塞的状态。很少有Linux系统支持,Windows的IOCP就是该模型。 

异步 I/O 和  信号驱动I/O的区别是:
        1、信号驱动 I/O 模式下,内核在操作可以被操作的时候通知给我们的应用程序发送SIGIO 消息。
        2、异步 I/O 模式下,内核在所有的操作都已经被内核操作结束之后才会通知我们的应用程序。

可以看出,阻塞程度:阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO,效率是由低到高的

转载原文:https://blog.csdn.net/ZWE7616175/article/details/80591587 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值