首页 > 其他分享 >【计算机网络】高级IO模型

【计算机网络】高级IO模型

时间:2024-03-31 14:31:25浏览次数:37  
标签:fcntl 文件 调用 模型 阻塞 计算机网络 IO 拷贝

高级IO模型

一、 理解 IO

当我们调用系统接口 writeread 的时候,本质是把数据从用户层写给操作系统,也就是写入到 OS 的发送缓冲区中,或者从 OS 的接收缓冲区中读取数据,所以它们的本质也就是拷贝函数。

那么在这个过程中,调用 write 的时候只有当发送缓冲区中有足够的空间才能进行拷贝,当发送缓冲区没有空间了,此时 write 只能阻塞等待,不能继续拷贝。而调用 read 的时候,只有当接收缓冲区有数据才能进行读取拷贝,当接收缓冲区没有数据了,此时 read 也只能阻塞等待。上面就是 IO 的过程,所以,IO 的过程被分为两个部分:等待拷贝

所以在 IO 的过程中,要进行拷贝,必须先判断条件成立,也就是读写事件是否就绪。那么什么叫做高效的 IO 呢?就是在单位时间内,IO 过程中,等的比重越小,IO 的效率越高!

二、认识五种高级 IO 模型

1. 阻塞 IO

在内核将数据准备好之前,系统调用会一直等待。所有的套接字,默认都是阻塞方式。

阻塞IO是最常见的IO模型,过程如下:

在这里插入图片描述

2. 非阻塞IO

非阻塞 IO 就是,如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回 EWOULDBLOCK 错误码。

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

在这里插入图片描述

3. 信号驱动 IO

内核将数据准备好的时候,使用 SIGIO 信号通知应用程序进行 IO 操作。

在这里插入图片描述

4. IO 多路转接

在这里插入图片描述

IO 多路转接,虽然从流程图上看起来和阻塞 IO 类似,实际上最核心在于 IO 多路转接能够同时等待多个文件描述符的就绪状态。

5. 异步 IO

由内核在数据拷贝完成时,通知应用程序。和信号驱动 IO 的区别在于,信号驱动是告诉应用程序何时可以开始拷贝数据。

在这里插入图片描述

以上就是五种高级 IO 的模型的简单介绍。任何 IO 过程中,都包含两个步骤:第一是等待,第二是拷贝。而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间。想要让 IO 更高效,最核心的办法就是让等待的时间尽量少。

三、高级 IO 重要概念

1. 阻塞和非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

  • 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程

其实阻塞IO和非阻塞IO的效率是差不多的,因为 IO = 等待+拷贝,数据好了大家都要拷贝,只是非阻塞IO在等待的时候可以做其他事情,也就是它们之间等的方式不一样,非阻塞IO在进行非阻塞轮询时可以做自己其它的事情,所以这就导致非阻塞IO在效率上稍微高一点。

2. 同步通信和异步通信

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

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

四、非阻塞 IO

fcntl

我们以前学过 recv() 这样的接口,其中它的参数如下:

在这里插入图片描述

我们知道前三个参数是和 read() 的一模一样,而最后一个参数 flag 设为 0 默认就是阻塞等待。而我们可以将这个参数设为 MSG_DONTWAIT,就是非阻塞IO,如下:

在这里插入图片描述

但是这种选项用起来不方便,更通用的做法是使用 fcntl() 接口。我们知道,文件描述符就是一个数组下标,而我们所有的网络通信、文件等等,都是读写文件描述符,而每一个文件描述符指向的都是内核中的文件对象,文件对象是有关于这个文件的 flags 的,也就是它的标记位。所以我们可以通过 fcntl() 接口来直接设置一个文件描述符的属性!其实就是设置其文件对象中的 flags 标志位,告诉内核这个指定的文件描述符要以非阻塞的方式来操作。系统接口如下:

在这里插入图片描述

如上,可以按照指定的 cmd 来对指定的文件描述符来进行可变参数部分的设置。

传入的 cmd 的值不同,后面追加的参数也不相同。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)

我们尝试将标准输入设置为非阻塞IO的形式,如下代码:

				void SetNonBlock(int fd)
				{
				    // 获取文件状态标记位
				    int fl = fcntl(fd, F_GETFL);
				    if(fl < 0){
				        perror("fcntl");
				        return;
				    }
				
				    // 对获取到的文件状态标记位追加属性标记位
				    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
				    cout << " set " << fd << " nonblock done " << endl;
				}
				
				int main()
				{
				    char buffer[1024];
				    // 设置标准输入为非阻塞IO
				    SetNonBlock(0);
				    while(true){
				        cout << "Please Enter# ";
				        ssize_t n = read(0, buffer, sizeof(buffer) - 1);
				        if(n > 0){
				            buffer[n - 1] = 0;
				            cout << "echo: " << buffer << endl;
				        }
				        else if(n == 0){
				            cout << "read done" << endl;
				            break;
				        }
				        else{
				            cerr << "read error, n = " << n << ", errno code: " << errno << ", errstr: " << strerror(errno) << endl;
				            break;
				        }
				    }
				    return 0;
				}

上面的代码其实是跑不通的,因为我们根本没有输入数据,因此 n 是小于0的,我们可以通过打印错误信息观察:

在这里插入图片描述

我们可以看到,错误码的描述大概意思就是临时资源不可用,因为我们在 else 中 break 了,我们应该把 break 去掉,改为 sleep(1),我们方便观察。

此时运行后我们在键盘上输入是可以直接回显的,如下:

在这里插入图片描述

所以,设置为非阻塞,如果底层 fd 数据没有就绪,recv/read/write/send,返回值会以出错的形式返回。所以出错就分为两种情况了,一种是真的出错了,另一种是底层没有就绪,这种情况就是返回 11 号错误码,也就是 EWOULDBLOCK. 那么我们怎么区分呢?可以通过 errno 区分!如果 errno 为 11,代表底层没就绪!所以我们对代码稍作修改,如下:

				void SetNonBlock(int fd)
				{
				    // 获取文件状态标记位
				    int fl = fcntl(fd, F_GETFL);
				    if(fl < 0){
				        perror("fcntl");
				        return;
				    }
				
				    // 对获取到的文件状态标记位追加属性标记位
				    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
				    cout << " set " << fd << " nonblock done " << endl;
				}
				
				int main()
				{
				    char buffer[1024];
				    // 设置标准输入为非阻塞IO
				    SetNonBlock(0);
				    while(true){
				        // cout << "Please Enter# ";
				        ssize_t n = read(0, buffer, sizeof(buffer) - 1);
				        if(n > 0){
				            buffer[n - 1] = 0;
				            cout << "echo: " << buffer << endl;
				        }
				        else if(n == 0){
				            cout << "read done" << endl;
				            break;
				        }
				        else{
				            if(errno == EWOULDBLOCK){
				                // do other thing...
				            }
				            else{
				                cerr << "read error" << endl;
				                break;
				            }
				            //cerr << "read error, n = " << n << ", errno code: " << errno << ", errstr: " << strerror(errno) << endl;
				            sleep(1);
				        }
				    }
				    return 0;
				}

标签:fcntl,文件,调用,模型,阻塞,计算机网络,IO,拷贝
From: https://blog.csdn.net/YoungMLet/article/details/137028651

相关文章

  • List和ObservableCollection的转换
    1、我们后台查询全部List数据的时候,前台需要ObservableCollection展示这个时候List需要转换成ObservableCollectionpublicstaticObservableCollection<T>ToObservableCollection<T>(thisIEnumerable<T>source){if(source==null){thrownewArg......
  • Multimedia Presentation
    ......
  • STM32 GPIO输入检测——按键
    前言在嵌入式系统开发中,对GPIO输入进行检测是一项常见且关键的任务。STM32微控制器作为一款功能强大的处理器,具有丰富的GPIO功能,可以轻松实现对外部信号的检测和处理。在本文中,我们将深入探讨如何在STM32微控制器上进行GPIO输入检测,并介绍两种常见的方法:轮询检测和中断检测。......
  • 探秘STM32 GPIO位带操作:释放硬件潜能,轻松驾驭IO
    前言在嵌入式系统开发中,对于STM32微控制器的GPIO操作,位带操作是一种强大而精巧的技术。通过位带操作,我们可以更加高效地操纵单个GPIO引脚,释放硬件的潜能,实现更快速、更精确的IO控制。本文将深入探讨STM32GPIO位带操作的奥秘,带领读者一窥其中的精妙之处。GPIO位带操作是什么?......
  • JINGWHALE ABCDE 概念模型系统设计建模法,帮你规范系统产品设计,打造全链路的产品体验!
    《一种基于概念模型思想的ABCDE系统设计建模法的研究与应用》张云龙(JINGWHALE数字科学艺术创新中心,浙江杭州,310008)国作登字-2023-A-00087399摘要:本文基于概念模型思想提出了一种归纳设计纲领、梳理业务模型、抽象概念模型、具象设计模型、评估改进模型的ABCDE概......
  • @Transactional详解(作用、失效场景与解决方法)| 事务注解实际原理(AOP)解析
    开发中代码实现事务的方式,理论上说有两种:编程式事务、注解式事务。但是实际上使用最多的还是注解实现的事务控制; 1、编程式事务(开发用的很少了)基于底层的API,如PlatformTransactionManager、TransactionDefinition 和 TransactionTemplate 等核心接口,开发者完全可以通过编......
  • 模型部署实战:从训练到上线
    目录1.前言2.RESTfulAPI设计3.使用Flask/Django开发后端服务4.使用TensorFlowServing部署模型5.性能监控与服务维护要点6.总结1.前言        在机器学习的全周期中,模型部署是至关重要的一环。经过长时间的训练、验证和优化,当模型准备就绪时,我们需要确保它能......
  • 神经网络算法:一文搞懂Attention(注意力)机制
    本文将从Attention的本质、Attention的原理、Attention的应用三个方面,带您一文搞懂Attention(注意力)机制。Attention的本质核心逻辑:从关注全部到关注重点Attention机制处理长文本时,能从中抓住重点,不丢失重要信息。Attention机制像人类看图片的逻辑,当我们看一张图片的......
  • SiMBA:基于Mamba的跨图像和多元时间序列的预测模型
    这是3月26日新发的的论文,微软的研究人员简化的基于mamba的体系结构,并且将其同时应用在图像和时间序列中并且取得了良好的成绩。语言模型的发展正在从大型语言模型(LLMs)向小型语言模型(SLMs)转变。llm和slm的核心都是transformers,它是llm和slm的构建模块。虽然transformers通过其......
  • maya给模型以及子节点随机染色
    maya给模型以及子节点随机染色 importrandomimportmaya.cmdsaspydefmaterial1():sel=py.ls(sl=True)ifsel!=[]:forobjinsel:myShade=py.shadingNode('lambert',asShader=True)#printmyShademyShad......