??博客主页??:??https://blog.csdn.net/wkd_007??
??博客内容??:??嵌入式开发、Linux、C语言、C++、数据结构、音视频??
??本文内容??:??介绍 ??
??金句分享??:??你不能选择最好的,但最好的会来选择你——泰戈尔??
本文未经允许,不得转发!!!
目录
- ??一、进程终止
-
- ?1.1 正常终止
- ?1.2 异常终止
- ??二、孤儿进程、僵死进程
-
- ?2.1 孤儿进程
- ?2.2 僵死进程
- ??三、等待子进程结束 | wait、waitpid
-
- ?3.1 wait 函数
- ?3.2 waitpid 函数
- ??四、总结
![]()
??一、进程终止
?1.1 正常终止
进程有下面5种正常终止方式:
-
1、在main函数内执行return语句。这等效于调用exit。
在子函数中调用return不会导致进程退出,而调用exit会导致整个进程退出。#include <stdio.h> #include <stdlib.h> int child_fun() { //return 0; // 不导致进程退出 exit(0); // 会导致进程退出 } int main() { child_fun(); while(1) sleep(1); exit(0); // 等效于 return 0; } -
2、调用
exit 函数。此函数由ISO C定义,其操作包括调用各终止处理程序(终止处理程序在调用atexit 函数时登记),然后关闭所有标准IO流等。因为ISO C并不处理文件描述符、多进程(父、子进程)以及作业控制,所以这定义对UNIX系统而言是不完整的。#include <stdio.h> #include <stdlib.h> #include <unistd.h> void end_deal() { printf("atexit deal ... "); } int main() { printf("程序开始... "); printf("调用 atexit 注册 "); atexit(end_deal); printf("程序结束 "); exit(0); }
-
3、调用
_exit 或_Exit 函数。ISO C定义_Exit ,其目的是为进程提供一种无需运行终止处理程序或信号处理程序 而终止的方法。对标准IO流是否进行冲洗,这取决于实现。在UNIX系统中,_Exit 和_exit 是同义的,并不清洗标准I/O流。_exit 函数由exit调用,它处理UNIX特定的细节。_exit是由POSIX.1说明的。#include <stdio.h> #include <stdlib.h> #include <unistd.h> void end_deal() { printf("atexit deal ... "); } int main() { printf("程序开始... "); printf("调用 atexit 注册 "); atexit(end_deal); printf("程序结束 "); _exit(0); }_exit 退出进程时,不会调用atexit 注册的函数。

-
4、进程的最后一个线程在其启动例程中执行返回语句。但是,该线程的返回值不会用作进程的返回值。当最后一个线程从其启动例程返回时,该进程以终止状态0返回。
-
5、进程的最后一个线程调用pthread_exit函数。如同前面一样,在这种情况中,进程终止状态总是0,这与传送给pthread_exit的参数无关。
?1.2 异常终止
三种异常终止方式如下:
- 1、调用abort。它产生SIGABRT信号,这是下一种异常终止的一种特例
- 2、当进程接收到某些信号时。信号可由进程自身(例如调用abort函数)、其他进程或内核产生。例如,若进程越出其地址空间访问存储单元或者除以0,内核就会为该进程产生相应的信号。
- 3、最后一个线程对“取消”(cancellation)请求做出响应。按系统默认,“取消”以延迟方式发生:一个线程要求取消另一个线程,一段时间之后,目标线程终止。
不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。
终止进程时,我们希望能够通知其父进程它时如何终止的。对于三个终止函数((exit、_exit和_Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进程都能用
![]()
??二、孤儿进程、僵死进程
?2.1 孤儿进程
父进程先于子进程结束,子进程变成孤儿进程,该孤儿进程会被1号进程(init进程)领养
其操作过程大致如下:在一个进程终止时,内核逐个检查所有活动进程,以判断它是否是正要终止进程的子进程,如果是,则将该进程的父进程ID更改为1 (init进程的ID)。这种处理方法保证了每个进程都有一个父进程。
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("父进程%d开始运行,并创建子进程
", getpid());
pid_t pid = fork();
if(pid == 0)
{
printf("子进程%d被创建并开始运行,父进程是%d
",getpid(),getppid());
printf("子进程进入睡眠状态");
sleep(3);
printf("子进程睡醒后,发现父进程变为%d
",getppid());
return 0;
}
printf("父进程继续运行....
");
sleep(1);
printf("父进程执行结束
");
return 0;
}
运行结果如下:

?2.2 僵死进程
在UNIX术语中,一个已经终止、但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)的进程被称为
如果子进程先结束,会给父进程发信号,如果父进程没有收到该信号或没有及时处理,子进程将变成僵死进程,直到父进程处理了该信号或者父进程退出。
内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("%d 进程:我要调用fork()了...
", getpid());
pid_t pid = fork();
if(pid == 0)
{
printf("%d进程:我是%d的子进程,即将成为僵尸...
",getpid(),getppid());
return 0;//子进程结束,自动发信号
}
sleep(1);
printf("%d进程: 我是%d的父进程
", getpid(),pid);
getchar();
return 0;
}
运行结果打印:

打开另一个命令行窗口,查看子进程状态为僵死进程:

![]()
??三、等待子进程结束 | wait、waitpid
本节介绍两个函数(wait、waitpid),用于等待调用进程的子进程中的状态更改,并获取子进程状态更改的信息。
状态变化被认为是:①子进程被终止;②子进程被一个信号拦住了;③子进程被一个信号恢复了。
在终止子进程的情况下,执行wait函数允许系统释放与该子进程相关联的资源;如果不执行等待,则终止的子进程将保持“僵尸”状态。
下表是判断退出状态的宏
| 宏 | 解释 |
|---|---|
| WIFEXITED(status) | 若为正常终止子进程返回的状态,则为真。对于这种情况可执行WEXITSTATUS( status),取子进程传送给exit、_exit或_Exit参数的低8位 |
| WEXITSTATUS(status) | 可以获取退出码 |
| WIFSIGNALED(status ) | 若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)。对于这种情况,可执行 |
| WIFSTOPPED(siatus) | 若为当前暂停子进程的返回的状态,则为真。对于这种情况,可执行wSTOPSIG(status),取使子进程暂停的信号编号 |
| WIFCONTINUED(status) | 若在作业控制暂停后已经继续的子进程返回了状态,则为真。(POSIX.1的XSI扩展,仅用于waitpid。) |
?3.1 wait 函数
函数原型:
#include <sys/wait.h> pid_t wait(int *status);
- 功能:
wait 函数会使调用进程一直阻塞,直到它的一个子进程终止为止,它等效于:waitpid(-1, &status, 0); 。如果调用进程没有子进程则立即返回。 - 参数:status,是传出参数,用来带出结束子进程的退出码和退出状态;
- 返回值:成功返回终止的子进程的进程ID,失败返回
-1 。
看例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid == 0)
{
printf("子进程开始执行,即将睡眠...
");
sleep(3);
printf("子进程运行结束
");
exit(132);//该值不应该超过255
}
//父进程
printf("父进程开始执行,子进程的PID=%d
",pid);
printf("父进程等待子进程结束....
");
int status = 0;
/*如果子进程不退出,该函数一直阻塞*/
pid_t res = wait(&status);
printf("发现%d子进程结束", res);
printf("status = %d
", status);
/*如果子进程正常结束,该宏的操作结果为1,反之为0*/
if(WIFEXITED(status))
{
printf("子进程正常结束
");
printf("返回值为:%d
", WEXITSTATUS(status));
}
return 0;
}
运行结果:

?3.2 waitpid 函数
函数原型:
#include <sys/wait.h> pid_t waitpid(pid_t pid, int *status, int options);
-
功能:
waitpid 函数会使调用进程一直阻塞,直到指定的子进程终止为止。 -
参数:
-
pid:
<-1 :等待进程组ID等于pid绝对值 的任何子进程;
-1 :等待任意子进程,与wait 等效;
0 :等待进程组ID等于调用进程的进程组ID 的任何子进程;
>0 :等待进程ID等于pid值的子进程。 -
status:是传出参数,用来带出结束子进程的退出码和退出状态;
-
options:
0 :调用进程阻塞等待;
WNOHANG :如果没有子进程终止,则立即返回。
-
-
返回值:成功时,返回状态已更改的子进程的进程ID;如果指定了WNOHANG,并且pid指定的一个或多个子级存在,但尚未更改状态,则返回0。出现错误时,返回-1。
看例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
int pid1 = 0;
int pid2 = 0;
pid1 = fork();
if(pid1>0)
{
pid2 = fork();
}
if(pid1 == 0)//子进程一
{
printf("子进程一开始执行 PID=%d,睡3秒
",getpid());
sleep(3);
printf("子进程一结束
");
exit(55);
}
if(pid2 == 0)//子进程二
{
printf("子进程二开始执行 PID=%d,睡1秒
",getpid());
sleep(1);
printf("子进程二结束
");
exit(200);
}
//父进程
printf("父进程等待子进程一结束
");
int status = 0;
/*阻塞等待子进程一结束*/
waitpid(pid1, &status, 0);
if(WIFEXITED(status))
{
printf("子进程一正常结束,返回值:%d
",WEXITSTATUS(status));
}
printf("父进程结束
");
}
运行结果:

![]()
??四、总结
本文先介绍了进程终止的8个方式,然后介绍孤儿进程、僵死进程,最后介绍了父进程等待子进程的两个函数wait、waitpid。

如果文章有帮助的话,点赞??、收藏?,支持一波,谢谢 ??????

