Linux 进程控制
1 fork 与 vfork 函数
UNIX 提供了两个函数用于创建新进程
#include <unistd.h> pid_t fork(); /* 子进程中返回0,父进程中返回子进程ID,出错返回-1 */ pid_t vfork(); /* 子进程中返回0,父进程中返回子进程ID,出错返回-1 */
1.1 fork 出来的子进程有几点要注意:
- 子进程与父进程分别持有相同的文件描述符,这些文件描述符共用核心中的同一个文件项
- 父进程设置的文件锁不会被子进程继承
- 子进程的未处理的闹钟(alarm)被清除
- 子进程的未处理信号集设置为空集
- 当子进程终止时,内核会向父进程发送 SIGCHLD 信号,但对该信号,系统的默认动作是忽略它
- 子进程会继承父进程的信号处理方式,因为子进程在开始时复制了父进程的存储映像,随意信号捕捉函数的地址在子进程中是有意义的
1.2 fork 与 vfork 的区别在于
- 在子进程调用 exec 或 exit 之前,vfork 并不会将父进程的地址空间复制到子进程中,即 子进程在父进程的空间中运行,因此子进程运行的结果会影响到父进程
- vfork 保证子进程先运行,在它调用 exec 或 exit 之后,父进程才可能被调度运行
2 wait 和 waitpid 函数
#include <sys/wait.h> /* 若成功则返回进程 ID:0,若出错则返回 -1 */ pid_t wait(int* status); pid_t waitpid(pid_t pid,int* status,int options);
- 如果所有子进程还在运行,则阻塞
- 如果一个子进程已终止,则父进程获取其终止状态并返回子进程 pid
- 如果父进程没有任何子进程,则 报错返回
- waitpid 可以通过设置选项,使得调用者不阻塞
2.1 参数 status 说明
参数 status 可以为空指针,表示不关心终止状态
在 <sys/wait.h>
中有定义四个宏来获取进程终止的原因
WIFXITED(status)
子进程是否正常终止,若为真,则可以通过宏 WEXITSTATUS(status)
来获取退出的状态码
WIFSIGNALED(status)
子进程是否因收到一个不可捕获的信号而异常退出,若为真,可以通过 WTERMSIG(status)
获取信号编号,某些系统可以通过 WCOREDUMP(status)
判断是否产生 core
文件
WIFSTOPPED(status)
子进程是否处于 stop 状态,若为真,则可以通过 WSTOPSIG(status)
来获取使子进程暂停的信号编号
WIFCONTINUED(status)
子进程是否处于 continue 状态
2.2 参数 pid 的说明
- pid == -1,等待任一子进程,此时 waitpid 与 wait 等效
- pid > 0,等待进程 id 与 pid 相等的子进程
- pid == 0,等待组 ID 等于调用进程组 ID 的任一子进程
- pid < -1,等待其组 ID 等于 pid 绝对值的任一子进程
2.3 参数 optioins 说明
通过设置 options 参数,waitpid 不一定等到子进程退出来能获取子进程的状态,也能在子进程处于 stop 或 continue 状态时返回子进程的状态
- WNOHANG:waitpid 不阻塞
- WUNTRACED:若子进程处于 stop 状态,且该 stop 状态尚未报告时,返回其状态
- WCONTINUED:若子进程处于 continue 状态,且该 continue 状态尚未报告时,返回其状态.
3 waitid 函数
waitid 可以指定要监控的子进程的哪些状态变化
#include <sys/wait.h> int waitid(idtype_t idtype,id_t id,siginfo_t* infop,int options);
- 参数 idtype_t 指定了要等待的子进程的类型
- P_PID
- 等待一个特定的进程
- P_PGID
- 等待一个特定进程组中的任一子进程
- P_ALL
- 等待任一子进程,此时忽略参数 id 的值
- 参数 id 的作用则跟 idtype 的值相关
- 参数 options 指定关注子进程的哪些状态变化
- WNOHANG
- 非阻塞等待
- WNOWAIT
- 不破坏子进程的退出状态,该状态可由后续 wait,waitid 或 waitpid 调用获取
- WSTOPPED
- 等待一个尚未报告的 STOP 状态的子进程
- WCONTINUED
- 等待一个尚未报告的 CONTINUE 状态的子进程
- WEXITED
- 等待已退出的子进程
4 exec 系列函数
- 由于 exec 一个新程序后,信号处理函数的地址已经失效了,因此 exec 函数将原先设置为要捕获的信号都更改为它们的默认动作
4.1 exec 系列函数的区分
- 字母p表示该函数可以为不带目录的文件名,则会从 PATH 环境变量中搜索可执行文件
- 字母l表示该函数的参数要一个一个的在函数签名中列出来,最后以一个(char*)0 结尾表示参数终结
- 字母v表示该函数取一个 argv[] 数组作为传递給新进程的参数
- 字母e表示该函数接收一个 envp[] 数组,可以分配不同于当前环境的新环境
4.2 exec 后的新进程与原进程的关系
- 新进程保持
- 原 pid 和 ppid
- 原实际用户 id 和实际组 id
- 附加组ID
- 进程组ID
- 会话ID
- 控制终端
- alarm尚存留的事件
- 当前工作目录
- 根目录
- 文件模式创建屏蔽字
- 文件锁
- 进程信号屏蔽
- 未处理信号
- 资源限制
- tms_utime、tms_stime、tms_cutime 及 tms_cstime
- 是否关闭原进程打开的文件与该文件描述符的 close-on-exec 标志有关
- 若设置了该标志,则指向 exec 时会关闭该描述符
- 若没设置该标准,则保持描述符打开
- 除非特地用 fcntl 设置了该标志,否则系统的默认为关闭该标志
- exec 时 明确会关闭打开的目录流(opendir)
4.3 当 exec 调用一个带 #! 解析器程序 可选参数项
的脚本文件时:
1、exec 实际执行的并不是该脚本文件,而是脚本文件第一行 #! 解析器程序 可选参数项
中的 解析器程序
2、exec 传递给该解析器程序的参数顺序为,解析器地址,可选参数,脚本文件地址,除 argv0 外的由 exec 函数传入的其他参数
/* /tmp/testinterp的内容为: #! /usr/bin/echo.exe args: */ #include <unistd.h> int main(){ execl("/tmp/testinterp","testinterp","myarg1","MY ARG2",(char*) 0); }
args: /tmp/testinterp myarg1 MY ARG2
5 进程会计
大多数 UNIX 系统都提供了一个选项以进行进程会计(process accounting)处理,启用该选项后,每当进程结束时,内核就写一个会计记录
会计记录是在 fork 时产生而不是 exec 时产生 但 exec 会改变响应记录中的命令名,而且 AFORK 标志也会被清除
会计记录一般为二进制格式的,且结构各个系统实现的都不一样,一般可以在 <sys/acct.h>
中查到 struct acct
root 用户可以使用 accton 命令行来对命令行开启进程会计选项
6 进程时间
任何进程都可以通过调用 times 函数获得自己的运行时间,用户态 CPU 时间和内核态 CPU 时间
#include <sys/times.h> /* 若成功返回进程相对上一次运行 times 函数的运行时间,单位为时钟滴答数,若出错返回 -1 */ clock_t times(struct tms* buf); struct tms{ clock_t tms_utime; /* user CPU time */ clock_t tms_stime; /* system CPU time */ clock_t tms_cutime; /* user CPU tim, terminated children */ clock_t tms_cstime; /* system CPU time, terminated children */ }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: 用户ID 与 组ID
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论