Unix 进程控制基础:权限、解释器与调度
最小特权与身份管理
在设计应用时,我们通常遵循最小特权原则,即程序只应拥有完成其任务所需的最小权限。在 Unix 环境下,这主要通过设置实际用户 ID、有效用户 ID 以及组 ID 来实现。setuid 和 setgid 函数就是用来调整这些标识的关键工具。通过它们,我们可以灵活地控制进程在运行时的身份上下文,确保安全性。
解释器文件机制
当你看到一个脚本文件以 #! 开头时,内核在调用 exec 系列函数时会识别它。例如 #!/bin/sh 告诉内核使用 /bin/sh 来执行该文件。内核实际上并不直接运行这个解释器文件本身,而是根据第一行指定的路径去加载对应的解释器。
这种机制有两个主要好处:一是隐藏了脚本语言的事实,让文件看起来像普通可执行程序;二是允许我们使用除 /bin/sh 以外的其他 Shell 来编写脚本,增加了灵活性。
system 函数的内部实现
很多人对 system 函数很熟悉,但它的底层实现其实挺有意思。它本质上是一个封装,内部依次调用了 fork、exec 和 waitpid。因此,它的返回值有三种情况:
- 如果
fork失败,或者waitpid返回了除EINTR之外的错误,函数会返回-1。 - 如果
exec失败(意味着无法执行 shell),返回值等同于 shell 执行了exit(127)。 - 否则,所有步骤都成功,返回值则是 shell 进程的终止状态。
理解这一点对于处理并发和信号中断非常重要。
进程会计记录
Unix 提供了进程会计功能,一旦启用,内核会在进程结束时写入一条会计记录。典型的记录包含命令名、CPU 时间总量、用户 ID、组 ID 以及启动时间等信息。acct 函数用于启动或禁用会计功能,通常由 accton 命令调用。
不过,这里有一些需要注意的坑:
首先,永远不终止的进程(如 init)不会产生会计记录。
其次,会计记录的顺序对应于进程终止的顺序,而非启动顺序。
最后,会计记录是针对进程的。在 fork 后内核会为子进程初始化记录,而 exec 不会创建新记录,只是更新命令名。这意味着如果一个进程连续执行了三个程序,会计记录里只会显示最后一个程序的名称,但 CPU 时间是这三个程序的总和。
进程调度与优先级
Unix 对进程调度优先级的控制比较粗粒度,具体策略主要由内核决定。用户可以通过 nice 函数获取或修改进程的 nice 值。调整 nice 值可以让进程以较低的优先级运行,从而减少对 CPU 资源的占用。这在多任务环境中是一种简单的资源协调手段。


