文章

IO

IO

IO

基础

文件描述符(File Descriptor FD)

计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

Linux 的设计思想:一切皆文件(不仅仅是磁盘文件,也有可能是内存文件)。

Linux 中一切都可以看作文件,包括普通文件、链接文件、Socket 以及设备驱动等,对其进行相关操作时,都可能会创建对应的文件描述符。文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指代被打开的文件,对文件所有 I/O 操作相关的系统调用(例如 read、write 等)都需要通过文件描述符

可见,在 Linux 中,FD 就是一种宝贵的系统资源。本质上,一个 Linux 进程启动后,会在内核空间生成文件描述符表(FDTable),记录当前进程所有可用的 FD,也即映射着该进程所有打开的文件。
qt3y6

FD 实际上就是文件描述符表的数组下标(所以是非负整数)。

缓存 IO

缓存 IO 又被称为标准 IO,大多数文件系统的默认 IO 操作都是缓存 IO。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

pipe 管道

在类 Unix 操作系统(以及一些其他借用了这个设计的操作系统,如 Windows)中,管道(英语:Pipeline)是一系列将标准输入输出链接起来的进程,其中每一个进程的输出被直接作为下一个进程的输入。这个概念是由道格拉斯·麦克罗伊为 Unix 命令行发明的,因与物理上的管道相似而得名。

管道就是通常用于进程间通信的一种机制。一个程序的输出为另外一个程序的输入。
如,未使用管道:

1
2
ls > abc.txt # 把当前目录的文件列表输入到abc文本
grep xxx abc.txt # adb文本作为输出,让grep程序查找内容xxx

使用管道:

1
ls | grep xxx

达到同样的效果,还不需要显式地产生文件。shell 会用一个管道连接两个进程的输入输出,以此实现跨进程通信

可以把管道的本质理解成一个文件,前一个进程用写的方式打开文件,后一个进程用读的方式打开。所以管道的系统调用函数是这样的:

1
int pipe(int pipefd[2]);

函数调用后会创建 2 个文件描述符,即填充 pipefd 数组,其中 pipefd[0] 是读方式打开,pipefd[1] 是写方式打开,分别作为管道的读和写描述符。

管道虽然形式上是文件,但本身并不占用磁盘存储空间,而是占用的内存空间,所以管道是一个操作方式和文件相同的内存缓冲区。

写入管道的数据会被缓存到直到另一端读取为止,所以上述命令是阻塞的,在 ls 没有结果产生之前 grep 并不会执行。

什么是 IO?

IO 是计算机体系中重要的一部分,不同的 IO 设备有着不同的特点:数据率不一样、传送单位不一样,数据表示不一样,很难实现一种统一的输入输出方法。
输入输出 (input/output) 的对象可以是文件 (file), 网络 (socket),进程之间的管道 (pipe)。在 linux 系统中,都用文件描述符 (fd) 来表示。

计算机角度的 IO

9ls97
冯.诺依曼结构:它将计算机分成分为 5 个部分:运算器、控制器、存储器、输入设备、输出设备。

  • 输入设备是向计算机输入数据和信息的设备,键盘,鼠标都属于输入设备
  • 输出设备是计算机硬件系统的终端设备,用于接收计算机数据的输出显示,一般显示器、打印机属于输出设备。

操作系统的 IO

将内存中的数据写到磁盘,将磁盘的数据读到内存中。这些操作由操作系统内核完成,用户应用程序跑在用户空间,它不存在实质的 IO 过程,真正的 IO 是在操作系统内核执行的,即应用程序的 IO 操作分为两种动作:IO 调用IO 执行。IO 调用是由进程发起,而 IO 执行是由操作系统内核完成的。所以我们所说的 IO 是应用程序对操作系统 IO 功能的一次触发,即 IO 调用。

I/O 控制器

I/O 控制器介绍

由于 CPU 无法直接控制 I/O 设备的机械部件,因此 I/O 设备还要有个电子部件作为 CPU 和 I/O 设备机械部件之间的 “ 中介 “,用于实现 CPU 对设备的控制,这个电子部件就是I/O 控制器,又称为设备控制器。
I/O 控制器是控制计算机输入输出的一个最基本的控制系统,可指挥计算机的各个部件按照指令的功能要求协调工作的部件。它由 指令寄存器IR(InstructionRegister)、程序计数器PC(ProgramCounter)和 操作控制器OC(OperationController)三个部件组成,对协调整个电脑有序工作极为重要。
I/O 控制器所在的位置:
53l1l
I/O 控制器图:
5o37i

I/O 控制器功能

  1. 接收设备 CPU 指令:CPU 的读写指令和参数存储在控制寄存器中
  2. 向 CPU 报告设备的状态:I/O 控制器中会有相应的状态寄存器,用于记录 I/O 设备的当前状态。(比如 1 代表设备忙碌,0 代表设备就绪)
  3. 数据交换:数据寄存器,暂存 CPU 发来的数据和设备发来的数据,之后将数据发给控制寄存器或 CPU。
  4. 地址识别:类似于内存的地址,为了区分设备控制器中的各个寄存器,需要给各个寄存器设置一个特定的地址。I/O 控制器通过 CPU 提供的地址来判断 CPU 要读写的是哪个寄存器。

I/O 控制器的工作示意图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
       +--------------+
       |  I/O控制器  |
       +--------------+
              |
              | 控制信号
              v
+-------+   +-------+
|  CPU  |<->|  总线 |
+-------+   +-------+
              |
              | 数据传输
              v
       +--------------+
       |   外部设备   |
       +--------------+

在这个示意图中,I/O 控制器位于计算机系统和外部设备之间,负责协调和控制两者之间的数据传输和控制信号传输。CPU 通过总线与 I/O 控制器通信,并向其发送命令和控制信号。I/O 控制器使用这些信号来控制外部设备的操作,并将数据传输到或从设备中读取。数据传输过程中,I/O 控制器可以直接访问计算机的内存,以加快数据传输速度。
这只是一个简单的示意图,实际的 I/O 控制器可能包含更多的组件和子系统,例如 DMA 控制器、中断控制器、状态寄存器等。不同类型的外部设备也可能需要不同类型的 I/O 控制器来管理其输入/输出操作。

I/O 控制方式

程序直接控制方式(串行,低效)

工作方式:CPU 向 I/O 模块发出读写指令,CPU 会从状态寄存器中读取 I/O 设备的状态,如果是忙碌状态就继续轮询检查状态,如果是已就绪,就代表 I/O 设备已经准备好,可以从中读取数据到 CPU 寄存器中,读到 CPU 后,CPU 还要往存储器(内存)中写入数据,写完后再执行下一套指令。
pauk7
优点:实现简单。在读写指令之后,加上实现轮询检查的一系列指令即可。
缺点:CPU 和 I/O 设备只能串行化工作,CPU 需要一直轮询检查,长期处于忙等状态,CPU 利用率很低。

CPU 长期轮训,忙等状态,CPU 利用率低

中断驱动方式

工作方式:中断驱动方式的思想是允许 I/O 设备主动打断 CPU 的运行并请求服务,从而 “ 解放 “CPU,使得其向 I/O 控制器发送读命令后可以继续做其他有用的工作。I/O 控制器从 CPU 接收一个读命令,然后从外围设备读数据,一旦数据读入到该 I/O 控制器的数据寄存器,便通过控制线给 CPU 发出一个中断信号,表示数据已准备好,然后等待 CPU 请求该数据。I/O 控制器收到 CPU 发出的取数据请求后,将数据放到数据总线上,传到 CPU 的寄存器中。至此,本次 I/O 操作完成,I/O 控制器又可开始下一次 I/O 操作。这样就使得 CPU 与 I/O 设备能够并行工作。
h2fet
优点: 与程序直接控制方式相比,在中断驱动方式中,I/O 控制器会通过中断信号主动报告 I/O 已完成,CPU 不再需要不停的轮询。CPU 和 I/O 设备可并行工作,CPU 利用率得到明显提升。

缺点 :由于数据中的每个字在存储器与 I/O 控制器之间的传输都必须经过 CPU,这就导致了中断驱动方式仍然会消耗较多的 CPU 时间。

CPU 被中断次数过多,CPU 时间消耗过多

DMA(直接存储方式)

工作方式: 方式的数据流向是从设备直接放入内存(设备→内存),或者是从内存直接到设备(内存→设备),不再使用 CPU 作为中间者。CPU 在读写数据前要指明要读入多少数据、数据要存放在内存中的什么位置、数据放在外部磁盘的什么位置等问题,然后 DMA 控制器会根据 CPU 提出的要求完成数据的读写操作。当整块数据的传输完成后,才向 CPU 发出中断信号。
h7ifq
优点: 数据传输效率以” 块 “为单位,仅仅在传送一个或多个数据块的开始和结束时,才需要 CPU 的干预,CPU 的介入性进一步降低;同时,CPU 和 I/O 设备的并行性进一步提升。
缺点: CPU 发出一条指令,只能读或写一个或多个 连续的数据块。如果读写的数据块不是连续存放的而是离散的,那么 CPU 要分别发出多条 I/O 指令,进行多次中断处理才能完成。

通道控制方式

工作方式:通道是一种硬件,可以理解为 “ 低配版的 CPU”。通道与 CPU 相比的话,CPU 能够处理的指令种类比较多,而通道只能执行单一指令。使用这种控制方式,CPU 干涉频率极低,通道会根据 CPU 的指令执行响应程序,只有完成一组数据块的读写后才需要发出中断信号让 CPU 干预。

优点: CPU、通道、I/O 设备可并行工作,资源利用率极高。

缺点: 实现复杂,需要专门的通道硬件支持。

I/O 控制方式小结

kzpyr

DMA

DMA(Direct Memory Access,直接内存访问) 是一种硬件技术,它需要通过硬件实现才能完成数据的直接内存访问。具体来说,DMA 需要由 DMA 控制器和相关的硬件组件组成,用于管理和控制数据传输过程,以实现在不需要 CPU 干预的情况下实现高效的数据传输。因此,DMA 是一种基于硬件的技术,不依赖于操作系统或软件的支持,可以独立地完成数据传输和处理任务。当然,在使用 DMA 技术时,需要使用一些驱动程序或操作系统提供的接口来控制 DMA 控制器的行为,以确保其能够正确地工作并提高系统的性能和效率。
DMA 存在于计算机的主板和外设之间,作为主板和外设之间的数据传输通道。具体来说,DMA 通常由以下硬件组成:

  1. DMA 控制器:用于管理 DMA 传输过程,包括控制数据传输的方向、地址和大小等。
  2. DMA 通道:用于连接 DMA 控制器和外设,传输数据。
  3. DMA 缓冲区:用于存储传输的数据,其大小和位置由 DMA 控制器管理。
  4. 总线接口:用于连接 DMA 控制器和主板的系统总线,以实现与主板的通信和数据传输。

在计算机系统中,DMA 技术被广泛应用于各种外设,例如网络适配器、声卡、硬盘、光驱等。通过使用 DMA 技术,这些外设可以快速地读取或写入数据,而无需 CPU 的干预,从而提高了系统的性能和效率。同时,DMA 技术也可以用于一些特殊的数据处理任务,例如数据压缩、加密等。
DMA 是一种用于高效数据传输的技术,可以大大提高系统的性能和效率。它通过直接访问计算机的内存来实现数据传输,减轻了 CPU 的负担,提高了系统的响应速度和数据传输速度。

I/O 过程需要 CPU 参与吗?

需要。

  • 程序直接控制、中断驱动等 IO 控制方式方式,需要大量的 CPU 参与整个 IO 过程
  • 基于 DMA 的 I/O 控制器在数据传输过程中可以通过 DMA 技术直接读写内存,从而不需要 CPU 的干预。但是,在数据传输开始和结束时,仍然需要 CPU的参与来初始化DMA控制器和处理传输结果。CPU 需要完成以下任务:
    1. 初始化 DMA 控制器:CPU 需要设置 DMA 控制器的源地址、目的地址、传输长度等参数,以确保 DMA 控制器能够正确地进行数据传输。
    2. 开始传输:CPU 需要启动 DMA 控制器开始数据传输,以便将数据从外设读入到内存中或从内存中写入到外设中。
    3. 结束传输:DMA 控制器在数据传输完成后会向 CPU 发送中断请求信号,CPU 需要响应中断请求并处理传输结果。

事件

  • 可读事件,当文件描述符关联的内核读缓冲区可读,则触发可读事件(可读:内核缓冲区非空,有数据可以读取)
  • 可写事件,当文件描述符关联的内核写缓冲区可写,则触发可写事件(可写:内核缓冲区不满,有空闲空间可以写入)

网络 IO 模型之同步 IO 和异步 IO

同步 IO

  • blocking IO 阻塞 IO
  • nonblocking IO 非阻塞 IO
  • IO multiplexing IO 多路复用
  • signal driven IO 信号驱动 IO(平时很少用)

异步 IO

  • asynchronous IO 异步 IO

一般情况下,一次网络 IO 读操作会涉及到两个系统对象:用户进程 Process 和内核对象 kernel。kernel 的两个处理阶段:

  • Waiting for the data to be ready 等待数据准备好
  • Coping the data from the kernel to the process 将数据从内核空间的 buffer 拷贝到用户空间进程的 buffer

IO 模型的异同点就是区分在这两个系统对象、两个处理阶段的不同上。

Blocking IO(阻塞 IO,BIO)

阻塞 IO:应用进程发起 IO 调用,如果内核的数据没有准备好的话,应用进程会一直阻塞等待,直到内核数据准备好了,从内核空间拷贝到用户空间,才返回成功。
l6lqc

application 在 Blocking IO 读 recvfrom 操作的两个阶段都是等待的,在数据没准备好的时候,application 原地等待 kernel 准备数据,kernel 准备好数据后,application 继续等待 kernel 将数据拷贝到 application 的 buffer,在 kernel 完成数据 copy 后 application 才会从 recvfrom 系统调用中返回。

阻塞 IO 应用_**:

  • Socket
  • Java BIO

阻塞 IO 缺点:
如果内核数据一直没有准备好,用户进程会一直阻塞,浪费性能

Nonblocking IO(非阻塞 IO,NIO)

xpfzg
非阻塞 IO:recvfrom 不阻塞,用户进程不断轮询看内核进程的数据是否准备好;kernel 将数据拷贝到进程空间 buffer 用户进程是需要等待的。

NIO 读 recvfrom 操作的第一个阶段是不会 block 等待的,如果 kernel 数据还没准备好,那么 recvfrom 会立刻返回一个 EWOULDBLOCK 错误;当 kernel 准备好数据后,进入第二个阶段,application 会等到 kernel 将数据 copy 到用户空间 buffer,在 kernel 完成数据的 copy 后 application 才会从 recvfrom 系统调用中返回。

非阻塞 IO 缺点

  • 频繁的轮询,导致频繁的系统调用,同样会消耗大量的 CPU 资源

IO multiplexing(IO 多路复用)

6jyc5
IO 多路复用:熟知的 select、poll 和 epoll 模型。他们可以同时监控多个 fd,任何一个返回内核数据就绪,应用进程再发起 recvfrom 系统调用。
IO 多路复用,application 在 wait for data 和 copy data from kernel to user 都是 block 住的
IO 多路复用优点:

  • 同时监听处理多个 IO

Asynchronous IO AIO 异步 IO

6c1sn
异步 IO:在 recvfrom 操作的两个处理阶段上都不能等待,application 调用 recvfrom 后立刻返回,kernel 自行去准备好数据并将数据从 kernel 的 buffer 中 copy 到 application 的 buffer 再通知 application 读操作完成了,然后 application 再去处理。
Linux 的网络 IO 中是不存在异步 IO 的,Linux 的网络 IO 处理的第二阶段总是阻塞等待数据 copy 完成的,真正的网络异步 IO 是 Windows 下的 IOCP 模型。

non-blocking IO 和 asynchronous IO 区别

这两种容易混淆,认为是一样的,其实是不一样的。

  • non-blocking IO 仅仅是在发送 recvfrom 后不会阻塞,但它仍然要求进程去主动检查,并且当数据准备完成后,它需要进程主动地再次调用 recvfrom 来将数据拷贝到用户内存中
  • asynchronous IO 它就像是用户进程将整个 IO 操作都交给了内核完成,然后内核做完后发信号通知

IO 多路复用模型

IO 多路复用背景:
阻塞 I/O 模型下,一个线程只能处理一个流的 I/O 事件。如果想要同时处理多个流,要么多进程,要么多线程。

select/poll

什么是 select?
应用进程通过调用 select 函数,可以同时监控多个 fd,在 select 监控的 fd 中,只要有任何一个数据状态准备就绪了,select 函数就会返回可读状态,这时应用进程再发起 recvfrom 请求去读取数据。
4d8qa
select 流程:
select 可以同时观察许多流的 I/O 事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流(于是我们可以把 “ 忙 “ 字去掉了),于是,如果没有 I/O 事件产生,我们的程序就会阻塞在 select 处。但是依然有个问题,我们从 select 那里仅仅知道了,有 I/O 事件发生了,但却并不知道是那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。
poll 背景:
select 存在连接限制,所以又提出了 poll,解决了连接限制的问题。
但是呢,select 和 poll 一样,还是需要通过遍历文件描述符来获取已经就绪的 socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。
select/poll 缺点:

  • 每次调用 select/poll,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大
  • 同时每次调用 select/poll 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很多
  • select 支持的 fd 数量太小,默认 1024

epoll

什么是 epoll?

epoll 是 Linux 内核的可扩展 I/O 事件通知机制。于 Linux 2.5.44 首度登场,它设计目的旨在取代既有 POSIX select(2) 与 poll(2) 系统函数,为了同时监听多个文件描述符的 I/O 读写事件而设计的。epoll 实现的功能与 poll 类似,都是监听多个文件描述符上的事件。
epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 采用事件驱动来实现,它会把哪个流发生了怎样的 I/O 事件通知我们。

epoll 流程

3j0jg
epoll 先通过 epoll_ctl() 来注册一个 fd,一旦基于某个 fd 就绪时,内核会采用回调机制,迅速激活这个 fd,当进程调用 epoll_wait() 时便得到通知(避免了遍历所有的文件描述符),而是采用监听事件回调的机制。

  • epoll_create 创建一个 epoll 对象
  • epoll_ctl 向 epoll 实例添加要监控的描述符和感兴趣的事件
  • epoll_wait 等待其管理的连接上的 IO 事件
1
2
3
4
5
6
7
8
9
// 在内核中创建epoll实例并返回一个epoll文件描述符
int epoll_create(int size);
int epoll_create1(int flags);

// 向epfd(上面create的)对应的epoll实例添加、修改或删除对fd(即第3个参数)上事件event的监听
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

// 它等待注册在epfd上面的事件,事件从events参数中带出
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll 优点

  1. 能高效检查大量文件描述符(相比 select/poll,少量描述符的性能可能还不如前面两个)
  2. 基于事件驱动的,不需要无差别的轮询
  3. 使用 mmap 加速内核与用户空间的消息传递
  4. 支持水平和边缘触发

select、poll 和 epoll 区别?

vgfkt

为什么 epoll 比 poll/select 高效?

  • select 连接数限制

select 和 poll 的动作基本一致,只是 poll 采用链表来进行文件描述符的存储,而 select 采用 fd 标注位来存放,所以 select 会受到最大连接数的限制 (Linux 默认是 1024,而 poll 不会)

  • select/poll 需遍历就绪找到 fd,epoll 不需要

select/poll 采用轮询的方式来检查文件描述符是否处于就绪状态,而 epoll 采用回调机制,造成的结果就是,随着 fd 的增加,select 和 poll 的效率会线性降低,而 epoll 不会手动太大影响,除非非活跃的 Socket 很多

  • **mmap **select/poll 都需要将有关文件描述符的数据结构拷贝进内核,最后再拷贝出来;而 epoll 创建的有关文件描述符的数据结构本身就存在于内核态中,系统调用返回时利用 mmap 文件映射内存加速与内核空间的消息传递,epoll 使用 mmap 减少复制开销
  • epoll 的边缘触发模式效率高,系统不会充斥着大量不关心的就绪文件描述符
  • 虽然 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select 和 poll 的性能可能比 epoll 好,毕竟 epoll 的通知机制需要很多函数回调。

磁盘 IO

Linux 系统下 IO 结构模型:
uacc0
pzttz

标准 IO standard io

大多数文件系统的默认 I/O 操作都是标准 I/O。在 Linux 的缓存 I/O 机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间。

  • read 操作系统检查内核的缓冲区有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回;否则从磁盘中读取,然后缓存在操作系统的缓存中。
  • write 将数据从用户空间复制到内核空间的缓存中。这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘中由操作系统决定,除非显示地调用了 sync 等同步命令。

优点

  1. 在一定程序上分离了内核空间和用户空间,保证系统本身的运行安全
  2. 可以减少读盘的次数,提高性能

缺点

  1. 数据在传输过程中需要在应用程序地址空间和内核缓存之间进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

直接 IO direct io 很少用

应用程序直接访问磁盘数据,而不经过内核缓冲区,目的是减少一次从内核缓冲区到用户程序缓存的数据拷贝。
direct io 一般很少使用,甚至有些文件系统干脆就不支持 direct io。
缺点

  1. 如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘加载,会非常耗时。

mmap

mmap 是指将磁盘上文件的位置与进程逻辑地址空间中的一块大小相同的区域映射,当要访问内存中一段数据时,转换为访问文件的一段数据。减少了数据在用户空间和内核空间之间的拷贝。
优点

  1. 减少系统调用,只需要一次 mmap 系统调用,不会出现大量的 read/write 系统调用
  2. 减少数据拷贝,普通的 read 调用数据需要经过两次拷贝;而 mmap 只需要从磁盘拷贝一次就可以了

缺点

面试题

mmap 相关

说说 mmap 为什么比普通 IO 效率高?

普通 IO需要两次内存拷贝,磁盘 ->内核 pagecache->应用程序 导致有两次数据拷贝
mmap是一种内存映射文件的方法,它将一个文件映射到进程的地址空间,建立文件磁盘地址与虚拟内存的一种对应关系,如此,读写相应的虚拟地址等同于直接读写对应的文件内容了,这样映射的最大好处是进程可以直接读取内存,避免了频繁的使用 read/write 等系统调用;(将内核 page_cache 映射到用户空间的虚存地址,用户代码直接以内存的方式访问,通过 pagefault 与操作系统交互)
mmap() 高效的原因:减少了一次 copy_to_user 内核态到用户态的文件拷贝。
但如果 mmap 出现了 pagefault,就不一定高效了;有人就会说了 mmap 会导致 pagefault,但是 standard io 也需要触发 io 操作?因为缺页中断比 io 开销更高。
vw2sb

在 4GB 物理内存的机器上,申请 8G 内存会怎么样?

应用程序通过 malloc 函数申请内存时,实际上申请的是虚拟内存,此时并不会分配物理内存。当应用程序读写了这块虚拟内存,CPU 就会访问这个虚拟内存,这时会发现这个虚拟内存没有映射到物理内存,CPU 就会产生缺页中断,进程会从用户态切换到内核态,并将缺页中断交给内核的 Page Fault Handler 处理

  • 在 32 位操作系统,因为进程最大只能申请 3GB 大小的虚拟内存,所以直接申请 8GB 内存,会申请失败
  • 在 64 位操作系统,因为进程最大只能申请 128 TB 大小的虚拟内存,即使物理内存只有 4GB,申请 8G 内存也是没问题,因为申请的内存是虚拟内存,等这块虚拟内存被访问了,因为物理空间不够,就会发生 OOM。
  • 在 4GB 物理内存的机器上,申请 8G 内存会怎么样?
本文由作者按照 CC BY 4.0 进行授权