以问题形式讲解
1.每一个进程都有一个堆空间吗?还是多个进程共用一个堆空间?
在操作系统中,每个进程都有自己独立的虚拟地址空间,其中包括自己独占的堆空间。堆空间是进程私有的,不与其他进程共享。
进程之间的内存在默认情况下不是共享的。
- 堆是每个进程私有的。进程A在堆上分配的内存,进程B无法直接访问。
- 如果进程A崩溃,不会影响进程B的堆内存(稳定性保障)。
但是可以有一种例外情况:共享内存。
若需要多个进程共享内存,必须通过显式创建的共享内存区域实现,例如:
- System V共享内存(
shmget
) - POSIX共享内存(
shm_open
+mmap
) - 内存映射文件(
mmap
+ 文件)
这类共享内存独立于进程的私有堆空间,是专门申请的区域。
补充一点:
内核空间部分(例如Linux中高地址的0xC0000000
以上区域)
所有进程的虚拟内存映射指向同一物理内核区域,内容完全相同。
但当进程进入内核态时(如系统调用、中断):
- 每个进程有自己独立的内核栈(kernel stack)
- 用于保存该进程在内核态执行的:
- 函数调用链
- 临时变量
- 系统调用参数
例如Linux中每个进程的
task_struct
都包含一个void *stack
指针指向其独有的内核栈(通常8KB大小)。
2.linux给每个进程分配8MB的栈空间大小。那如果有多个进程并发运行,但每个进程又没有用到太多的栈空间,不会浪费内存吗?
必须要明确的是这8MB的空间是虚拟内存不是物理内存。有虚拟内存和MMU存在就确保了即使有数百个进程并行运行,也不会造成显著的物理内存浪费。
在 Linux 系统中,每个进程的用户栈空间默认被分配 8MB 的虚拟地址空间,但这并不等价于物理内存的实际占用。设计上的关键点在于按需分配的物理内存管理机制,它确保了即使有数百个进程并行运行,也不会造成显著的物理内存浪费。以下是详细分析:
栈空间的本质:虚拟内存 vs 物理内存
-
虚拟地址空间:
Linux 为每个进程预留 8MB 的虚拟地址范围(默认值,可通过ulimit -s
调整)。这部分地址空间是逻辑上预留的区间,并不是实际内存。 -
物理内存分配:
操作系统通过按需分页(Demand Paging) 机制管理物理内存:-
只有当进程真正访问栈地址时(例如压入函数参数或局部变量),内核才会分配物理内存页(通常每页 4KB)。
-
未使用的虚拟地址空间仅消耗页表条目(约几十字节),不占物理内存。
-
举个例子:
假设系统运行 100 个并发进程,每个进程用户栈虚拟大小为 8MB:
- 虚拟地址空间总量:100 × 8MB = 800 MB(看似很大,但这是虚拟内存)。
- 物理内存实际占用:
- 若每个进程平均只使用 200 KB 栈空间(常见场景),物理内存占用为 100 × 200 KB = 20 MB。
- 若进程栈使用更少(如 10 KB),则占用可低至 1 MB。
只有 2.5% 的虚拟地址空间(200 KB / 8MB)真正映射到物理内存。其余 97.5% 只是预留地址,不消耗物理资源。
3.一个进程中的函数A调用的函数B,执行函数B时会给函数B中的局部变量在栈中分配内存空间,那么当函数B执行完成后,这个栈空间会被回收吗?
✅ 逻辑回收是立即的:函数返回时栈指针复位,空间可被后续函数重用
✅ 物理回收是惰性的:
- 正常情况 → 由后续函数调用覆盖重用
- 无后续调用 → 内核在内存压力时回收(可能延迟数十秒)
- 极限情况 → 进程退出时100%回收
4.linux嵌入式在启动时要初始化栈。这里的栈指的是内核进程的栈还是init进程的栈又或者是shell进程的栈?
在嵌入式 Linux 系统启动过程中,栈的初始化是多层次的,贯穿从硬件初始化到用户空间的全过程。
必然然初始化:
- Bootloader 栈:用于 U-Boot/CFE 等 bootloader 初始化硬件、加载内核
- 内核临时栈:内核解压、早期内存管理初始化
- 0 号进程(idle)内核栈:每个 CPU 核心一个,用于系统空闲时运行
- 1 号进程(init)内核栈:承载 init 进程在内核态的操作
- init 进程用户栈:内核切换到用户空间前,通过
execve()
系统调用加载 init 程序时创建
按需初始化:
- Shell 进程用户栈(仅在启用交互式 shell 时创建