APUE读书笔记:进程控制
重点函数:fork,exit,_exit
一、fork
函数原型:
#include <unistd.>
pid_t fork(void)
函数说明:fork函数将创建一个子进程,该函数调用一次,但是有两次返回。子进程返回值为0,父进程的返回值是该子进程的进程ID。
小技巧:父进程能够拿到该次fork生成的子进程的ID,这是唯一的机会。而子进程不需要关心自己的进程ID。通过是否关心子进程id,就能明确地区分是父进程还是子进程。
相关的函数:
获取自己的进程ID:getpid()
获取父进程ID:getppid()
#include <unistd.h> #include <stdlib.h> #include <stdio.h> int main() { pid_t pid; if ((pid = fork()) < 0) { exit(-1); } else if (pid == 0) { printf("This is child. pid=%d, ppid=%d\n", getpid(), getppid()); //child exit(0); } else { sleep(2); printf("This is parent. pid=%d, ppid=%d\n", getpid(), getppid()); //parent exit(0); } return 0; }
fork后的父子进程会共享哪些资源?
会共享持有的所有文件句柄。更多详情查看APUE(unix环境高级编程)中文版的175页。
特别地,IO缓存区作为特殊的文件句柄,也是会被共享的。
fork常见的使用场景:
1,一个父进程希望复制自己,使父子进程同时执行不同的代码段。这在网络服务中是常见的,父进程等待客户端的服务请求,当请求到达时,父进程fork一个子进程,子进程负责对该次请求作出处理和响应,而父进程则等待下一次服务请求的到达。
2,一个进程要执行一个不同的程序。在shell中是常见的,子进程从fork返回后立即调用exec。
常见的shell执行流:
假如要执行这条命令:ls -la ./,shell会先fork一个子进程,该子进程负责对命令进行处理,子进程处理结束后告诉它的父进程(shell进程)说我处理完了,可执行下一次命令。
二、exit退出函数
摘自APUE,178页
进程有5种正常终止方式:
1,在main函数内调用return。这种方式类似于exit。
2,调用exit函数。该函数由ISO C定义,其操作包括调用各种只处理程序,然后关闭所有标准IO流等。因为ISO C并不处理文件描述符,多进程以及作业控制,所以这一定义对于UNIX系统而言是不完整的。
3,调用_exit或者_Exit函数。ISO C定义_Exit,其目的是为进程提供一种无需运行终止处理程序或心法处理程序而终止的方法。对标准IO流是否进行冲洗,取决于系统的实现。在UNIX中,_exit和_Exit是同义的,并不清洗IO缓存(这意味着如果使用_exit来终止程序的话,io缓存区的内容不会输出到屏幕,也不会输出到文件)。_exit由exit调用。
4,进程的最后一个线程在启动例程中执行返回语句。
5,进程的最后一个线程调用pthread_exit函数。
有3种异常终止方式:
1,调用abort函数。
2,当进程接收到某些信号时。
3,最后一个线程对取消(cancellaction)请求作出响应。
不管如何终止,最后都会执行内核中的同一段代码。这段代码为响应进程关闭所有打开描述符,释放它所使用的存储器等。
note:对于大多数unix系统而言,exit是标准C库的一个函数,_exit是一个系统调用(系统调用指的是内核提供的接口)。
获取当前进程id:getpid
获取父进程id:getppid