fork()和exec()

OS · 2023-06-08
fork()和exec()

fork()是一个系统调用,用于创建一个新的进程。在调用fork()时,操作系统会创建当前进程的一个副本,这个副本被称为子进程。子进程与父进程几乎完全相同,包括代码、数据和打开的文件描述符等。

fork()之后,父进程先执行还是子进程先执行是不确定的,取决于内核所使用的调度算法

写时拷贝(Copy-on-Write,COW)是一种延迟复制的技术,用于优化fork()的性能和资源利用。在传统的fork()操作中,父进程的所有内存空间都会被完全复制到子进程中,这会消耗大量的时间和内存资源。而在写时拷贝机制下,操作系统并不立即复制父进程的内存,而是将父进程和子进程共享同一块物理内存页。

当父进程或子进程试图修改这个共享的内存页时,才会触发实际的复制操作。这时,操作系统会为修改的那部分内存页创建一个副本,并将副本分配给对应的进程。这样,父进程和子进程就拥有了各自的私有内存副本。

写时拷贝的好处在于避免了不必要的内存复制,节省了系统资源和时间。在fork()调用后,如果父进程和子进程都不修改共享的内存页,那么实际上没有进行任何复制操作,它们会继续共享同一块内存。只有在需要修改时,才会执行复制操作,确保进程之间的内存隔离性。

因此,写时拷贝使得fork()操作变得高效,并且只有在需要时才进行实际的内存复制,从而提高了系统的整体性能。

exec()也是一个系统调用,用于在一个进程中执行一个新的程序。它会用指定的可执行文件替换当前进程的执行映像,从而将控制权转移到新的程序。

exec()函数有多个变体,如execve()、execl()、execle()、execv()等,它们在参数传递和执行方式上略有不同,但都实现了替换当前进程的功能。

当调用exec()时,操作系统会加载指定的可执行文件到当前进程的地址空间,并将程序的入口点设置为新程序的起始地址。这样,原来进程的代码、数据和堆栈等信息都会被新程序所取代。因此,exec()被称为一个进程的"执行簿"。

exec()调用后,原进程的上下文会完全被新程序取代,包括进程的身份、环境变量、文件描述符和信号处理等。新程序从头开始执行,接管原进程的资源和状态,并按照自己的逻辑执行相应的任务。

通过exec(),可以实现在一个进程中动态加载不同的可执行文件,实现程序的灵活性和动态性。常见的应用场景包括在一个进程中执行不同的命令、创建新的进程并执行不同的程序,或者在进程中加载插件等。

需要注意的是,exec()调用后,原进程的代码不再执行,除非exec()调用失败。因此,在调用exec()之前,通常会先调用fork()创建一个子进程,然后在子进程中调用exec(),这样可以保留父进程的执行环境。

Theme Jasmine by Kent Liao