深入了解几种IO模型(阻塞非阻塞,同步异步)

一般来说,Linux下系统IO主要就是通过以下几个函数open(),close(),read(),write(),send(),recv(),lseek(),今天就以recv()为例来介绍下IO模型中的同步异步,阻塞非阻塞的区别。 先说阻塞与非阻塞的区别,recv()函数默认是阻塞的,什么是阻塞呢?就是当你调用recv()函数时,整个进程或者线程就等待在这里了,直到你recv的fd的所有信息都被send过来,这么做好处就是保证所有信息都能够完整的读取了,但劣势也很明显,就是在recv()的过程中你的进程或线程做不了其它事情,由此,引入了非阻塞IO。 非阻塞IO是什么呢,还是以recv()函数为例,当你将其设置为非阻塞时,每次当你recv()时,就直接返回,不管信息有没有完全send进来,好处很明显,recv()了之后进程马上能处理下一行代码,坏处也很明显,就是你不知道你的消息是否读完了,这种问题就是TCP中大名鼎鼎的半包问题(解决办法主要是通过一个buffer缓存所有读进来的消息) 打一个比方,你要去开水房接3/4杯水,在阻塞情况下,你需要等待你的杯子接够量,你才可以离开,当水是断断续续的时候,你就浪费了很多时间,因为在没水的这段时间你不能做其它事情,好处就是所有水接完就是刚刚好的3/4杯,在非阻塞情况下,你每次接水就接一些,只要没水了你就可以离开去做其它的事情,但需要你自己去看好量是不是够了3/4杯。阻塞和非阻塞的I/O模型图示如下: 阻塞IO模型:

非阻塞IO模型:

下面再说同步与异步的区别,在POSIX定义中把同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO,看概念感觉异步跟非阻塞好像也没有什么区别,要好好理解同步和异步,就要详细说明下IO过程:

IO过程主要分两个阶段:

1.数据准备阶段

2.内核空间复制回用户进程缓冲区空间 无论阻塞式IO还是非阻塞式IO,都是同步IO模型,区别就在与第一步是否完成后才返回,但第二步都需要当前进程去完成,异步IO呢,就是从第一步开始就返回,直到第二步完成后才会返回一个消息,也就是说,非阻塞能够让你在第一步时去做其它的事情,而真正的异步IO能让你第二步的过程也能去做其它事情。这里就在说一下select,poll和epoll这几个IO复用方式,这时你就会了解它们为什么是同步IO了,以epoll为例,在epoll开发的服务器模型中,epoll_wait()这个函数会阻塞等待就绪的fd,将就绪的fd拷贝到epoll_events集合这个过程中也不能做其它事(虽然这段时间很短,所以epoll配合非阻塞IO是很高效也是很普遍的服务器开发模式--同步非阻塞IO模型)。有人把epoll这种方式叫做同步非阻塞(NIO),因为用户线程需要不停地轮询,自己读取数据,看上去好像只有一个线程在做事情,也有人把这种方式叫做异步非阻塞(AIO),因为毕竟是内核线程负责扫描fd列表,并填充事件链表的,个人认为真正理想的异步非阻塞,应该是内核线程填充事件链表后,主动通知用户线程,或者调用应用程序事先注册的回调函数来处理数据,如果还需要用户线程不停的轮询来获取事件信息,就不是太完美了,所以也有不少人认为epoll是伪AIO,还是有道理的。 再生动一点,还是以接水为例,当你使用epoll的IO复用时,相当于接水方有了一个服务员,他会告诉你哪些水龙头有水可以接,然后你可以阻塞或者非阻塞(最好是非阻塞,不然epoll的意义就不大了)的去接水,但接水这个过程,还是要你自己完成。而异步的IO模型呢,相当于你去水房,直接把水杯递给服务员,让他给你接好后再通知你来,连接水的过程也不需要了,异步IO的模型如下图:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux非阻塞读是指在读取设备文件时,使用非阻塞方式进行读取操作。在非阻塞方式下,如果设备文件没有数据可读,则读取操作会立即返回,而不会阻塞进程。如果设备文件有数据可读,则读取操作会返回实际读取的字节数。\[2\] 在Linux中,可以通过在调用open()函数时添加O_NONBLOCK的mode参数来使用非阻塞方式读取设备文件。例如,使用以下代码可以打开终端设备文件并以非阻塞方式进行读取操作: ```c #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <stdio.h> int main(void) { char buf\[10\]; int fd, n; fd = open("/dev/tty", O_RDONLY | O_NONBLOCK); if (fd < 0) { perror("open /dev/tty"); exit(1); } n = read(fd, buf, 10); if (n < 0) { if (errno == EAGAIN) { // 如果设备文件没有数据可读,则返回EAGAIN错误 // 可以在这里进行其他操作,如等待一段时间后再次尝试读取 } else { perror("read /dev/tty"); exit(1); } } // 处理读取到的数据 close(fd); return 0; } ``` 在上述代码中,如果read()函数返回-1,并且errno被设置为EAGAIN,说明设备文件没有数据可读。可以根据需要进行相应的处理,如等待一段时间后再次尝试读取。\[1\] #### 引用[.reference_title] - *1* [linux下的阻塞非阻塞](https://blog.csdn.net/qq_45761493/article/details/120645210)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [linux下阻塞非阻塞文件读写](https://blog.csdn.net/weixin_44713496/article/details/124096950)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Linux下非阻塞读操作](https://blog.csdn.net/zxy131072/article/details/108534032)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值