1。Fork一旦一个程序调用了fork函数,系统就会为一个新的进程准备上述三个部分。首先,系统让新进程使用与旧进程相同的代码。segment,因为他们的程序还是一样的,对于datasegment和stacksegment,系统都拷贝一份给新进程,这样父进程的数据就可以全部留给子进程了,但是一旦子进程开始运行,虽然它继承了父进程的所有数据,但实际上数据已经分离,不再相互影响,即它们不再共享任何数据。而如果两个进程要共享任何数据,就需要使用另一套函数(shmget、shmat、shmdt等)来操作。现在,已经有两个过程。对于父进程,fork函数返回子例程的进程号,而对于子例程,fork函数返回零。这样,对于程序来说,只要判断fork函数的返回值,就知道自己是在父进程中,还是在子进程中。事实上,大多数Unix系统目前并没有在其实现中进行真正的复制。一般CPU以“页”为单位分配空间。像INTEL的CPU,一页一般是4K字节大小,数据段和堆栈段都是由很多“页”组成的。是的,fork函数复制了这两个段,只是“逻辑上”的,而不是“物理上的”。也就是说,在真正执行fork的时候,物理空间中两个进程的data段和stack段仍然是共享的,当一个进程写入某个数据时,那么两个进程之间的数据是不同的,而系统在物理上将不同的“页面”分开。可以最小化系统的空间开销。2、对于exec系列函数,一旦一个进程调用了exec类函数,它自己“死了”,系统用新程序的代码替换代码段,丢弃原来的数据段和堆栈段,分配一个新节目新节目。数据段和堆栈段中只剩下进程号了,也就是说对于系统来说,还是同一个进程,只不过已经是另外一个程序了。但是一些exec函数也允许继承环境变量等信息,这些信息可以通过exec系列函数中的一些函数的参数获取。对于fork():1、子进程将父进程的所有进程内存复制到自己的内存地址空间中。父子进程的“数据段”、“堆栈段”、“代码段”完全相同,即子进程中的每个字节都与父进程相同。2、子进程的当前工作目录和umask值与父进程相同。父进程在fork()之前打开的文件描述符在子进程中也打开了,并且都指向同一个文件表项。3.子进程有自己的进程ID。对于exec():1.进程调用exec()后,同一进程内存中会使用一个新的程序来代替调用exec()的进程。新程序将替换当前进程映像,当前进程的“数据段”、“堆栈段”和“代码段”均由新程序重写。2、新程序会保持调用exec()的进程ID不变。3.在调用exec()之前打开打开的描述符,继续打开(貌似新程序中有一些参数可以让打开的描述符关闭)进程,资源将被复制1。fork():子进程复制父进程的数据段,代码段vfork():子进程与父进程共享数据段2.fork()父子进程的执行顺序不确定或者在exit之前,将数据共享给父进程,父进程调用exec或exit后可能会被调度运行。3、vfork()确保子进程先运行,父进程可能在她调用exec或exit后被调度运行。如果子进程在调用这两个函数之前依赖于父进程的进一步操作,则会导致死锁。为什么需要vfork:fork会复制一个独立的数据段和栈,exec的调用会产生新的数据段和栈。在这种情况下,之前复制数据段和代码段的行为就没有意义了。复制是浪费时间和空间。为了提高效率,创建了一个vfork。vfork不会复制数据段和栈,它生成的子进程会使用父进程的数据段和代码段。因此,使用vfork代替fork,从效率上来说是非常划算的。线程池工作流程:线程池判断核心线程池中的线程是否都在执行任务。如果没有,则创建一个新的工作线程来执行该任务。如果核心线程池中的线程都在执行任务,则进入下一个流程。线程池决定工作队列是否已满。如果工作队列未满,则将新提交的任务存放到该工作队列中。如果工作队列已满,则进入下一个流程。线程池判断线程池中的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行该任务。如果已经满了,就交给饱和策略来处理这个任务。
