文章目录
- 作者
- 环境
- 概述
- 正文
- 测试程序
- 实验1:虚拟地址区域的分配
- 实验2:验证通过缺页异常实验物理页的按需分配
- 实验3:验证fork操作后,父子进程页表属性的变化
- 实验4: 验证父进程的Cow
- 实验5:验证父进程发生CoW后,子进程发生写操作时的行为
作者
pengdonglin137@163.com
环境
Linux-6.5 + ARM64
概述
我们知道,Linux内核为了提高进程fork的效率,实现了CoW技术,即将进程的私有的可写区域的Page的映射属性改成只读,父子进程的任何一方先发起写操作的将触发CoW:分配一个新的物理页,然后将要写的区域页的内容拷贝到这个物理页,最后将这个物理页映射到要写的用户虚拟地址上。
下面我们通过实验和源码分析来理解这个过程。
正文
测试程序
下面是用到的测试程序。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>int parent_run = 0;
int child_run = 0;void sig_parent(int sig)
{parent_run += 1;
}void sig_child(int sig)
{child_run += 1;
}// 测试用的缓冲区指针
static char *buffer;int main(int argc, const char *argv[])
{pid_t child_pid;// 通过SIGUSR1和SIGUSR2来控制父子进程的运行printf("parent use signal: %d\n", SIGUSR1);signal(SIGUSR1, sig_parent);printf("child use signal: %d\n", SIGUSR2);signal(SIGUSR2, sig_child);// 实验1:验证虚拟地址区域的分配printf("parent (%d) wait ...\n", getpid());while(parent_run == 0) sleep(1);buffer = malloc(0x100000);printf("parent alloc 1MB memory: %p\n", buffer);// 实验2:验证通过缺页异常实验物理页的按需分配printf("parent (%d) wait ...\n", getpid());while(parent_run == 1) sleep(1);// 这里之所以写0x1000的位置,是因为malloc返回的虚拟地址所属的第一个虚拟页已经映射到了物理页// 不满足实验要求,所以我们使用下一个虚拟页buffer[0x1000] = 1;printf("parent write buffer: %p\n", buffer + 0x1000);// 实验3:验证fork操作后,父子进程页表属性的变化printf("parent (%d) wait ...\n", getpid());while(parent_run == 2) sleep(1);child_pid = fork();if (child_pid > 0) { // parent// 实验4: 验证父进程的Cowprintf("parent (%d) wait ...\n", getpid());while(parent_run == 3) sleep(1);buffer[0x1000] = 3;printf("parent write 3 buffer: %p\n", buffer + 0x1000);printf("parent wait to exit\n");} else if (child_pid == 0) { // child// 实验5:验证父进程发生CoW后,子进程发生写操作时的行为printf("child pid: %d, wait for write buf: %p\n", getpid(), buffer);while(child_run