抚顺市网站建设_网站建设公司_测试上线_seo优化
2025/12/29 1:35:04 网站建设 项目流程
  • 五种IO模型
  • 同步通信vs异步通信
  • 非阻塞IO

我们在进行IO的时候,分为两个过程等待资源就绪+拷贝。
因此IO=等+拷贝。
那么所谓的高效IO,就是降低等的占比

五种IO模型

我先以生活中钓鱼的例子来形容五种IO模型。
来钓鱼的人会有各种不同的行为。
首先是我们的张三,这是个非常专注的钓鱼者,他会全神贯注盯着鱼竿,期间不做任何事。
随之而来的是李四,李四则是在钓鱼的时候刷刷视频,时不时看看鱼竿有没有动静。
王五钓鱼又不同于他们,他将一个铃铛系在鱼竿上,然后就专心干自己的事情不去理会鱼竿,只有铃铛响了他才会去收杆。
赵六则是方圆十里的首富,他带来一百根鱼竿一起钓鱼,不断来回跑看看哪根鱼竿上钓了。
最后是田七,田七则不享受钓鱼的过程,只想要鱼,因此他不参与钓鱼的过程,让自己的手下去钓鱼。

我们将上述钓鱼过程中的人看成系统调用,鱼竿就是socket,湖是系统内部,🐟相当于数据,鱼漂浮动就是数据就绪,钓就是发生拷贝。

那么上述五种钓鱼行为对应五种钓鱼模型。
张三:阻塞IO
李四:非阻塞IO
王五:信号驱动IO
赵六:多路复用/多路转接IO
田七:异步IO

事实上,阻塞IO才是最常见的IO,占90%以上。因为他十分简单。所有的套接字,默认都是阻塞方式。
上述IO效率最高的自然就是多路复用IO。

同步通信vs异步通信

同步通信synchronouscommunication
异步通信asynchronous communication

我们将上述五种IO模型中的前四种称为同步IO,最后一种称为异步IO。
这时候就有人提出异议了:“诶,前面信号部分不是说过信号是异步的吗,那为什么信号驱动IO不是异步的?”
这里就要明确一下,这两个异步和同步不是一个概念。

通信中的同步和异步关注的是消息通信机制:

  • 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就得到返回值了;换句话说,就是由调用者主动等待这个调用的结果;
  • 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果; 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果;而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用.

而我们进程、线程中的同步讲的是两种执行流的执行呈现一定顺序性。

非阻塞IO

我们这里浅浅讲一下非阻塞IO怎么实现。
我们正常打开open打开文件是阻塞方法打开的。注意到我们的open其实是可以传三个参数的:


我们传入O_NONBLOCK 或 O_NDELAY就能以非阻塞方式打开文件。

但是这样有违我们正常的编写习惯,这里还有个能将阻塞方法打开的文件描述符改为非阻塞的替代方案:

fcntl 函数有5种功能:
• 复制一个现有的描述符(cmd=F_DUPFD).
• 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
• 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
• 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
• 获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

我们此处只是用第三种功能,获取/设置文件状态标记,就可以将一个文件描述符设置为非阻塞.

如此我们就能实现一个将文件描述符设置为非阻塞的函数:

然后我们再编写一下主函数非阻塞轮询逻辑,还记得这个在我们进程等待的时候也用过非阻塞轮询的方法。
再次之前我们要知道read如果在非阻塞轮询时,没有读到数据会返回什么:

是的,读到数据就会出错返回。
那怎么区分真的出错还是没读到数据呢?
我们可以根据他设置的错误码不同,如果是没读到数据设置的错误码是:

EAGAIN 或EWOULDBLOCK。其实这两是一个值:


除此之外,read还有可能因为信号中断,这时错误码会设置为:

那么对这些特殊情况稍微处理一下之后:

intmain(){charbuffer[1024];SetNonBlock(0);while(true){printf("Enter# ");fflush(stdout);ssize_t n=::read(0,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;printf("echo# %s",buffer);}elseif(n==0)// ctrl + d{printf("read done\n");break;}else{if(errno==EWOULDBLOCK){sleep(1);std::cout<<"底层数据没有就绪,开始轮询检测"<<std::endl;std::cout<<"可以做其他事情"<<std::endl;continue;}elseif(errno==EINTR){continue;}else{perror("read");break;}}}return0;}

尝试运行:

非常完美!

但是非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符,这个过程称为轮询.这对CPU来说是较大的浪费,一般只有特定场景下才使用.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询