| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 系统运维 -> Linux IO复用技术与零拷贝 -> 正文阅读 |
|
[系统运维]Linux IO复用技术与零拷贝 |
前言:1.内核空间和用户空间??虚拟内存被操作系统划分成两块:内核空间和用户空间,内核空间是内核代码运行的地方,用户空间是用户程序代码运行的地方。当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。 ??32位inux虚拟地址空间划分如下:
正是有了不同运行状态的划分,才有了上下文的概念。用户空间的应用程序,如果想要请求系统服务,比如操作一个物理设备,或者映射一段设备空间的地址到用户空间,就必须通过系统调用来(操作系统提供给用户空间的接口函数)实现。 所谓的进程上下文,就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容,当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。 进程、线程、上下文我们都知道现在的操作系统都是多任务操作系统,但实际上对于单核心CPU来说,它在某个具体时间只能同时处理一个任务,而多任务的实现,很重要的就是进程的出现。由于在单线程阶段CPU只能处理一个任务经常会导致CPU处于空闲状态而浪费大量资源。进程出现后让计算机的性能得到了很大的提升,但是后来人们发现CPU会顺序处理进程中的每个任务,当一个进程有多个子任务的时候,顺序执行带来的效率问题就出现了,于是,线程的概念也就出现了。线程把进程的子任务进行分割,每个子任务对应一个线程,这样让进程内部的并发成为了可能。 那么进程线程是如何解决CPU同时只能处理一个任务从而解决多任务的问题的呢。这里谈谈我的理解。 (cpu的读取址相对于磁盘等硬件来说是非常快的,cpu的速度是纳秒级别,磁盘速度是毫秒) 把这些串起来我们就可以得到下面的信息:我们之前提到CPU同时只能处理一个任务,那当有多个任务同时需要处理怎么办呢?轮着来,也就是如果有A、B两个程序同时运行,在CPU这里实际上是这样的:A→保存A上下文→读取B上下文→B→保存B上下文→读取A上下文→A……如此循环直到程序结束,由于CPU运行极快,我们并不能感知到A和B中间短暂的中止状态,于是在我们看来就像是在同时运行。 那么线程是什么呢,再举个例子,比如程序A在运行的时候有三个子任务A1、A2、A3由于CPU的特性这三个任务会被顺序执行,也就是如果在执行A1的时候我们需要A3的结果单线程的情况下意味着我们只能等待A1、A2、之行结束。这显然不符合我们的要求,于是线程的概念就出现了,线程把每个进程的子任务独立出来单个处理,当CPU在处理程序A的时候CPU的运行时间被分成更细小的片段执行,由于不是等待A1执行完在执行A2而是A1→A2→A3→A1→A2→A3……直到执行结束。如下图: 再加一句,CPU在工作的时候是按照时间片段进行的,靠这些分割的时间片段来回切换实现了多任务。所以也有的人会说线程和进程描述的是CPU时间段。(线程把进程的CPU时间继续分割来解决进程的并发问题) 缓存IO缓存 IO 又被称作标准 IO,大多数文件系统的默认 IO 操作都是缓存 IO。在 Linux 的缓存 IO 机制中,操作系统会将 IO 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。 以read为例,数据会先被拷贝到操作系统内核的缓冲区中(DMA),然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间(CPU)。(缓冲区是作为IO中介的内存块,用于协调输入输出两端的速度)
又如: 实现零拷贝后,如下图:
DMA当用户进程想要执行IO操作时(比如想要读取磁盘数据),由于用户进程工作在用户模式下,它没有执行这些操作的权限。需要发起系统调用切换到内核态。这里会产生一次进程的上下文切换。 DMA(直接存储器存取)技术,需要硬件支持,即需要主板上一块独立的芯片,即DMAC(协处理器),在进行IO操作时,不再需要CPU频繁切换,而是直接通过DMA控制数据传输。在DMA方式中,内存和IO设备中有一条专门的数据总线用于数据传输,因此不再需要占用CPU。 以 关于IO多路复用
I/O多路复用可以用来在单个进程/线程中处理多个事件流。 通过上面的例子再来看下面两句话就比较容易理解了。 Linux系统下IO模型linux系统提供下列五种IO处理模型 1.阻塞IO最为简单且常用的IO模型,表示在发起一次IO操作后,需要一直等待其成功或者失败后才返回,期间程序不能做其他事情。阻塞IO操作只能对单个文件描述符进行操作,例如read和write。 2.非阻塞IO通过将文件描述符的flags 设置为O_NONBLOCK 表示当前I/O操作为非阻塞式。系统会采用类似轮询的方式去判断当前IO成功或失败。一般来说,非阻塞IO是在循环里面,程序需要不断地询问内核数据是否就绪,成功则执行IO操作并退出,失败则继续询问。也就说非阻塞IO会一直占用CPU,导致CPU占用率高。 3.IO多路复用同时监听多个描述符的IO事件,取出状态ready的描述符列表。相比前两种,改进之处在于不在主动查询事件状态,而是等待准备好的通知。 4.信号驱动IO为IO事件绑定处理程序,当事件发生时触发处理程序。 上述4中方案可归类为同步IO,因为其都需要在读写事件就绪后自己负责进行读写。也就是说,在接收数据的过程中,(一般指执行io操作,读写过程中),依然是阻塞状态。 5.异步IO利用Linux的信号机制,当IO事件发生时,触发信号处理程序。 实现I/O复用的三种方法select函数原型
其中我们注意参数fd_set结构体包含一个整形数组,该数组中每一个元素的每一位标记一个文件描述符。当调用select函数时,由内核根据IO状态修改fd_set的内容,由此来通知执行select的进程,哪一个或者几个文件可操作。 换句话说,select实现多路复用的方式是,当前进程把所有的文件描述符都放入fd_set中,然后调用 select 函数将文件描述符集合拷贝到内核里,让内核通过轮询(遍历)的方式判断是否有多少个IO请求产生。找到并标记之后, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态再通过遍历的方法找到前面标记过的文件描述符,然后操作。 可看到,使用select的好处是: 缺点如下:
pollpoll机制类似于select,管理多个文件描述符也是采取轮询的方式。,但是 poll 没有最大文件描述符数量的限制,也就是说,poll 只解决了上面的问题 3,并没有解决问题 1,2 的性能开销问题。
poll函数对文件描述符的管理方式不再是集合了,而是采用链表fds的方式,而且链表的每个元素pollfd指针,没有使用select 直接传值的方法,因此没有连接数的限制。 epoll |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/15 11:56:05- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |