前言
今天读完了《UNIX环境高级编程》第八章,这一章围绕着进程的创建过程需要涉及的各种配置进行讲解,还讲解了如何处理子进程的退出。
exec函数族
当使用fork函数创建子进程后,这个子进程执行的程序有两种:
- 与父进程执行同一个程序的不同代码段
- 执行另一个程序
如果需要子进程执行父进程的代码,我觉得的可以考虑使用线程,而不是进程了。所以我觉得,创建子进程主要就是用来执行另一个程序的,而这就涉及到exec函数族了。
需要注意的是,执行exec函数,并没有创建新的进程,而是加载了新的程序。(我们可以用C语言写一个类似脚本的东西,在这个程序里,调用exec执行不同的程序)
这个函数族有7个函数,目的都是用新的程序替换从父进程复制来的程序,主要区别有四种:
- 使用可变长参数传递命令行参数和环境变量参数,exec后面为 l (list);使用数组传递命令行参数和环境变量参数,exec后边为v(vector)。
- 如果可以传递环境变量参数,则exec后边加上e(environ)
- 如果使用PATH查找文件,则exec后面加上p(path)
- 使用文件描述符定位文件,则exec前面加上f
在实现这些函数时,并不是每个函数都单独时,可能一个会是另一个的封装,书中就提到一种实现,其关系如下图:
当使用exec执行脚本文件时,并不是把脚本文件当作可执行文件,进行加载,而是加载脚本文件的第一行指定的解释器程序作为进程的上下文。
进程权限
进程的权限通过检查进程的几种ID决定(不同的UNIX实现,可能还有别的的ID)
当程序被执行时,系统会将实际用户ID和组ID设置为执行用户的ID和组ID。然后检查程序文件的set-user-id位,如果设置该位,则有效用户ID为文件的uid,对组ID同理。然后将有效用户ID和组ID复制到保存的设置用户ID和组ID(这一过程一般由exec函数做)
以上时,创建一个进程时,其默认的权限。UNIX系统还提供了一些函数,来修改这些ID,以改变进程的权限。
对于普通用户,其不能随意设置用户ID和组ID,由于一般系统进行权限检查时,主要使用的是有效用户ID和组ID,所以一般进程也是通过修改有效用户ID,来改变进程的权限的。但是这个有效用户ID只能从实际用户ID和保存设置用户ID中选,不能设置为其他的用户ID。
处理子进程终止状态
当子进程退出时,系统不会立即删除该子进程的进程描述符,并将进程状态设置为Z(zombie),然后等待父进程处理子进程的退出。如果父进程不处理,那么子进程就会一直处于僵尸状态。
父进程处理子进程使用wait函数族。当使用wait函数处理完子进程的终止状态后,子进程才彻底的消失了,在次调用wait函数处理该子进程将会出错。
子进程的终止状态一般由一个整型变量status表示,这个变量主要包含两方面的信息
- 进程的退出状态(main函数的返回值,或者exit函数的参数)
- 进程退出时,涉及的一些信号,如进程异常退出,就会由SIGABRT信号
当然其实还可以获取子进程的更多的信息,如运行时长等,这一般要使用wait3和wait4函数。
在使用wait函数时,一般等待三种进程的退出
- 等待任意一子进程退出
- 等待具体的某个子进程退出
- 等待某个进程组的任意一子进程退出
一般情况下wait函数的目的就是取出子进程的终止状态,但是通过设置一些选项,我们可以控制wait的一些行为,比如非阻塞式的,只查看是否有子进程退出,而不取出该子进程的终止状态等。