COW技术初窥:
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
那么子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?
在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
在网上看到还有个细节问题就是,fork之后内核会通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。
未执行exec 父子进程共享代码段、数据段、堆栈段, 但执行写入能力时,内核给子进程数据段、堆栈段分配相应的物理空间,而代码段继续共享,若是执行exec 之后,子进程的代码段也会分配单独的物理空间。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#define SEM_NAME "mysem"
#define FILE_MODE (0400 | 0200)
int a = 2;
/*void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);*/
int main(int argc, const char *argv[])
{
pid_t pid;
sem_t *mutex;
int i = 2;
if ((pid = fork()) == 0)
{
i = 10;
a = 10;
printf("Child: i = %d\n", i);
printf("Child: a = %d\n", a);
}
else if (pid > 0)
{
i = 20;
a = 20;
printf("parent inter: i = %d\n", i);
printf("parent inter: a = %d\n", a);
}
printf("parent outer: i = %d\n", i);
printf("parent outer: a = %d\n", a);
waitpid(-1, NULL, 0);
return 0;
}
