wait函数

#include <sys/types.h>

#include <sys/wait.h>

 

pid_t wait(int *status);

 

僵尸进程。进程结束后放弃了几乎所有的内存空间,没有任何可以执行的代码,也不能被调度,仅仅在进程列表中保留着一个位置,记载着该进程的退出状态等信息供其他进程收集,除此之外僵尸进程不再占有任何内存空间。

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态,此时是一个僵尸进程。(正常终止→退出值;异常终止→终止信号)

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

在shell终端执行的命令和程序,都是shell通过fork产生的子进程来完成的,因此在命令和程序结束后,shell终端进程也可以获取子进程的结束状态,通过特殊命令$?查看,如果为0,表示正常结束,如果为非0值,表示出现了错误。Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:1.阻塞等待子进程退出(父进程调用该函数后,如果子进程没死,则父进程阻塞并等待子进程结束);2. 获取子进程结束状态(退出原因);3.回收子进程残留资源 (回收子进程结束后残留在内核中的PCB资源),然后彻底清除掉这个进程。

进程的一生。随着fork的成功执行,一个新的子进程产生,但此时它还只是父进程的克隆。然后随着exec函数族,新进程脱胎换骨,开始独立执行一个全新的程序,并完全替代了原有的父进程。进程结束,可以是自杀,也可以是他杀。自杀:遇到main函数的最后一个“}”、在main函数中使用return或者调用exit_exit;他杀:被其它进程通过另外一种方式杀掉。进程自杀(属于正常死亡),即使程序出错,但是进程自身结束了自己,可以通过return和exit赋给进程结束后一个值,通过wait或waitpid函数来获取该值,从而知道该进程自杀的原因,是否出错(一般0代表正常,非0代表出错),当然可以返回任何值,由wait函数可以获取到。进程如果属于他杀,则为异常结束,在linux操作系统中,所有的异常终止都是由于某一个信号导致的(通过kill –l参数可以查看所有的信号及信号编号),如在程序中给一个常量赋值,会发生段错误,此时进程会收到段错误相应的信号,从而被终止;除数为0时,进程也会收到一个信号(浮点数例外),从而被终止。而终止进程的信号会记录在终止信息中,供父进程调用wait函数获取。进程死掉后,会留下一具僵尸,wait和waitpid则去收集信息,清理尸体。

exit和_exit这两个函数(都是系统调用)都有一个整型形参,用于传递进程结束时的状态(正常结束还是出错结束)。一般,0表示正常结束,非0值(如1)表示出现了错误,非正常结束。可以利用wait系统调用接收子进程的返回值,从而针对不同的情况进行不同的处理。

 

pid_t wait(int *status);

status为传出参数,用于获取子进程结束时的状态(原因)。函数执行成功,则返回清理掉的子进程ID,如果失败则返回-1。进程一旦调用了wait,就立即阻塞自己,由wait自动分析该进程的某个子进程是否已经退出,如果找到了一个已经结束的子进程,则收集其信息,并将其清除掉并返回该子进程ID;如果没有找到这样的一个子进程,则会一直阻塞到那里,直到有一个出现为止。如果父进程没有子进程,则直接返回-1,表示出错。

如果对子进程如何死掉的不关心(不需要收集其残留的信息),只想把僵尸进程消灭掉,可以将函数形参设置为NULL,即pid=wait(NULL);

 

可使用wait函数传出参数status(整型值)来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

 1. WIFEXITED(status) 为非0   → 表示进程正常结束

WEXITSTATUS(status) 如上宏为真(进程正常结束),使用此宏 → 获取进程退出状态 (exit/_exit/return的参数)

 2. WIFSIGNALED(status) 为非0 → 表示进程异常终止

       WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。可以通过kill –l命令查看编号的详细含义。

3. WIFSTOPPED(status) 为非0 → 表示进程处于暂停状态(进程没有终止,只是暂停运行,如挂起或阻塞)。

WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

 

//代码举例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>int main(void)
{pid_t pid, wpid;int status;pid = fork( );if(pid == 0){printf("---child, my parent= %d, going to sleep 10s\n",getppid( ));sleep(10);printf("-------------child die--------------\n");exit(77); //return 77;  _exit(77);  //子进程正常结束,置其结束状态为77}else if(pid > 0){wpid = wait(&status);  //父进程阻塞等待子进程结束if(wpid==-1){perror("wait");exit(1); //置结束状态为1}if( WIFEXITED(status) )  //如果子进程正常结束{printf( "The child process exit with %d\n",WEXITSTATUS(status));}else if( WIFSIGNALED(status) )   //如果子进程异常结束{printf( "The child process was killed by %dth signal\n",WTERMSIG(status));}while (1){printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);sleep(1);}   //父进程一直运行}else{perror("fork");exit(1);}return 0;
}

[root@localhost wait]# ./wait_test

---child, my parent= 30770, going to sleep 10s

-------------child die--------------

The child process exit with 77   //获取了结束状态为77

I am parent, pid = 30770, myson = 30771

I am parent, pid = 30770, myson = 30771

I am parent, pid = 30770, myson = 30771

[root@localhost wait]#ps aux

root      30770  0.0  0.0   4164   356 pts/0    S+   18:11   0:00 ./wait_test

root      30791  0.0  0.0 123360  1380 pts/2    R+   18:12   0:00 ps aux

可见,只有父进程在一直运行,子进程不存在了,也不存在僵尸进程,子进程痕迹被完全清除。

 

在子进程运行期间,利用kill -9 将其杀掉,则为异常结束:

[root@localhost wait]#ps aux

[root@localhost wait]# kill 30830

[root@localhost wait]# ./wait_test

---child, my parent= 30829, going to sleep 60s

The child process was killed by 15th signal

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

I am parent, pid = 30829, myson = 30830

利用kill -l 命令可以查看编号为15的信号为     15) SIGTERM

 

总结,进程正常结束,父进程利用wait函数回收其结束状态,状态值通过return、exit和_exit返回(一般,如果程序正常则为0,出错设非0,但不是必须的),如果进程结束没有指明其值,如main函数中没有return语句且正常结束,则会有默认值(正常0,出错非0)。注意,returnexit_exit返回的值必须不超过128可通过wait函数获取。如果函数异常结束,wait函数回收其终止原因(信号编号)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385358.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

map与unordered_map

时间复杂度&#xff1a; mapunordered_mapOrderingincreasing orderno orderImplementationSelf balancing BSTHash Tablesearch timelog(n) O(1&#xff09;: 平均水ping O(n&#xff09;&#xff1a;最糟糕情况 Insertion timelog(n) RebalanceSame sa searchDelete timelo…

waitpid函数

#include <sys/wait.h> #include <sys/types.h> pid_t waitpid(pid_t pid, int *status, int options); 作用&#xff1a;同wait&#xff0c;但可指定pid进程清理&#xff0c;可以不阻塞。 waitpid函数的第二个参数int *status跟wait函数的形参一样&#xff0c;…

进程间通信的方法

Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进…

管道的概念(匿名管道)

管道是一种最基本的IPC机制&#xff0c;作用于有血缘关系的进程之间&#xff0c;完成数据传递。调用pipe系统函数即可创建一个管道。管道有如下特质&#xff1a;1.其本质是一个伪文件&#xff08;实为内核缓冲区&#xff09;&#xff0c;伪文件即不是真正的文件&#xff0c;不占…

1023. 组个最小数 (20)

给定数字0-9各若干个。你可以以任意顺序排列这些数字&#xff0c;但必须全部使用。目标是使得最后得到的数尽可能小&#xff08;注意0不能做首位&#xff09;。例如&#xff1a;给定两个0&#xff0c;两个1&#xff0c;三个5&#xff0c;一个8&#xff0c;我们得到的最小的数就…

pipe函数

#include <unistd.h> int pipe(int pipefd[2]); 作用&#xff1a;创建管道 成功&#xff1a;0&#xff1b;失败&#xff1a;-1&#xff0c;设置errno 函数调用成功返回r/w两个文件描述符。无需open&#xff0c;但需手动close。规定&#xff1a;fd[0] …

二分查找总结

二分查找法作为一种常见的查找方法&#xff0c;将原本是线性时间提升到了对数时间范围&#xff0c;大大缩短了搜索时间&#xff0c;具有很大的应用场景&#xff0c;而在LeetCode中&#xff0c;要运用二分搜索法来解的题目也有很多&#xff0c;但是实际上二分查找法的查找目标有…

管道的读写行为

使用管道需要注意以下4种特殊情况&#xff08;默认都是阻塞I/O操作&#xff0c;没有设置O_NONBLOCK标志&#xff09;&#xff1a; 1. 如果所有指向管道写端的文件描述符都关闭了&#xff08;管道写端引用计数为0&#xff09;&#xff0c;而仍然有进程从管道的读端读数据&#…

【C++ Primer | 08】课后习题答案

文章目录练习8.13练习8.13 include <iostream> #include <sstream> #include <fstream> #include <string> #include <vector> using namespace std;struct PersonInfo {string name;vector<string> phones; };bool valid(const string&…

管道缓冲区大小

可以使用ulimit –a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为&#xff1a;pipe size 4K&#xff0c;即一个页面大小。也可以使用fpathconf函数来查看&#xff1a; #include <unistd.h> long fpathconf(int fd, int name); 当需要查看管道的大…

FIFO(命名管道)

FIFO常被称为命名管道&#xff0c;以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO&#xff0c;不相关的进程也能交换数据。FIFO是Linux基础文件类型中的一种&#xff08;p,管道文件&#xff09;。但FIFO文件在磁盘上没有数据块&#xff0c;仅仅用来标…

文件进程间通信

使用文件也可以完成IPC&#xff0c;理论依据是&#xff0c;fork后&#xff0c;父子进程共享文件描述符。也就共享打开的文件。 //父子进程共享打开的文件。借助文件进行进程间通信&#xff08;可先打开文件&#xff0c;再创建子进程&#xff09; #include <unistd.h> #…

mmap内存映射、system V共享内存和Posix共享内存

linux内核支持多种共享内存方式&#xff0c;如mmap内存映射&#xff0c;Posix共享内存&#xff0c;以system V共享内存。当内核空间和用户空间存在大量数据交互时&#xff0c;共享内存映射就成了这种情况下的不二选择。它能够最大限度的降低内核空间和用户空间之间的数据拷贝&a…

mmap、munmap函数

#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); int munmap(void *addr, size_t length); void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); 返回&#xff1a;成功&…

mmap和munmap对文件进行操作(读写等)

//mmap、munmap函数的使用 #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h>void sys_err(char *str) {perror(str);exit(1); }…

1017. A除以B (20)

本题要求计算A/B&#xff0c;其中A是不超过1000位的正整数&#xff0c;B是1位正整数。你需要输出商数Q和余数R&#xff0c;使得A B * Q R成立。 输入格式&#xff1a; 输入在1行中依次给出A和B&#xff0c;中间以1空格分隔。 输出格式&#xff1a; 在1行中依次输出Q和R&#…

mmap父子进程间通信

父子等有血缘关系的进程之间也可以通过mmap建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数flags&#xff1a;MAP_PRIVATE&#xff1a;&#xff08;私有映射&#xff09;父子进程各自独占映射区&#xff1b;MAP_SHARED&#xff1a;&#xff08;…

匿名映射

通过使用我们发现&#xff0c;使用映射区来完成文件读写操作十分方便&#xff0c;父子进程间通信也较容易。但缺陷是&#xff0c;每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件&#xff0c;创建好了再unlink、close掉&#xff0c;比较麻烦。…

mmap无血缘关系进程间通信

实质上mmap是内核借助文件帮我们创建了一个映射区&#xff0c;多个进程之间利用该映射区完成数据传递。由于内核空间多进程共享&#xff0c;因此无血缘关系的进程间也可以使用mmap来完成通信。只要设置相应的标志位参数flags即可。若想实现共享&#xff0c;当然应该使用MAP_SHA…

【C++ Primer | 13】课后习题答案

文章目录13.1.4节目练习13.2节练习13.2.2练习13.1.4节目练习 练习13.14 #include <iostream> using namespace std;class numbered { private: static int seq; public:numbered() { mysn seq; }int mysn; };int numbered::seq 0;void f(numbered s) { cout <…