Unix进程控制
wait函数和waitpid函数用于捕获子进程停止信号,获取子进程停止状态,并在子进程结束后清除该进程所占用的系统资源。其函数原型为
#include <sys/wait.h> pid_t wait(int *statloc); //返回已结束子进程id或-1 pid_t waitpid(pid_t pid, int *statloc, int options); //返回已结束子进程id、0或-1
wait函数监听所有子进程,当存在任一子进程停止时,wait函数立即返回。waitpid函数监听子进程受pid参数限制,当pid>0时,pid值为指定子进程id;当pid值为0时,waitpid函数监听与父进程具有相同用户组的子进程;当pid值为-1时,waitpid函数监听所有子进程,与wait函数等价;当pid<-1时,waitpid函数监听所有用户组id与pid绝对值相等的子进程。参数statloc用于存储子进程返回状态(是否正常结束、是否异常结束、是否暂停、进程返回值),options用于指定waitpid函数何时返回。
对子进程返回状态需要使用宏定义进行判断,如:
WIFEXITED(status); //子进程正常结束时为真 WIFSIGNALED(status); //子进程被异常终止时为真 WIFSTOPED(status); //子进程被暂停时为真 WIFCONTINUED(status); //子进程继续运行时为真 WEXITSTATUS(status); //获取子进程正常结束时返回值 WTERMSIG(status); //获取子进程异常终止信号 WCOREDUMP(status); //获取子进程异常终止所产生的内核文件 WSTOPISG(ststus); //获取引起子进程暂停的信号
wait3与wait4函数与wait、waitpid函数类似。区别在于wait3、wait4可以读取子进程所占用的资源信息。结构体rusage中内容可通过查询man手册getrusage(2)获得。其函数原型为
pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);
exec系列函数用于在当前进程中执行指定程序。exec系列函数包括
#include <unistd.h> int execl( const char *parhname, const char *arg0, ... /* (char*) 0 */ ); int execv( const char *pathname, char *const argv[]); int execle( const char *pathname, const char *arg0, ... /* (char*) 0 */ , char *const envp[]); int execve( const char *pathname, char *const argv[], char *const envp[]); int execlp( const char *filename, const char *arg0, ... /* (char*) 0 */ ); int execvp(const char *filename, char *const argv[]);
在上述命名中,字母l代表可变参数列表(list),字母v代表向量数组(vector),字母e代表环境参数(environment),字母p代表使用PATH环境路径搜索文件。
exec系列函数的区别在于
- execl、execv、execle、execve函数中第一个参数为可执行文件的绝对路径。execlp、execvp函数中第一个参数为可执行文件的文件名。当filename参数为文件名时,可执行文件搜索顺序为PATH环境变量中值的顺序。
- 当execlp与execvp函数无法找到可机械执行文件时,filename参数将被理解为shell脚本命令,由/bin/sh负责执行该命令。
- 在具有可变参数列表的exec函数(execl、execle、execlp)中,可变参数最后一个参数必须为NULL,代表可变参数结束。可变参数从第0个参数开始。
- execle、execve函数中可指定完整的环境参数列表。其他几个exec函数将复制父进程的环境参数列表。
在大多数Unix系统中,exec系列函数仅有一个系统函数,其他几个函数可通过对系统函数的加工来实现,其调用关系如图1所示
图1. exec系列函数调用关系
使用exec函数执行的程序将从调用进程继承
- 进程ID和父进程ID
- 实际用户ID和实际用户组ID
- 附加用户组ID
- 进程组ID
- 会话ID
- 控制终端
- 警报(alarm clock)剩余时间
- 当前工作目录
- 根目录
- 文件权限掩码(file mode creation mask)
- 文件锁
- 进程信号掩码
- 未处理信号
- 资源限制
- 用户CPU时间(tms_utime)、系统CPU时间(tms_stime)、用户终止子进程时间(tms_cutime)、系统终止子进程时间(tms_cstime)
当调用进程中已打开的文件执行时关闭标识符(close-on-exec flag)被设置,exec函数执行时,文件被关闭。
setuid、seteuid、setreuid函数可用于修改当前进程的真实用户id与有效用户id。其原型为
#include <unistd.h> int setuid( uid_t uid); int seteuid( uid_t uid); int setreuid(uid_t ruid, uid_t euid);
其修改关系如图2所示
图2. setuid、seteuid、setreuid修改关系图
在进程开始运行时,保留设置用户ID(saved set-user-ID)从有效用户id复制而来。用户无法直接获取保留设置用户id的值(仅能够通过获取有效用户id来获得相同值)。
当使用setuid,且当前进程不具有超级用户权限时,如果uid与真实用户id或保留设置用户id相同,则有效用户id被修改为uid的值。若当前进程具有超级用户权限,三种用户id(真实用户id、有效用户id、保存设置用户id)都将被修改。
当使用setreuid时,且无超级用户权限时,其参数必须与有效用户id或真实用户id相同。若不想对某一用户id做出修改,可将参数设置为-1。