【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,一经查实,立即删除!

相关文章

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…

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

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

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…

关于源文件用不同的编码方式编写,会导致执行结果不一样的现象及解决方法

如果我们编写以下程序,并分别另存为ANSI和UTF-8两种不同的编码方式保存,放到Linux下编译并运行如下图,两端相同的程序以不同的编码方式保存编译后的运行结果不一样,./ansi采用ANSI编码方式,会自动采用GBK方式来保存中文…

arm-linux-gcc静态编译和动态编译的区别

很多教程会提到加上-static是静态编译,但对于新手来说没有用例子来说明可能不太好理解,今天我就介绍一下关于这方面知识的一个例子: 最近在做一个关于freetype字体的东西,需要依赖freetype官方提供的库,我已经把电脑这…

从0到1写RT-Thread内核——线程定义及切换的实现

从0写RT-Thread内核之线程定义及切换的实现具体可以分为以下六步来实现 一:分别定义线程栈、线程函数、线程控制块; ALIGN(RT_ALIGN_SIZE)//设置4字节对齐 /* 定义线程栈 */ rt_uint8_t rt_flag1_thread_stack[512]; rt_uint8_t rt_flag2_thread_stack…

从0到1写RT-Thread内核——临界段的保护

临界段就是一段在执行的时候不能被中断的代码段,在RT-Thread里,临界段最常出现的就是对全局变量的操作(类似Linux下的锁)。RT-Thread对临界段的保护是直接把中断全部关了,NMI FAULT和硬FAULT除外。下图是3个关于中断屏…

从0到1写RT-Thread内核——空闲线程与阻塞延时的实现

在之前写的另外一篇文章——<从0到1写RT-Thread内核——线程定义及切换的实现>中线程体内的延时使用的是软件延时&#xff0c;即还是让CPU空等来达到延时的效果。RTOS中的延时叫阻塞延时&#xff0c;即线程需要延时的时候&#xff0c;线程会放弃CPU的使用权&#xff0c;C…

从0到1写RT-Thread内核——支持多优先级

在本章之前&#xff0c;RT-Thread还没有支持多优先级&#xff0c;我们手动指定了第一个运行的线程&#xff0c;并在此之后三个线程&#xff08;包括空闲线程&#xff09;互相切换&#xff0c;在本章中我们加入优先级的功能&#xff0c;第一个运行的程序是就绪列表里优先级最高的…

AD软件之模块化原理图

首先我们创建两个原理图文件 然后我们在Sheet2.SchDoc里放置一个页面符并双击绿色的方框 选择目标文件 我们选择我们刚才创建的Sheet4.SchDoc 然后在 视图——>面板——>Navigator选项 里点一下交互式导航 就可以看到Sheet4.SchDoc被添加到Sheet2.SchDoc下面了 通过上面…

AD软件操作技巧

本文介绍一些关于AD软件的实用小操作&#xff0c;这些小技巧可以大大的减少我们的工作量 一.批量操作丝印&#xff08;或者操作别的东西也可以&#xff0c;主要是凸显批量操作的思想&#xff09; 如下图假设我们工程里有很多丝印和焊盘等等&#xff0c;现在我想改批量地修改丝…

V4L2框架分析

V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。v4L2是针对uvc&#xff08;USB Video Class&#xff09;免驱usb设备的编程框架&#xff0c;主要用于采集usb摄像头等。 下图是V4L2的框架&#xff0c;首先系统核心层分配设置注册一个名为cdev结构体变量&#x…

mjpg-streamer框架分析

mjpg-streamer程框架图如下所示&#xff1a; 程序运行起来后&#xff0c;主进程根据传入的参数设置的输入输出通道打开对应的输入输出动态链接库&#xff0c;并依次调用以下函数 1、输入---仓库-----输出&#xff08;mjpg-streamer.h&#xff09; &#xff08;1&#xff09;gl…