世间万物都有产生、发展以及消亡的过程。进程亦不例外。每个进程都有创建、执行与消亡的过程。这也就是进程的三部曲。也是今天要讲解的。
在linux系统中,第一个进程是系统固有的(内核设计者设计好的)。系统在引导并完成了基本的初始化以后,就有了系统的第一个进程(内核线程)。除此之外,所有其他进程和内核进程都是在这个原始进程或其他子孙进程所创建的。所以,在linux系统中,准确地来说:一个新的进程一定是由一个已经存在的进程”复制“出来的,而不是”创造“出来的。
linux中将进程的创建与目标程序的执行分成两步。
第一步:
从已经存在的”父进程“中复制出一个子进程,但是该子进程有自己的task_struct和系统空间堆栈,但与父进程共享其他所有的资源。例如:如果父进程打开里五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。
为此,linux提供了两种调用方式,一个是fork(),另一个是clone()。两者的区别主要是fork()是全部复制,而clone()是带参数的,可以是选择性地复制。后来,为提高效率,又增设了vfork()。vfork()也是不带参数的,但是除了task_struct结构和系统空间堆栈之外的资源全部通过数据结构和指针的复制”遗传“。所以,vfork()出来的是线程而不是进程。
其实,这三个系统调用都是通过do_work()来完成的,所不同的只是对do_work()的调用参数所起的作用不同。即每次调用时,系统都会产生相应的信号,而这个信号将决定do_work()中哪个参数起作用。
第二步:
目标程序的执行。一般来说,创建一个新的进程是因为执行新的任务。为此,系统提供了execve()调用,让一个进程执行得以执行。
好处:
复制代价比较低,而且通过复制而传承下来的资源往往对子进程很有用;更重要的是有利于父、子进程间通过pipe来建立一种简单有效的进程间通信,并且从而产生了操作系统的用户界面即shell的”管道“机制。
创建子进程后,父进程有两个选择——同步和异步。同步也即”受阻“状态,父进程会进入休眠状态,而异步就是不受阻状态,父进程继续执行它原先的任务。