socket I/O模型

socket I/O模型

在Linux环境下,阻塞和非阻塞的概念能应用于所有文件描述符,socket也是如此。阻塞的文件描述符称为阻塞I/O,非阻塞的文件描述符称为非阻塞I/O。socket在创建的时候默认是阻塞的,可以通过socket系统调用的第二个参数传递SOCK_NONBLOCK标志,或通过fcntl系统调用的F_SETFL命令,将其设置为非阻塞的。

针对阻塞I/O执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。比如,客户端通过connect向服务器发起连接时,connect将首先发送同步报文段给服务器,然后等待服务器返回确认报文段。如果服务器的确认报文段没有立即到达客户端,则connect 调用将被挂起,直到客户端收到确认报文段并唤醒connect调用。socket 的基础API中,可能被阻塞的系统调用包括accept、send、 recv 和connect。
针对非阻塞I/O执行的系统调用则总是立即返回,而不管事件是否已经发生。如果事件没有立即发生,这些系统调用就返回-1,和出错的情况一样。此时我们必须根据errno来区分这两种情况。对accept、send和recv而言,事件未发生时errno通常被设置成EAGAIN (意为“再来一次”)或者EWOULDBLOCK (意为“期望阻塞”);对connect而言,设置成EINPROGRESS (意为“在处理中”)。
很显然,我们只有在事件已经发生的情况下操作非阻塞I/O (读、写等),才能提高程序的效率。因此,非阻塞I/O通常要和其他I/O通知机制一起使用,比如I/O复用和SIGIO信号。

I/O复用是最常使用的I/O通知机制。它指的是,应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序。Linux上常用的1/O复用函数是select、poll 和epoll_wait。需要指出的是,I/O复用函数本身是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个I/O事件的能力
SIGIO信号也可以用来报告I/O事件。 我们可以为一个目标文件描述符指定宿主进程,那么被指定的宿主进程将捕获到SIGIO信号。这样,当目标文件描述符上有事件发生时,SIGIO 信号的信号处理函数将被触发,我们也就可以在该信号处理函数中对目标文件描述符执行非阻塞I/O操作了。

从理论上说,阻塞I/O、I/O 复用和信号驱动I/O都是同步I/O模型。因为在这三种I/O模型中,I/O 的读写操作,都是在I/O事件发生之后,由应用程序来完成的。而POSIX规范所定义的异步IO模型则不同。对异步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复用程序阻塞于I/O复用系统调用,但可同时监听多个I/O事件。对I/O本身的读写操作是非阻塞的
SIGIO信号信号触发读写就绪事件,用户程序执行读写操作。程序没有阻塞阶段
异步I/O内核执行读写操作并触发读写完成事件。程序没有阻塞阶段
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_200_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值