0%

虚拟内存

1. 术语

resident set size (RSS)

2. 文件系统

/proc/self/statm

3. 相关命令

3.1. top

3.1.1. Fields

TIME+1 TIME
5432:01 means “5432 minutes and 1 second” 90,32 means “90 hours and 32 minutes”
25:15.20 means “25 minutes, 15 seconds and 20% of 1 second” 25:15 means “25 minutes and 15 seconds”

3.2. ps

3.2.1. 输出说明

TIME: the cumulated CPU time in [DD-]hh:mm:ss format (time=TIME)

Field value & means
TIME 1-18:09:38 means “1 day, 18 hours, 9 minutes and 38 seconds”

4. 相关系统调用

4.1. fork

子进程是父进程的副本,获取了父进程的数据空间、堆和栈的副本,但是他们共享正文段。

写时复制(Copy-On-Write,COW):不复制父进程的完全副本,只有在父或子进程尝试修改这些区域时,则为修改区域的那块内存制作一个副本,通常是虚拟内存的一页。

博主注: 如果父进程尝试修改共享的内存页,内核会为父进程同样制作副本,而把原本的内存页留给子进程使用,这会导致即使在父进程中,原本的内存页的物理地址也会发生改变,从而使得 RDMA 等机制发生错误(要求物理地址保持不变),所以此时需要使用 madise 设置 MADV_DONTFORK 标志。

在 Linux 中,写时复制(Copy-On-Write, COW)机制主要通过页表和内存管理单元(MMU)来实现。以下是 COW 在 Linux 中的具体实现步骤:

页表标记:当进程调用 fork() 创建子进程时,父进程和子进程的页表会标记共享的内存页为只读。这意味着这些页在初始状态下是共享的,且无法被写入。

页错误处理:如果父进程或子进程尝试写入这些只读页,会触发页错误(page fault)。操作系统内核会捕获这个页错误,并执行 COW 机制。

内存页复制:在页错误发生时,操作系统会分配一个新的物理内存页,并将原始页的内容复制到这个新页中。然后,页表会更新,以指向新的可写内存页。

页表更新:操作系统更新进程的页表,使得写入操作可以在新的内存页上进行,而不会影响其他进程共享的原始页。

引用计数:操作系统维护每个内存页的引用计数,以跟踪有多少进程共享该页。当引用计数减少到零时,内存页可以被释放。

4.2. mmap

TODO

4.3. madvise

madvise 是一个系统调用,用于向内核提供关于内存使用的建议。

1
2
3
#include <sys/mman.h>

int madvise(void *addr, size_t length, int advice);

4.3.1. 选项 advice:

4.3.1.1. MADV_DONTFORK

MADV_DONTFORK 阻止 fork() 后的子进程看见这些范围的内存页。

这意味该内存不会被复制,即在 fork() 调用时,指定的内存区域不会被子进程继承,避免写时复制导致的页物理地址发生变化。在 fork() 之后,如果父进程对共享内存页进行写操作,写时复制(COW)机制会将这些页复制到新的物理位置。这会导致 RDMA 操作使用的内存地址不一致,从而引发数据错误。

拓展: RDMA 中,ibv_fork_initRDMAV_HUGEPAGES_SAFE 会调用 madvise() 来为 IB 卡的 DMA 内存页设置 MADV_DONTFORK ,以避免数据损坏。

5. 参考

what-does-virtual-memory-size-in-top-mean
[2]: https://blog.csdn.net/weixin_42319496/article/details/125940896
[3]: https://docs.nvidia.com/networking/display/rdmaawareprogrammingv17/ibv_fork_init