fork和vfork都是创建进程,exit和_exit都是退出进程,但之间也有些细微的区别,并且很让人迷惑。一般情况下用fork创建的子进程用exit结束,而用vfork创建的进程则用_exit。那他们具体的区别在哪儿了,为什么这么做!

预备知识:程序的存储空间布局,系统的缓冲区

 

c程序在内存中从高地址到低地址依次为:

命令行参数和环境变量:存储命令行的各个参数和系统的环境变量

栈段:局部变量(函数内部定义使用的变量)和函数调用存放的位置,此块由操作系统处理

堆段:通常在堆里面进行动态存储分配,malloc就是分配堆里面的空间,通常由程序员控制

数据段:存储一些初始化数据和非初始化数据

正文段:cpu执行的机器码

 

系统的缓冲区:

操作系统为了提高文件都写效率,都使用的缓存机制,当我们写入一个文件时,很有可能写入了缓冲区而没有真真意义上的写入硬盘,缓存分为三个级别:

全缓冲:当缓冲空间(4096B或其它)写满后,一次性写入硬盘,文件缓存通常如此

行缓冲:遇到输入输出遇到换行符,执行写入操作,命令行界面就是使用行缓冲

无缓冲:这个就不用解释了

 

fork与vfork

执行fork创建子进程时,子进程将拷贝父进程的数据段、堆段和栈段,此时父子进程的数据一致当互相独立,各不影响,子进程对数据的处理不影响父进程。(其实当父子进程都不对三个段进行写操作时,父子进程仍然共享数据段、堆段和栈段,当有任何一方有写操作时,才产生副本,这种方式成为写是复制)

vfork与fork区别有两点:1、vfork创建的子进程与父进程共享数据段,2、vfork创建的子进程优先于父进程执行,当子进程执行时,父进程进入阻塞状态。

所以vfork产生的子进程对数据的修改一定可以影响到父进程的数据

 

exit与_exit

exit:当执行exit时,终止处理程序,执行标准的I/O清楚操作(将缓存中文件写入),调用atexit,调用_exit。由此可见,exit是加强版的_exit.

_exit:通知内核,进程结束

 

测试exit和_exit

 
  
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <unistd.h> 
  4.  
  5. int main(void
  6.     printf("the first line\n"); 
  7.      
  8.     printf("the second line\n"); 
  9.      
  10.     exit(0); 

 

 
  
  1. eno@eno-ThinkPad-T420:~$ vim exit.c 
  2. eno@eno-ThinkPad-T420:~$ gcc exit.c -o exit 
  3. eno@eno-ThinkPad-T420:~$ ./exit  
  4. the first line 
  5. the second line 
  6. eno@eno-ThinkPad-T420:~$  

结果与预期相同,改为_exit(0)

 
  
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4.  
  5. int main(void)  
  6. {    
  7.     printf("the first line\n");  
  8.  
  9.     printf("the second line\n");  
  10.  
  11.     _exit(0);  
  12. }  

 

 
  
  1. eno@eno-ThinkPad-T420:~$ ./_exit  
  2. the first line 
  3. the second line 
  4. eno@eno-ThinkPad-T420:~$  

结果怎么还是一样的,有的系统可能看到是没有输出,这都是正常的,每个系统最输出的缓存机制可能不一致,通常为行缓存,把他强制设置成全缓存看看:

 
  
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <unistd.h> 
  4.  
  5. int main(void
  6.     char buf[4096]; 
  7.   //设置成全缓存
  8.     setvbuf(stdout, buf, _IOFBF, 4096); 
  9.  
  10.     printf("the frist line\n"); 
  11.      
  12.     printf("the second line\n"); 
  13.      
  14.     _exit(0); 

ok,结果与预期相同,缓冲区的数据全部没掉了

 
  
  1. eno@eno-ThinkPad-T420:~$ gcc _exit.c  -o _exit 
  2. eno@eno-ThinkPad-T420:~$ ./_exit  
  3. eno@eno-ThinkPad-T420:~$ 

这么一比划,就基本上明白了为什么fork产生的子进程要用exit而不用_exit了,如果缓冲区的东西都丢了,子进程所产生的数据就不完整,这对于程序使用者来说是不可接受的。那为什么vfork一般用_exit了,难道不怕丢数据么?用vfork的子程序通常用_exit结束,而父进程通常用exit结束,这样做数据就不会丢失,因为他们共享一个数据段,父进程结束会处理所有打开的数据流,将其写入。万一子程序也用exit结束,那就比较麻烦了,试想一下父子进程都把同一份数据写入,那个文件的价值有在哪里。