说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
Linux网络编程、linux系统编程
前提概念
僵死(zombie)进程
1️⃣目的:父进程可获取终止的子进程信息
2️⃣概念:父进程未终止,子进程先终止,将终结的子进程设置为僵死进程。
3️⃣补充:只保留最小的概要信息——一些保存着有用信息的内核数据结构。
僵死进程等待父进程查询自己信息,若父进程获取子进程信息,则子进程消失,否则保持僵死状态
一 、wait()函数
1、函数介绍:
(1)函数功能
用于父进程与子进程的同步。
暂停父进程进入睡眠状态,一旦子进程终止(正常/异常)时【异步事件(在任意时刻都可发生)】,父进程会获取子进程终止状态后重新执行(结束wait系统调用)。
①若有多个子进程在执行,那么父进程中的wait()
在第一个子进程结束时,恢复父进程。
②若所有子进程都还在运行,则阻塞。
③若无任何子进程,则立即出错返回。
项目 | 说明 |
---|---|
函数原型 | pid_t wait(int *wstatus); |
头文件 | sys/types.h、sys/wait.h |
参数说明 | wstatus:整数指针或null指针 |
返回值 | ①返回结束的子进程的进程标识符 ②返回-1,表示没有子进程结束。errno中含有出错代码ECHILD |
注意 | 参数`wstatus`用于获取子进程exit系统调用的参数值(只读取最低的1字节),若不关心则可设为`NULL`。 |
(2)errno错误可能值:
项目 | 说明 |
---|---|
ECHILD | 调用进程没有任何子进程 |
EINTR | 等待子进程结束时,收到了一个信号, wait()提前返回了。 |
(3)检查子进程返回状态的宏:
位于:stdlib.h
或sys/wait.h
宏名 | 说明 |
---|---|
WIFEXITED(status) | 若进程通过系统调用_exit()或exit()正常退出,该宏返回为真(非零值) |
WEXITSTATUS(status) | 若 WIFEXITED (status)返回为真,该宏返回由子进程调用_exit( status)或exit (status)时设置的调用参数status值 |
WIFSIGNALED(status) | 若进程是由信号引起的进程终止,该宏返回为真(非零值) |
WTERMSIG(status) | 若WIFSIGNALED (status)返回为真,该宏返回导致子进程退出的信号编号 |
WCOREDUMP(status) | 若收到信号发生主存信息转储,则该宏返回真(非零值) |
WIFSTOPPED(status) | 若子进程暂停(停止并可继续执行),则该宏返回非零值。可通过ptrace()跟踪(只有当实现一个调试器才有用) |
WSTOPSIG(status) | 如果 WIFSTOPPED (status)返回非0,该宏返回导致子进程停止的信号编号 |
WIFCONTINUED(status) | 若子进程暂停(停止并可继续执行),则该宏返回非零值。可通过ptrace()跟踪(只有当实现一个调试器才有用) 新标准将它定义为waitpid() |
PS:
①WIFEXITED(status)
常与WEXITSTATUS(status)
搭配使用。【正常退出结束】
②WIFSIGNALED(status)
常与WTERMSIG(status)
搭配使用。【信号引起结束】
(4)wait系列其他函数
后面的数字表示对应的参数个数
①wait3()
:等待任何一个子进程改变状态(结束)
②wait4()
:等待某个特定子进程改变状态(结束)
PS:
wait3()
、wait4()
不是POSIX定义,最好不使用,虽然几乎所有UNIX系统都支持。
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3 (int *status, int options, structrusage *rusage);
pid_t wait4 (pid_t pid, int *status, int options,struct rusage *rusage);
(5)与waitpid()函数对比
1️⃣子进程终止前,wait()
使调用者阻塞,waitpid()
可通过选项使调用者不阻塞。
2️⃣waiitpid()
并不等待调用后的第一个终止子进程,有若干选项控制等待进程。
2、示例实践:
(1) 源程序:wait.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main (int argc,char* argv[])
{
int status = 0;
pid_t pid = 0; //子进程PID
pid_t wpid =0; //第一个结束的子进程PID
/**
* Step 1:成功创建子进程并立即执行不同形态退出动作
*/
pid = fork();
if (pid < 0)
{
perror("fork");
}
else if(pid > 0){
printf("I am parent process %d\n",getpid());
}
else if(pid == 0)
{
printf("I am child process %d\n",getpid());
/* 情形一:子进程没有返回 (wait返回-1)*/
//空
/* 情形二: 子进程正常退出 (WIFEXITED)*/
// exit(1); // 等效于 return 1;
// exit(0); // 等效于 return 0;
/* 情形三: 子进程得到信号没被捕捉退出 (WIFSIGNALED)*/
/* WTERMSIG 用该宏返回导致子进程退出的信号值 */
// abort(); //向自己发送SIGABRT信号
/* 情形四: 多子进程 */
sleep(10);
}
/* 情形四: 多子进程 */
if(!fork())
{
printf("I am sibling process %d\n",getpid());
exit(0); //直接退出
}
/**
* Step 2:父进程调用wait系统调用,进入睡眠状态
* wait()的返回值:
* 1.返回已终止子进程PID
* 2.返回-1(出错)
*/
wpid = wait(&status); //wpid表示第一个子进程退出的PID
if (wpid == -1)
{
perror ("wait");
}
printf ("wpid=%d\n", wpid); //输出PID
/**
* Step 3:根据子进程不同退出状态 输出语句
*/
if (WIFEXITED (status))
{
printf("PID = %d\n",getpid());
printf ("Normal termination with exit status=%d\n", WEXITSTATUS (status));
}
else if (WIFSIGNALED (status))
{
printf("PID = %d\n",getpid());
printf ("Killed by signal=%d%s\n",WTERMSIG(status),WCOREDUMP(status)?"(dumpedcore)":"");
}
else if (WIFSTOPPED (status))
{
printf("PID = %d\n",getpid());
printf ("Stopped by signal=%d\n", WSTOPSIG(status));
}
else if (WIFCONTINUED (status))
{
printf("PID = %d\n",getpid());
printf ("Continued\n");
}
return 0;
}
(2)编译运行及结果
编译:gcc wait.c -o wait
运行:./wait
j结果:
I am parent process 8501
I am sibling process 8503
wpid=8503
PID = 8501
Normal termination with exit status=0
I am child process 8502
I am sibling process 8507
wpid=8507
PID = 8502
Normal termination with exit status=0
(3)总结
<1>进程结构:
xx─┬─ 8501
└─ 8502 ─┬─ 8503
└─ 8507
<2>解释:
父进程创建两个进程 8502 与 8503,其中 8503 创建后直接退出,8502 延时10s。
父进程调用wait()
函数进入睡眠状态,接收到 8503 的结束信号,输出语句:Normal termination with exit status=0
子进程 8503 也创建进程 8507 ,并调用wait()
函数进入睡眠状态,等待进程 8507结束后退出,并输出:Normal termination with exit status=0
<3>结论:
验证了调用wait()进入父进程进入睡眠状态,若多子进程中某子进程先行结束,则返回该子进程PID,父进程进入运行状态。
二 、 waitpid ()函数
1、函数介绍
(1)函数功能:
等待特定进程(可通过额外参数进行微调)。
情景:
若一个进程有多个子进程,但无需等待所有子进程结束,父进程只想等待其中一个特定的子进程。
解决方法:
①多次调用wait()
,每次根据返回值判断是不是特定进程。
②调用waitpid()
系统调用。
项目 | 说明 |
---|---|
函数原型 | pid_t waitpid(pid_t pid, int *wstatus, int options); |
头文件 | sys/types.h、sys/wait.h |
参数说明 | 1.pid:需等待的pid(一个或多个),可能情况: ①< -1 ,等待其组ID是参数pid的绝对值的任一子进程。 ②== -1 ,等待任一子进程,与`wait()`等效。 ③== 0 ,等待与调用进程处于同一进程组的任一进程 。 ④ > 0 ,等待进程ID等于实参进程ID。 |
2.wstatus:整数指针或null指针 存放进程结束状态的存储空间 | |
3.options:0或多个选项按二进制“或”运算结果 | |
返回值 | ①返回终止进程ID,并将该子进程终止状态存放__stat_loc指向的存储单元。 ②若指定进程或进程组不存在或参数pid指定进程不是调用进程的子进程都将出错。 |
注意 |
(2)参数__options常量:
参数可为0,或以下表格中常量按或运算的结果。
常量 | 说明 |
---|---|
WCONTINUED | 若实现支持作业控制。那么由pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态(POSIX.1的XSI扩展) |
WNOHANG | 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0 |
WUNTRACED | 若某实现支持作业控制,而由pid指定的任一子进程已处于暂停状态,并且其状态自暂停以来还未报告过、则返回其状态。WIFSTOPPED宏确定返回值是否对应于一个暂停子进程 |
(3)与wait()函数对比
1️⃣waitpid()
可等待一个特定的进程,而wait则返回任一终止子进程的状态。在讨论popen()
函数时会再说明这一功能。
2️⃣waitpid()
提供了一个wait()
的非阻塞版本。有时用户希望取得一个子进程的状态,但不想阻塞。
3️⃣waitpid()
支持作业控制(利用WUNTRACED
和WCONTINUED
选项)。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main (int argc,char* argv[])
{
int status;
pid_t pid;
pid = waitpid (1742, &status, WNOHANG);
if (pid == -1){
perror ("waitpid");
}
else {
printf ("pid=%d\n", pid);
if (WIFEXITED (status))
{
printf ("Normal termination with exit status=%d\n", WEXITSTATUS (status));
}
if (WIFSIGNALED (status))
{
printf ("Killed by signal=%d%s\n", WTERMSIG(status), WCOREDUMP(status)?"(dumped core)":"");
}
}
}
wait (&status);
与waitpid (-1, &status, 0);
两者等效。
三 、waitid()函数
1、函数介绍:
(1)函数功能:
与wait()、waitpid()
一样,等待子进程结束,了解子进程状态改变的信息(终止、停止或继续运行),但具有更多选项。
使用单独的参数表示要等待的子进程类型,而不是将此与进程ID或进程组合成一个参数、
项目 | 说明 |
---|---|
函数原型 | int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); |
头文件 | sys/types.h、sys/wait.h |
参数说明 | 1.idtype:等待PID类别:下表三种中的一种 |
2.id:通用ID号 | |
3.infop:必须指向一个有效的siginfo_t类。 成功调用会保证以下成员被填充 | |
4.options:0或多个选项按二进制“或”运算结果 | |
返回值 | 成功返回0,错误返回-1,并设置errno值 |
注意 | ①使用__idtype与__id指定要等待的子进程 |
(2)参数idtype的值(可能扩充)
常量 | 说明 |
---|---|
P_PID | 等待pid值是id的子进程。 |
P_GID | 等待进程组ID是id那些子进程 |
P_ALL | 等待所有子进程,参数id被忽略。 |
(3)参数options常量:
以下表格中一个常量或多个常量按或运算的结果。
常量 | 说明 |
---|---|
WEXITED | 调用进程会等待结束的子进程(由id 和 idtyp指定)。 |
WSTOPPED | 调用进程会等待收到了信号而停止了执行的子进程。 |
WCONTINUED | 调用进程会等待收到了信号而继续执行的子进程。 |
WNOHANG | 调用进程不会阻塞,如果没有子进程结束(停止或者继续执行),它就立刻返回。 |
WNOWAIT | 调用进程不会移除满足条件的子进程的僵死状态。调用进程可能会在将来继续等待。 |
(4)参数infop:
成员 | 说明 |
---|---|
si_pid | 子进程的pid |
si_uid | 子进程的uid |
si_code | 根据子进程的状态是被信号所终止、杀死、停止或者继续执行而分别设置为CLDEXITED、CLD_KILLED、CLD_STOPPED或者CLD_CONTINUED中的一个。 |
si_signo | 设置为SIGCHLD。 |
si_status | 如果si.code是CLDEXITED,它是子进程的退出值。否则,它引起状态改变的那个信号的编码。 |
(5)errno值
常量 | 说明 |
---|---|
ECHLD | 由id和idtype确定的进程不存在。 |
EINTR | 一个信号打断了子进程的执行,但是在options里没有设置WNOHANG。 |
EINVAL | options参数不合法,或者id和 idtyp 的组合不合法。 |
四、sleep()
一、函数介绍
函数功能:
使进程休眠SECONDS
秒,或直到信号到达且不被忽略。
项目 | 说明 |
---|---|
函数原型 | extern unsigned int sleep (unsigned int __seconds); |
头文件 | unistd.h |
参数说明 | 1.__seconds:秒数 |
返回值 | 返回小于实际休眠seconds的秒数(因此,如果它完全休眠,则返回0)。 |
注意 | 若一个信号处理程序在' sleep'调用中执行' longjmp'或修改对SIGALRM信号的处理,则之后对SIGALRM信号的处理是未定义的。 |