2021SC@SDUSC
文章目录- 进程/线程相关
- execXX 函数组
- execve
- sleep
- wait 函数组
- wait
- waitpid
- wait3/wait4
- 如何使用int* status
- WIFEXITED(int status)
- WEXITSTATUS(status)
- WIFSIGNALED(status)
exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,
并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。
另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行的脚本文件。
exec函数一共有六个,其中execve为内核级系统调用,
其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。
不同之处在于如何找到程序、如何指定参数以及环境来自何处。
-
名称中带有 v 的调用,用带有一个数组参数来指定argv[]新程序的数组。数组的最后一个元素为 NULL。
-
名称中带有 l 的调用,将新程序的参数作为函数本身的可变长度参数列表。参数的结尾为参数指针(char *)NULL。
你应该始终进行类型转换,因为NULL实际是整数常量,并且调用可变参数函数时的默认参数转换不会将其转换为指针。 -
名称中带有 e 的调用,需要一个额外的参数(或l情况下的参数)来提供新程序的环境;否则,程序将继承当前进程的环境。
- 头文件:
#include
-
函数说明:
pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。 -
函数定义:
int execve(const char *pathname, char *const argv[], char *const envp[]);
-
返回值:
如果执行成功则函数不会返回(后面的代码也并不会执行了),执行失败则直接返回-1,失败原因存于errno 中。 -
示例代码:
#includeint main() { char *argv[] = {"ls", "-el", NULL}; char *envp[] = {"PATH=/bin", 0}; execve("/bin/ls", argv, envp); }
参考链接:
- execve(2) — Linux manual page
- What is the difference between the functions of the exec family of system calls like exec and execve?
- 头文件:
#include
-
函数说明:
sleep()会令目前的进程暂停, 直到达到参数seconds 所指定的时间, 或是被信号所中断. -
函数定义
unsigned int sleep(unsigned int seconds);
- 返回值
若进程暂停到参数seconds 所指定的时间则返回0, 若有信号中断则返回剩余秒数.
进程调用 exit() 退出执行后,被设置为僵死状态。
这时父进程可以通过 wait4() 系统调用查询子进程是否终结,之后再进行最后的操作,彻底删除进程所占用的内存资源。
wait4() 系统调用由 linux 内核实现。
linux 系统通常提供了 wait()、waitpid()、wait3()、wait4() 这四个函数,
四个函数的参数不同,语义也有细微的差别,但是都返回关于终止进程的状态信息。
- 头文件:
#include// 提供类型 pid_t 的定义 #include
-
函数说明:
当进程调用 wait() 时,会暂停目前进程的执行(即阻塞),由 wait() 来自动分析是否当前进程的某个子进程已经退出,
如果找到了这样一个已经变成僵尸进程的子进程,wait 就会收集这个子进程的信息,并将其彻底销毁后返回;
如果没有找到这样一个子进程,wait 就会一直阻塞在这里,直到出现僵尸进程。 -
函数定义
pid_t wait(int *status);
参数 status 保存着子进程退出时的一些状态(包括 task_struct、thread_info及内核栈等)它是一个指向 int 类型的指针;
如果不在意子进程的结束状态值,只想把这个僵尸进程消灭掉(实际上,大多数时候都是这样做的),则可以将这个参数设为 NULL。
关于 status参数比较复杂,可以参考这里
传送门
- 返回值
如果 wait() 调用成功,则会返回被收集子进程的进程ID;如果被调用进程没有子进程,则调用失败,返回 -1
- 函数原型:
pid_t waitpid(pid_t pid,int *status,int options);
waitpid() 函数的功能与 wait() 的功能类似,不过,它比 wait() 函数多了两个参数:
-
1)参数 pid 为欲等待的子进程的识别码:
pid < -1 :等待进程组 ID 为 pid 绝对值的进程组中的任何子进程;
pid = -1 :等待任何子进程,此时 waitpid() 相当于 wait()。实际上,wait()就是 pid = -1、options = 0 的waitpid(), 且有:
pid = 0 :等待进程组 ID 与当前进程相同的任何子进程(也就是等待同一个进程组中的任何子进程);
pid > 0 :等待任何子进程 ID 为 pid 的子进程,只要指定的子进程还没有结束,waitpid() 就会一直等下去。 -
2)参数 options 提供一些额外的选项来控制 waitpid():
WNOHANG:如果没有任何已经结束了的子进程,则马上返回,不等待;
WUNTRACED:如果子进程进入暂停执行的情况,则马上返回,但结束状态不予理会;
也可以将这两个选项组合起来使用,使用 OR 操作。如果不想使用这两个选项,也可以直接把 options 设为 0 -
3)waitpid() 的返回值,有三种:
a)正常返回时,waitpid() 返回收集到的子进程的PID;
b)如果设置了 WNOHANG,而调用 waitpid() 时,没有发现已退出的子进程可收集,则返回0;
c)如果调用出错,则返回 -1,这时erron 会被设置为相应的值以指示错误所在。(当 pid 所指示的子进程不错在,或此进程存在,但不是调用进程的子进程, waitpid() 就会返回出错,这时 erron 被设置为 ECHILD)
函数原型
#include#include pid_t wait3(int *status,int options,struct rusage *rusage); pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);
wait3() 和 wait4() 函数除了可以获得子进程状态信息外,还可以获得子进程的资源使用信息,这些信息是通过参数 rusage 得到的。
而 wait3() 与 wait4() 之间的区别是,wait3() 等待所有进程,而 wait4() 可以根据 pid 的值选择要等待的子进程。
参数 pid 的意义与 waitpid() 函数的一样。
其中,需要注意的是,参数rusage所指向的结构中返回终止子进程的资源使用情况。其中包括进程使用的CPU时间总量以及内存管理的统计数据。
如下,为它的结构定义:
struct rusage {
struct timeval ru_utime;
struct timeval ru_stime;
long ru_maxrss;
long ru_ixrss;
long ru_idrss;
long ru_isrss;
long ru_minflt;
long ru_majflt;
long ru_nswap;
long ru_inblock;
long ru_oublock;
long ru_msgsnd;
long ru_msgrcv;
long ru_nsignals;
long ru_nvcsw;
long ru_nivcsw;
};
其中比较重要的三项:
- timeval ru_utime; 用户态CPU使用时间
- timeval ru_stime; 内核态CPU使用时间
- ru_maxrss; 最大驻留集
还可以shell中使用
man getrusage如何使用int* status
如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这指向整数值(int),
指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪一个信号结束的等信息。
由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会非常麻烦,
于是就设计了一套专门的宏来完成这项工作,下面介绍一下其中最常用的几个。
请注意,下面的宏中的 status 虽然名字一样,这里的参数status并不同于wait中的参数
一个是指向整数的指针,而是那个指针所指向的整数,切记不要搞混。
这个宏用来指出进程是否为正常退出的,如果是,它会返回一个非零值。
WEXITSTATUS(status)当WIFEXITED返回非零值时,可以用这个宏来提取子进程的返回值。
如果子进程调用exit(5)退出,WEXITSTATUS(status) 就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。
请注意,如果进程不是正常退出的,也就是 说,WIFEXITED返回0,这个值就毫无意义。
所以二者往往这样配合使用:
//正常退出判断
if(WIFEXITED(status)){
printf("child exit with %dn", WEXITSTATUS(status));
}
WIFSIGNALED(status)
这个宏来指出进程是否遇到未处理的信号而意外退出,如果是,返回非零0.
当 WIFSIGNALED 返回非零值时,此时可通过WTERMSIG(status)获取使得进程退出的信号编号
所以二者可以这样配合使用
//因为某种信号中断获取状态
if(WIFSIGNALED(status)){
printf("child killed by %dn", WTERMSIG(status));
}
- 参考链接
wait4(2) — Linux manual page



