linux网络编程之socket(八):五种I/O模型和select函数简介

一、五种I/O模型

1、阻塞I/O

我们在前面所说的I/O模型都是阻塞I/O,即调用recv系统调用,如果没有数据则阻塞等待,当数据到来则将数据从内核空间(套接口缓冲区)拷贝到用户空间(recv函数提供的buf),然后recv返回,进行数据处理。

2、非阻塞I/O

我们可以使用 fcntl(fd, F_SETFL, flag | O_NONBLOCK); 将套接字标志变成非阻塞,调用recv,如果设备暂时没有数据可读就返回-1,同时置errno为EWOULDBLOCK(或者EAGAIN,这两个宏定义的值相同),表示本来应该阻塞在这里(would block,虚拟语气),事实上并没有阻塞而是直接返回错误,调用者应该试着再读一次(again)。这种行为方式称为轮询(Poll),调用者只是查询一下,而不是阻塞在这里死等,这样可以同时监视多个设备:

while(1) 

非阻塞read(设备1);  if(设备1有数据到达) 

处理数据; 

非阻塞read(设备2);  if(设备2有数据到达) 

处理数据; 

..............................

}

如果read(设备1)是阻塞的,那么只要设备1没有数据到达就会一直阻塞在设备1的read调用上,即使设备2有数据到达也不能处理,使用非阻塞I/O就可以避免设备2得不到及时处理。

非阻塞I/O有一个缺点,如果所有设备都一直没有数据到达,调用者需要反复查询做无用功,如果阻塞在那里,操作系统可以调度别的进程执行,就不会做无用功了,在实际应用中非阻塞I/O模型经常与IO multiplexing 一起使用。

3、I/O复用

用select来管理多个I/O,当没有数据时select阻塞,如果在超时时间内数据到来则select返回,再调用recv进行数据的复制,recv返回后处理数据。

4、信号驱动I/O

先注册SIGIO信号的处理函数,进程继续执行其他操作,当数据到来时会发送SIGIO信号给进程,然后可以在信号处理函数中调用recv进行数据的复制,然后recv返回进行数据处理。

5、异步I/O

aio_read 函数也会提供一个buf,系统调用进入内核,如果没有数据则立即返回,进程继续执行其他操作,所以叫异步I/O,当数据到来时内核自动复制数据,然后推送给用户空间,通过在aio_read中指定的信号通知进程,让其处理数据。异步I/O跟信号驱动I/O的不同之处在于,它不用调用recv进行数据的复制,如果将后者比做”拉pull“,则前者可以认为是”push推“,push的效率会高点,其实异步I/O跟windows下面的完成端口差不多,但aio_read的实现或多或少存在问题,用得也比较少。实践中用得比较多的如boost 库的asio 也是异步IO。

脚注:同步和异步的区别在于是不是要求处理消息者自己来完成将数据从内核缓冲区复制回进程缓冲区的过程。消息者阻塞和非阻塞应该是发生在消息的处理的时刻。阻塞其实就是等待,发出通知,等待结果完成。非阻塞属于发出通知,立即返回结果,没有等待过程。

对unix来讲:阻塞式I/O(默认),非阻塞式I/O(nonblock),I/O复用(select/poll/epoll),信号驱动IO都属于同步I/O,因为它们在数据由内核空间复制回进程缓冲区时都是阻塞的(不能干别的事)。只有异步I/O模型(AIO)是符合异步I/O操作的含义的,即在1数据准备完成、2由内核空间拷贝回缓冲区后 通知进程,在等待通知的这段时间里可以干别的事。

POSIX defines these two terms as follows:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes. An asynchronous I/O operation does not cause the requesting process to be blocked. Using these definitions, the first four I/O models—blocking, nonblocking, I/O multiplexing, and signal-driven I/O—are all synchronous because the actual I/O operation (recvfrom) blocks the process. Only the asynchronous I/O model matches the asynchronous I/O definition.

二、select函数简介

/* According to POSIX.1-2001 */        #include <sys/select.h>        /* According to earlier standards */        #include <sys/time.h>        #include <sys/types.h>        #include <unistd.h>

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

参数1:读写异常集合中的文件描述符的最大值加1;

参数2:读集合,关心可读事件;

套接口缓冲区有数据可读 对等连接的写一半关闭。即接收到FIN段,读操作将返回0 如果是监听套接口,已完成连接队列不为空时。 套接口上发生了一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取。

参数3:写集合,关心可写事件;

套接口发送缓冲区有空间容纳数据。(连接一旦建立就可写) 对等连接的读一半关闭。即收到RST段之后,再次调用write操作。 套接口上发生了一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取。

参数4:异常集合,关心异常事件;

套接口存在带外数据(TCP头部 URG标志,16位紧急指针字段)

参数5:超时时间结构体

对于参数2,3,4来说,如果不关心对应事件则设置为NULL即可。注意5个参数都是输入输出参数,即select返回时可能对其进行了修改,比如集合被修改以便标记哪些套接口发生了事件,时间结构体的传出参数是剩余的时间,如果设置为NULL表示永不超时。用select管理多个I/O,select阻塞等待,一旦其中的一个或多个I/O检测到我们所感兴趣的事件,select函数返回,返回值为检测到的事件个数,并且返回哪些I/O发送了事件,遍历这些事件,进而处理事件。注意当select阻塞返回后,此时调用accept 接收连接是不会阻塞的,直接返回已连接套接字,可以认为是select 提前阻塞了。但此时调用write 还是可能阻塞的,因为需要写入的空间大小可能缓冲区还不满足。

下面是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); // 将集合清零

RETURN VALUE      On success, select() return the number of file descriptors contained in the three returned descriptor sets (that is,        the total number of bits that are set in readfds, writefds, exceptfds) which may be zero if the timeout  expires  before  anything        interesting  happens.   On error, -1 is returned, and errno is set appropriately; the sets and timeout become undefined, so do not        rely on their contents after an error.

select 函数的举例应用看这里

参考:

《Linux C 编程一站式学习》

《TCP/IP详解 卷一》

《UNP》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

5488
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.4K7
来自专栏魂祭心

原 canvas绘制clock

5184
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4405
来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

7306
来自专栏我和未来有约会

Kit 3D 更新

Kit3D is a 3D graphics engine written for Microsoft Silverlight. Kit3D was inita...

2936
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3318
来自专栏跟着阿笨一起玩NET

c#实现打印功能

3772
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

39210
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

6018

扫码关注云+社区