关于进程创建,系统提供三个系统调用fork()、vfork()、clone()。
一、fork()
头文件:#include <unistd.h>
函数原型:pid_t fork(void);
fork创建的子进程和父进程共享代码段,复制数据段和堆栈段,复制时是“写时拷贝”技术,即一旦fork成功后,父进程和子进程的地址空间已经分开,两者独立运行,不会相互影响。
当然子进程也不是完全复制父进程,还有少量信息不同,例如:进程的标识、状态等。
fork有两个返回值,子进程返回0,父进程返回子进程的ID。一般来说,fork之后父进程子进程的执行顺序是不确定的,这取决于调度器。一般,父进程通过调用wait或waitpid来等待子进程运行结束后自己再运行。
fork的一大特性是父进程的所有打开文件描述符都被复制到子进程中。父子进程的每个相同的打开描述符共享一个文件偏移量。
父子进程的区别:
1.fork的返回值
2.进程ID
3.具有不同的父进程ID
4.子进程的tms_utime、tms_stime、tms_cutime、tms_ustime均被设置为0
5.父进程设置的文件锁不会被子进程继承
6.子进程的未处理闹钟被清理
7.子进程的未处理信号集被设置为空集
二、vfork()
头文件:#include <unistd.h>
pid_t vfork(void);
vfork和fork都可以创建一个子进程,那么既然都有fork了,为什么还会有vfork?
假设fork创建一个子进程后,产生一个父进程的副本,然后调用一个exec执行一个新的代码段,这样一个程序替换了当前进程的正文、数据、堆栈段,这样的拷贝就觉得无意义了。于是就出现了vfork。
vfork用于创建一个子进程,而该进程的目的是exec一个子进程。vfork不将父进程的地址空间复制到子进程中,因为子进程会立即调用exec,于是不会存放该地址空间。相反,在子进程调用exec和exit之前,它在父进程的空间中运行,即会更改父进程的数据段、栈和堆中运行。
总结:
vfork和fork都会创建一个子进程,但是vfork并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec会exit,不过在子进程调用exec或exit之前,它在父进程的空间运行。
三、fork和vfork
1.fork:子进程拷贝父进程的数据段、代码段
vfork:子进程与父进程共享数据段
2.fork父子进程的执行次序不确定
vfork保证子进程先运行,在调用exec或exit之前与父进程数据是共享的,在调用exec或exit后父进程才能被调度运行。
3.vfork保证子进程先执行,在调用exec或exit之后父进程才可能被调度运行,如果在调用这两个函数之前子进程依赖于父进程的进一步动作,会导致死锁。