【Linux网络编程学习】阻塞、非阻塞、同步、异步以及五种I/O模型

文章目录

  • 1. 基本概念
    • 1.1 阻塞与非阻塞
    • 1.2 同步与异步
    • 1.3 为什么没有“异步阻塞”
  • 2. 五种IO模型
    • 2.1 阻塞 blocking
    • 2.2 非阻塞 non-blocking
    • 2.3. IO复用(IO multiplexing)
    • 2.4 信号驱动(signal-driven)
    • 2.5 异步(asynchronous)

1. 基本概念

首先回顾并理解一下网络IO中阻塞、非阻塞;同步、异步的概念。

首先我们要知道:一个典型的网络IO接口调用,分为两个阶段,分别是“数据就绪”和“数据读写”。
阻塞与非阻塞是“数据就绪”阶段的概念,同步与异步是“数据读写”阶段的概念。

1.1 阻塞与非阻塞

所谓阻塞,就是IO在没有准备好数据的时候,调用IO方法的线程会被阻塞,直至收到数据时解除阻塞;而非阻塞IO在没有准备好数据的时候IO方法一样会返回,线程不会被阻塞。以recv方法为例,我们一般这样调用:

int size = recv(sockfd, buf, 1024, 0);

我们创建的sockfd默认是阻塞的,那么当执行这个函数时,如果sockfd对应的socket上数据没有就绪(即TCP接收缓存区中无数据),则该线程会阻塞,再往下的代码不会执行;但如果我们使用fcntl设置sockfd对应的socket为非阻塞,该socket工作在非阻塞状态下,那么执行该函数时,无论是否有数据准备好,该函数都会返回,然后我们可以通过返回的size值和errno来判断是否读到数据:

size == -1 && errno == EAGAIN: 表示没有读到数据,是因为非阻塞才返回;
size == 0:表示对端断开连接;
size > 0 : 表示读到数据,size为数据的长度;

1.2 同步与异步

再来说网络IO的同步和异步。刚才我们讨论阻塞和非阻塞时,关注的是数据准备阶段且数据没有准备好的情况,现在我们讨论同步和异步,关注的是数据已经准备好(TCP接收缓存区中有数据),进入数据读写阶段时的情况。

同步:同步表示A向B请求调用一个网络IO接口时,数据的读写都是由请求方A自己来完成的(不管是阻塞还是非阻塞)
比如调用recv方法(recv是同步IO方法)的时候其检测到数据已经准备好,然后把数据从内核的TCP接受缓存区拷贝到我们传入的buf(recv方法的第二个参数)中,这一拷贝过程耗费的是调用recv的线程的时间,直至拷贝结束,recv才返回;

异步:异步表示A向B请求调用一个网络IO接口时,向B传入请求的事件以及事件发生时通知的方式,A就可以处理其它逻辑了,当B监听到事件处理完成后,会用事先约定好的通知方式,通知A处理结果。
比如某个应用程序调用了一个异步IO的API来处理数据读入(如aio_read),那么应用程序只需把需要监听的sockfd给内核传入,内核来负责数据的准备和读入。在此期间,应用程序可以做自己的事情。数据读入完毕以后,再通过信号等方式来通知应用程序。

1.3 为什么没有“异步阻塞”

把阻塞、非阻塞,同步、异步两两组合有:

  • 同步阻塞
  • 同步非阻塞
  • 异步阻塞
  • 异步非阻塞

其中,所有平时使用的Linux网络IO的API都是同步的(如write、read、recv等等),我们可以通过设置其关注的socket为阻塞或者非阻塞,这样就有了同步阻塞和同步非阻塞的API。

但是异步的API一定是非阻塞的,因为异步IO是要让内核帮我们完成数据的读写,让调用它的应用程序可以去做自己的事情,如果异步的同时又阻塞,那么应用程序无法做自己的事情,异步就失去了意义。

2. 五种IO模型

有了以上的知识铺垫,再学习Linux的五种IO模型就通畅很多。

2.1 阻塞 blocking

调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作。下图以read为例。
在这里插入图片描述

2.2 非阻塞 non-blocking

非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。非阻塞I/O执行系统调用总是立即返回,不管事件是否已经发生,若事件没有发生,则返回-1,此时可以根据 errno 区分这两种情况,对于accept,recv 和 send,事件未发生时,errno 通常被设置成 EAGAIN。
在这里插入图片描述

2.3. IO复用(IO multiplexing)

Linux 用 select/poll/epoll 函数实现 IO 复用模型,这些函数也会使进程阻塞,但是和阻塞IO所不同的是这些函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。直到有数据可读或可写时,才真正调用IO操作函数。
在这里插入图片描述

2.4 信号驱动(signal-driven)

Linux 用套接口进行信号驱动 IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO 信号,然后处理 IO 事件。
在这里插入图片描述
注意区分信号驱动和异步IO的区别:信号驱动中,内核在数据准备阶段是异步,但在数据读写阶段是同步,仍然要花费应用程序自己的时间;与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断的轮询检查,减少了系统API的调用次数,提高了效率。

2.5 异步(asynchronous)

Linux中,可以调用 aio_read 函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。从图中可以看出,异步IO是内核负责把数据从内核空间拷贝到用户空间,不耗费应用程序自己的时间,而前面四种IO方式在数据读写阶段都需要应用程序自己负责。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LRU缓存 数据结构设计(C++)

做LeetCode第146题LRU缓存,觉得收获不小,特此记录。 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。int get(int key) 如果关键…

STM32时钟树解析

本人之前其实也用STM32做过一些小东西,但因为时钟的初始化一般是直接在SystemInit时钟系统初始化函数里直接配置为72MHz,所以对于STM32的时钟框图并没有怎么理会,今天刚好有空就重新看了一下并写一篇博客记录一下吧,以免以后又忘了…

S3C2440时钟体系

S3C2440在默认情况下,整个系统全靠一个12MHz的外部晶振提供频率来工作运行的,也就是说CPU、内存、UART、ADC等所有需要用到时钟频率的硬件都工作在12MHz下,但是通过查阅芯片手册我们知道CPU时钟最高可为400MHZ,那么怎么设置时钟让…

关于MCU、CPU扩展SDRAM的一个小知识

像上图这种硬件电路图上的16个数据位和我们在初始化SDRAM的时候设置的16位数据位宽是指我们读写SDRAM的时候可以同时读写16个数据位,数据线越多肯定越快,但是数据线也不可能无限增加,我们在程序里是可以读写8位,16位,3…

S3C2440扩展SDRAM

本文主要目的是记录一下S3C2440扩展SDRAM的一些知识,方便以后查阅。 通过查阅手册我们知道,2440有8个可以用来扩展内存的BANK,其中第6和第7还可用来扩展SDRAM 下面我们来看一下2440扩展SDRAM需要设置哪些寄存器。 一、BWSCON寄存器 该寄存器…

汇编语言的相对跳转和绝对跳转以及反汇编代码解析

上图第一行的b1 main为相对跳转,即跳转到pcoffset,其中pc为当前pc值,offset可以理解为偏移地址,也就是根据当前所在地址加上偏移地址实现跳转,为相对跳转。 我们来看看它的反汇编代码 上图清除完bss区后使用b1指令跳转到30000668…

韦东山嵌入式第一期14课第004节_und异常模示程序示例_P笔记

本节课的第一个程序韦老师是想让大家见识一下未定义异常,而第二个程序是对第一个程序进行改进,防止在某些条件下执行不了,下面就来讲一下第2个程序改进了哪些地方并且有什么用。 程序在此路径中:源码文档图片\源码\源码_20180321…

关于NOR FLASH地址左右移的问题

问题引入:不知道你会不会有这样的疑问:为什么在发送解锁命令时,我们不用右移一位,而发送扇区地址时却要右移一位(nor_cmd函数内部已经左移一位),这里先补充说明一下说明是cpu角度和nor角度&…

在linux下利用ls命令进行模糊查找

如上图,我们当前路径下有三个文件,分别为helloworld.c以及helloworld和1.c,直接输入命令ls则显示所有文件,我们可以利用ls 加*的方向进行模糊查找。 输入ls 目录名 形式的命令行,则是对该目录名下的文件全部进行显示&a…

Makefile常见符号意思

Makefile里有许许多多的符号,对于新手而言如果没有经常使用,就很容易忘记,所以我把常见符号的意义写下,方便日后忘记查询。本文章会持续更新... 1.$:代表目标;$^代表所有依赖,$^代表第一个依赖。…

Linux下串口通信详解

https://blog.csdn.net/u010783226/article/details/73369097

fstat、stat和lstat 区别

nt fstat(int filedes, struct stat *buf); int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); 一眼就能看出来fstat的第一个参数是和另外两个不一样的,fstat区别于另外两个系统调用的地方在于,fstat系…

Linux的帧缓冲设备

Linux的帧缓冲设备 帧缓冲(framebuffer)是 Linux 为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的…

Linux下没有包含头文件(不知是哪个)导致编译无法通过的解决心得

最近写程序的时候编译出错了,提示信息为:invalid use of undefined type fb_var_screeninfo。显示根据英文知道是没有定义 fb_var_screeninfo这个类型,明显是缺少了某个头文件,但是缺少哪个头文件以及有什么又快又好的解决方法呢&…

gcc编译缺少数学库

Linux下编译出现以下提示可以在编译的后面加上-lm,例如,arm-none-linux-gnueabi-gcc -o example1 example1.c -lm,意思就是添加数学库的意思,编译就能通过了 example1.c:(.text0x3e8): undefined reference to cos example1.c:(.…

Linux编译程序时加-I指定头文件位置

Linux下编译出现以下错误,错误的原因是在/usr/local/arm/arm-2009q3/bin/../arm-none-linux-gnueabi/libc/usr/include/freetype/config/下找不到ftheader.h,而我到该目录下看,发现路径是这样的rootubuntu:/usr/local/arm/arm-2009q3/arm-non…

树莓派远程监控的实现

原文:https://blog.csdn.net/ayz123456/article/details/79252923 http://shumeipai.nxez.com/2016/09/01/raspberry-pi-motion-cameras-for-remote-monitoring.html https://blog.csdn.net/wto882dim/article/details/82195001 https://blog.csdn.net/qq_3950082…

公网访问树莓派

公网访问树莓派控制小车 上篇已经介绍了小车在局域网中的控制方法,比较简单,既然是远程遥控那就要能够进行公网访问,使得你的小车可以在任何有网络的地方都能访问到,并且后续还会加上摄像头,进行实时监控,想…

关于对象的引用作为参数,可以直接访问私有成员的问题

#include using namespace std; class CPoint { public:CPoint(int xx, int yy){x xx;y yy;}CPoint(const CPoint &p){x p.x;y p.y;} private:int x, y; };首先,我们来看一个例子,在CPoint这个类中定义了两个构造函数,第一个为普通的…