1、wait和waitpid函数 #include pid_t wait(int *statloc); pid_t waitpid(pid_t pid, int *statloc, int options); 注1:不管子进程正常还是异常终止,内核都会给父进程发送SIGCHLD 信号,默认父进程是忽略该信号 注2:wait可以block调用者直到子进程结束,而waitpid有一个选 项让调用者不被block,它不会等子进程首先结束,而是利用选 项控制等待哪个进程 注3:如果调用者block并且有很多子进程,那么只要有一个子进程结 束wait立即返回,并且可以根据返回的ID得知哪个子进程结束 注4:指针指向终止进程的终止状态,NULL表示不想关心,该整数的 一些bits表示exit状态(正常退出),一些bits表示signal number(异常退出),有1bit表示是否生成了core文件 可以通过以下宏来获取程序是怎样退出的,true or false #include WIFEXITED(status) ; //获取子进程传递给exit参数的低8位,正常结束 WIFSIGNALED(status) //获取异常终止的signal number WCOREDUMP(status); //进程终止是否生成core文件 WIFSTOPPED(status); //若子进程stopped,则为true,可以用WSTOPSIG获取让子 //进程stop的signal number WIFCONTINUED(status); //若子进程在job control stop之后已continue,则为 // true 注5:pid参数 pid == -1 等待任何子进程,waitpid等同于wait pid > 0 等待ID等于pid的子进程 pid == 0 等待任何子进程,其进程group ID与调用进程一 样 pid < -1 等待任何子进程,其进程group ID等于正pid值 注6:options参数 WNOHANG nonblocking版本的wait WCONTINUED WUNTRACED 这2支持 JOB CONTROL 2、#include #include #include #include pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); 注1:struct rusage 终止进程使用的资源概况:使用的user CPU time, system CPU time, page fault数量,接受到的singal数量等等 和它所有的子进程
3、Race Conditions 注1:若想等待子进程结束,就必须调用wait函数系列中的一个,若 想等待父进程结束,可以使用 while ((getppid() != 1) sleep(1);//waste CPU time, every 1 second 注2:设想的一种互相通讯的方式 TELL_WAIT(); //set things up for TELL_xxx & WAIT_xxx /* * child process */ TELL_PARENT(getppid()); //tell parent we are done WAIT_PARENT(); // and wait parent /* * parent process */ TELL_CHILD(pid) //tell child we are done WAIT_CHILD(); // and wait for child
4、exec函数 #include int execl(const char *pathname, 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[]); (-1 on error, no return on success)
Function
pathname
filename
Arg list
argv[]
environ
envp[]
execl
.
.
.
execlp
.
.
.
execle
.
.
.
execv
.
.
.
execvp
.
.
.
execve
.
.
.
letter
p
l
v
e
Tab.1 Difference among six exec functions
Fig.1 Relationship of the six exec funcions
注1:调用exec函数之后,进程完全被新程序替代,从新程序的main 函数开始执行;但进程ID不改变,因为并没有新建一个进程,只 是从磁盘上读取了新程序,填充到当前进程的text,data, heap,stack 注2:使用fork()可以创建一个新进程;使用exec可以开始一个新程 序 注3:envp[] 传递环境变量字符串数组 environ 拷贝当前进程的环境变量给新程序 Arg list 由ARG_MAX(resouce limit)限制 注4:exec几乎继承了调用进程的所有属性甚至包括file locks file mode creation mask, process signal mask, pending singals, resouce limits 注5:进程里每个打开的文件描述符(fd)都有一个close_on_exec标 志,若设置了,则描述符会在exec中关闭,默认还是开启的; 但POSIX.1特别说明了要求open directory streams (opendir)在exec过程中关闭 注5:跟子进程继承不同的是,effective user ID可能在这过程中 改变,得看执行程序文件的set-user-ID和set-group-ID位 注6:只有execve是系统调用函数,其他5个都是库函数
5、Changing User IDs and Group IDs #include int setuid(uid_t uid); int setgid(gid_t gid); (0 if OK, -1 on error) 注1:若进程有超级用户权限,那么setuid将real/effective/ saved set-user-ID都设置成uid 若进程没有超级用户权限,那么setuid只将effective user ID设置成uid,其他2个不变 若前2个条件都不满足,errno会设为EPERM,返回-1 注2:只有超级用户进程可以改变real user ID。一般,real user ID在登录的时候被login程序设置,且不改变 saved set-user-ID在exec过程中从effective ID拷贝 #include int setreuid(uid_t ruid, uid_t euid); int setregid(gid_t rgid, gid_t egid); (0 if OK, -1 on error,交换real和effective ID) int seteuid(uid_t uid); int setegid(gid_t gid); (0 if OK, -1 on error,只有effective ID改变)
Fig.2 Summary of all the funcitons that set the various user IDs