关于写时复制
在Linux中要启动一个新进程的方式通常是:先调用fork()
函数fork出一个新的进程,然后在 新的进程中调用exec()函数来启动新的程序从而达到启动新程序的目的,比如采用下面的代码实现。
int start_prog(char* prog,char* args[]) {
pid_t pid = fork(); // 创建子进程
if(pid < 0)
return -1;
if(pid ==0) { // 子进程
if(execv(prog, args)<0)
return -1; // 加载新的程序
}
else {
return 1;
}
}
int main() {
char* args[] = { NULL };
return start_prog("/bin/ls", args);
}
我们知道进程间的内存地址空间是隔离的,fork()系统调用的结果是生成一个新的子进程,为了保证隔离性, 早期的UNIX采用在fork()
将父进程的地址空间完整的复制一份。这个操作非常的耗时。 为了提高效率现代的Unix及Linux采用了一种称为写时复制
的技术,其实也就是一种延迟操作的做法, 子进程和父进程在fork()
时并不马上复制,而是暂时共享内存空间,随后只要父进程或者子进程试图写共享的内存就会产生一个异常, 这时内核才把内存空间进程复制,比如我们在Shell中启动一个程序时随后就会启动新的程序,启动后的程序将会覆盖旧的内存空间, 如果提前就复制了,那么这个复制操作其实是白做了,为此系统将这个操作优化为写时复制。
写时复制,发生写时才复制内存地址空间
如果马上进行exec加载新的程序,那么复制地址就没必要了