创建子进程

2014-08-01 06:50:50   最后更新: 2016-07-18 08:25:58   访问数量:642




pid_t fork( void);

 

定义于 unistd.h 头文件中,在父进程中返回子进程ID,子进程中返回0,出错返回-1

 

fork函数用于创建子进程,fork被调用一次返回两次,分别在父进程中返回子进程ID,子进程中返回0,父进程除了在fork子进程时获得子进程的PID外,没有其他方法可以获得 其子进程的ID,因为没有相关的函数,而子进程只要调用getppid函数就可以获得其父进程的PID。

子进程调用fork函数后,子进程是父进程的副本,子进程获得父进程数据空间、堆、栈的副本,父子进程并不共享这些空间部分,父子进程共享正文段,即机器指令部分。

 

e.g.

/* * file: fork.c * author: 龙泉居士 * date: 2012-09-11 16:49 */ #include "../apue.2e/include/apue.h" int glob = 6; char buf[] = "a write to stdout\n"; int main () { int var; pid_t pid; var = 88; if (write (STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1) err_sys("write error"); printf ("before fork\n"); if ((pid = fork()) < 0) err_sys ("fork error"); else if (pid == 0) { glob++; var++; } else sleep(2); printf ("pid = %d\nglob = %d\nvar = %d\n", getpid(), glob, var); exit(0); }

 

 

运行结果如下:

a write to stdout

before fork

pid = 2669

glob = 7

var = 89

pid = 2668

glob = 6

var = 88

 

一般来说,fork之后父进程与子进程谁先执行是取决于内核的具体调度算法。如果要求父子进程相互同步,则要求某种形式的进程间通信,想让子进程先执行,可以让父进程sleep一段时间,但这段时间是否足够是无法判定的。

父进程打开的所有文件描述符都被复制到子进程中,父子进程的每个打开的文件描述符都共享一个文件表项,因此此时对同一文件进行读写可能会有很多种情况发生,需要有一定行使得同步,但一般情况下,系统会选择让一个进程等待另一个进程读写后进行读写。

 

pid_t vfork( void);

 

定义于 unistd.h 头文件中,在父进程中返回子进程ID,子进程中返回0,出错返回-1

 

vfok和fork的调用序列和返回都与fork一样,他们的区别在于:

  1. vfork创建的新进程的目的是exec一个新程序,所以他不将父进程地址空间的全部内容复制给子进程,而在子进程exec之前,他在父进程的地址空间中运行
  2. vfork保证子进程运行,父进程只有子进程调用exec或exit后才会继续运行,如果调用这两个函数之前,子进程依赖于父进程的进一步动作,则会导致死锁。

 

e.g.

/* * file: vfork.c * author: 龙泉居士 * date: 2012-09-12 11:31 */ #include "../apue.2e/include/apue.h" int glob = 6; int main () { int var; pid_t pid; var = 88; printf ("before vfork"); if ((pid = vfork()) < 0) err_sys("vfork error"); else if (pid == 0) { glob++; var++; _exit(0); } else printf ("pid= %d\nglob = %d\nvar = %d\n", pid, glob, var); exit (0); }

 

 

运行结果如下:

beforevforkpid = 5697

glob= 7

var= 89

 

可见,子进程更改了父进程的变量值,这是因为子进程在exec或exit之前是一直运行在父进程的地址空间中的,所以所有更改都会直接影响到父进程。

另外,因为调用的是_exit,所以并没有在24行子进程退出时冲洗缓冲、关闭流等工作,因为如果关闭流,那么可能会导致父进程无法打印的情况,虽然现代实现不再在流的关闭方面自找麻烦了,在进程终止时不再在库中关闭流,而是只在内核中关闭流,所以不会影响到父进程的打印。

 

进程有三种正常终止函数和三种异常终止情形,但我们一般都希望终止进程可以通知其父进程它是如何终止的。

对于三个正常终止函数,子进程会将其退出状态作为参数传给函数,在异常终止情况下,内核产生一个指示其终止原因的终止状态。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。

对于fork开辟的子进程,父进程可能在子进程终止前先终止,这样的情况,子进程的父进程会改为init进程,我们称这种情况为进程领养。

当终止进程时,其父进程调用wait或waitpid时,可以得到很多信息,这些信息至少包括进程ID、该进程的终止状态以及该进程使用的CPU时间总量。

内核可以释放终止进程所使用的所有存储区,关闭所有打开文件。

在UNIX系统的术语中,一个已经终止但没有进行善后处理的进程被称为“僵死进程”,ps命令将僵死进程打印为Z,对于fork出来的进程,除非其父进程等待取得子进程的终止状态或提前终止,否则这些子进程终止后就会变成僵死进程,僵死进程会造成系统资源的浪费

 






读书笔记      技术帖      linux      unix      c语言      龙潭书斋      apue      unix环境高级编程      进程      fork     


京ICP备15018585号