IO读写原理

为了避免用户进程直接操作内核,保证内核安全,操作系统将内存(虚拟内存)划分 
为两部分:

  1. 内核空间(Kernel-Space):内核模块运行在内核空间,对应的进程处于内核态;
  2. 用户空间(User-Space):用户程序运行在用户空间,对应的进程处于用户态。

     操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内核空间,也有访问底层硬件设备的权限。内核空间总是驻留在内存中,它是为操作系统的内核保留的。应用程序是不允许直接在内核空间区域进行读写,也是不容许直接调用内核代码定义的函数的。每个应用程序进程都有一个单独的用户空间,对应的进程处于用户态,用户态进程不能访问内核空间中的数据,也不能直接调用内核函数的,因此要进行系统调用的时候,就要将进程切换到内核态才能进行。

      内核态进程可以执行任意命令,调用系统的一切资源,而用户态进程只能执行简单的运算,不能直接调用系统资源,所以用户态进程必须通过系统接口(System Call),才能向内核发出指令,完成调用系统资源之类的操作。

      用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的两大系统调用: sys_read & sys_write。sys_read&sys_write两大系统调用都会涉及缓冲区。

  1. sys_read系统调用:把数据从内核缓冲区复制到应用程序的进程缓冲区
  2. sys_write系统调用:把数据从应用程序的进程缓冲区复制到操作系统内核缓冲区。

        应用程序的IO操作实际是内核缓冲区与应用程序进程缓冲区的缓冲复制sys_read&sys_write两大系统调用,都不负责数据在内核缓冲区和物理设备(如磁盘、网卡等)之间的交换。这项底层的读写交换操作,是由操作系统内核(Kernel)来完成的。

        所以,应用程序中的IO操作,无论是对Socket的IO操作,还是对文件的IO操作,都属于上层 
应用的开发,它们的在输入(Input)和输出(Output)维度上的执行流程,都是在内核缓冲区和进程缓冲区之间的进行数据交换。

内核缓冲区与进程缓冲区

        缓冲区的目的,是为了减少频繁地与设备之间的物理交换。计算机的外部物理设备与内存与CPU相比,有着非常大的差距,外部设备的直接读写,涉及操作系统的中断。发生系统中断时,需要保存之前的进程数据和状态等信息,而结束中断之后,还需要恢复之前的进程数据和状态等信息。为了减少底层系统的频繁中断所导致的时间损耗、性能损耗,于是出现了内核缓冲区。

        有了内核缓冲区,操作系统会对内核缓冲区进行监控,等待缓冲区达到一定数量的时候,再进行IO设备的中断处理,集中执行物理设备的实际IO操作,通过这种机制来提升系统的性能。至于具体在什么时候执行系统中断(包括读中断、写中断),则由操作系统的内核来决定,应用程序不需要关心。

        上层应用程序使用sys_read系统调用时,仅仅把数据从内核缓冲区复制到上层应用的缓冲区(进程缓冲区);上层应用使用sys_write系统调用时,仅仅把数据从应用的用户缓冲区复制到内核缓冲区中。

        内核缓冲区与应用缓冲区在数量上也不同,在Linux系统中,操作系统内核只有一个内核缓冲区。而每个用户程序(进程)则有自己独立的缓冲区,叫做用户缓冲区或者进程缓冲区。Linux系统中的用户程序的IO读写程序,在大多数情况下,并没有进行实际的IO操作,而是在用户缓冲区和内核缓冲区之间直接进行数据的交换。

        系统调用sys_read&sys_write,并不是使数据在内核缓冲区和物理设备之间的交换。 sys_read调用把数据从内核缓冲区复制到应用的用户缓冲区,sys_write调用把数据从应用的 
用户缓冲区复制到内核缓冲区,两个系统调用的大致的流程如下图所示:

举例说明,比如客户端和服务器端之间完成一次socket请求和响应(包括sys_read和sys_write)的数据交换 ,其流程如下:

  1. 客户端发送请求:客户端程序通过sys_write系统调用,将数据复制到内核缓冲区,Linux将内核缓冲区的请求数据通过客户端器的网卡发送出去。
  2. 服务端系统接收数据:在服务端,这份请求数据会被服务端操作系统通过DMA硬件,从接收网卡中读取到服务端机器的内核缓冲区。
  3. 服务端程序获取数据:服务端程序通过sys_read系统调用,从Linux内核缓冲区复制数据,复制到用户缓冲区。
  4. 服务器端业务处理:服务器在自己的用户空间中,完成客户端的请求所对应的业 
    务处理。
  5. 服务器端返回数据:服务器程序完成处理后,构建好的响应数据,将这些数据从用户缓冲区写入内核缓冲区,这里用到的是sys_write系统调用,操作系统会负责将内核缓冲区的数据发送出去。
  6. 服务端系统发送数据:服务端Linux系统将内核缓冲区中的数据写入网卡,网卡通过底层的通信协议,会将数据发送给目标客户端。
### Linux系统下IO读写原理 Linux系统IO读写底层流程涉及用户空间与内核空间的交互、内存缓存(PageCache)管理、磁盘 I/O 调度等核心机制。其核心流程是从用户空间触发写入开始,以系统调用为入口开启整个流程 [^1]。 ### Linux系统下IO读写方法 - **每一次行的I/O**:当输入内容遇到`\n`时,将流中`\n`之前的内容送到缓冲区。Linux使用`fgets`和`gets`函数提供一次读入一行的功能,示例代码如下: ```c #include <stdio.h> int main() { char buf[100]; // 使用fgets函数 FILE *fp = stdin; char *result = fgets(buf, 100, fp); if (result != NULL) { printf("fgets读取的内容: %s", buf); } // 使用gets函数(不推荐,存在安全风险) char *input = gets(buf); if (input != NULL) { printf("gets读取的内容: %s", buf); } return 0; } ``` ### Linux系统下IO读写相关技术 - **不同的IO模型** - **阻塞IO**:发起IO调用,若不具备IO条件,则等待IO条件具备,数据拷贝完毕后返回,但会造成资源浪费 [^2]。 - **非阻塞IO**:发起IO调用,若不具备条件则立即报错返回,通常需循环发起调用,具备IO条件后拷贝数据完毕返回,但不够实时 [^2]。 - **信号驱动IO**:先定义IO信号处理方式,若IO条件具备,内核用信号通知进程,进程发起调用,拷贝数据后返回,比较实时,但流程控制较难,也是一种异步IO [^2]。 - **异步IO**:定义信号处理,发起异步IO调用后自己直接返回,让别人等待条件具备并完成数据拷贝,IO完成后信号通知进程,进程可直接操作数据 [^2]。 - **多路转接IO**:一种IO事件监控,同时对大量描述符进行事件(可读/可写/异常)默认阻塞监控,就绪时返回并对就绪的IO进行操作,是高并发的处理模型 [^2]。 - **内核通知应用的方式**:内核通知应用的方式有信号驱动IO和让应用主动询问。信号驱动IO会打乱应用程序执行流,实际使用较少;应用主动询问可通过`io_getevents`系统调用让应用知道IO操作是否完成 [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值