wait()函数的详细分析
之前一直没太深入的去理解wait()函数,今天机缘巧合之前又看了看,发现之前没有真正的理解该函数。
众所周知,wait()函数一般用在父进程中等待回收子进程的资源,而防止僵尸进程的产生。
(In UNIX System terminology, a process that has terminated, but whose parent has not yet waited for it, is called a zombie. )
下面我就带着问题,层层深入地来分析这个函数。
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。
因为子进程终止是个异步事件(这可以在父进程运行的任何是否发生),所以这种信号也是内核向父进程发的异步通知。
父进程可以选择忽略该信号,或者提供一个信号处理程序。
对于这种信号的系统默认动作是忽略。
调用wait()或waitpid()的父进程会发生什么情况:
a. 如果其所有子进程都还在运行,则阻塞;
Q1:如果是一部分子进程终止,而另一部分还在运行,那么父进程还会阻塞吗?
不会,只要有一个进程终止,wait就会返回。也就是说只要wait接收到一个SIGCHLD信号,wait()就会返回。对于两个或多个子进程的情况,需要调用wait两次或多次
b. 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;
c. 如果它没有任何子进程,则立即出错返回;
int wait(int* statloc);
int waitpid(pid_t pid, int* statloc, int options);
这两个函数的区别如下:
1. 在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞;
2. waitpid()并不等待在其调用之后的第一个终止的子进程,它有若干个选项,可以控制它所等待的进程;
如果一个子进程已经终止,并且是一个僵尸进程,则wait立即返回并取得该子进程的终止状态,否则wait使其调用者阻塞直到一个子进程终止。
如果调用者阻塞而且它有多个子进程,则在其一个子进程终止时,wait就立即返回。
因为wait的返回值是终止进程的进程ID,所以父进程总能知道哪一个子进程终止了。
参数statloc如果不是一个空指针,则终止进程的终止状态就存放在statloc所指向的单元。
参数statloc如果是一个空指针,则表示父进程不关心子进程的终止状态。
Q2:statloc中不同的值具体表示什么含义呢?
wait的输出参数statloc中,某些位表示退出状态(正常返回),其它位则指示信号编号(异常返回),有一位指示是否产生了一个core文件等等。
有四个互斥的宏可用来取得进程终止的原因。
1. WIFEXITED(status):若为正常终止子进程返回的状态,则为真。WEXITSTATUS(status)可取得子进程传送给exit、_exit或_Exit参数的低8位;
2. WIFSIGNALED(status):若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号,如SIGCHLD)。对于这种情况,可执行WTERMSIG(status),取得是子进程终止的信号的编号。
另外,有些实现(非Single UNIX Specification)定义宏WCOREDUMP(status),若已产生终止进程的core文件,则它返回真。
3. WIFSTOPPED(status):若为当前暂停的子进程的返回的状态,则为真。对于这种情况,可执行WSTOPSIG(status),取使子进程暂停的信号编号。
4. WIFCONTINUED(status):若在作业控制暂停后已经继续的子进程返回了状态,则为真.(POSIX.1的XSI扩展,仅用于waitpid)
Q3: wait函数和SIGCHLD信号的关系?两者之间的关系,需要分成三个问题
已知系统默认是忽略SIGCHLD信号,在一个进程终止或停止时,会将SIGCHLD信号发送给其父进程。
已知父进程若不调用wait()获取子进程的终止状态,那么子进程就会变成僵尸进程。
Q3.1:wait()是关于是否产生僵尸进程的问题。
Q3.2:SIGCHLD信号是关于自己本身的处理方式的选择问题。
当这两个问题(Q3.1&Q3.2)结合在一起应用时,就产生了另外一个问题,父进程是同步还是异步的问题(或者描述为阻塞还是非阻塞问题)
当SIGCHLD的处理方式是系统默认时,父进程调用了wait()以防止子进程变成僵尸进程,那么父进程必须等待子进程结束之后才能执行wait()之后的流程,即同步问题。
当SIGCHLD的处理方式是捕获时,在其信号处理程序中调用wait()函数,就能获取子进程的终止状态而不产生僵尸进程同时父进程并不会阻塞,做自己想做的事,即异步问题。
Q3.3:wait什么时候返回的问题,wait()的返回和SIGCHLD有什么关系?
根据wait()的描述:
All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose
state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a
signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is
not performed, then terminated the child remains in a "zombie" state (see NOTES below).
If a child has already changed state, then these calls return immediately. Otherwise they block until either a child changes state or a signal
handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of sigaction(2)). In the
remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these system calls is termed waitable.
当子进程的状态发生改变时,wait()返回;
当调用wait()的进程接收到一个被设置为SA_INTERRUPT的信号时,wait()返回;
因为SIGCHLD信号的产生必然是伴随着子进程状态的改变,所以当有SIGCHLD信号发生时,wait会返回。
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/wait.h>
- #include <errno.h>
- #include <signal.h>
- void print_exit(int status)
- {
- if (WIFEXITED(status))
- printf("normal termination, exit status = %d\n", WEXITSTATUS(status));
- else if (WIFSIGNALED(status))
- printf("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
- #ifdef WCOREDUMP
- WCOREDUMP(status) ? ("core file generated") : (""));
- #else
- "");
- #endif
- else if (WIFSTOPPED(status))
- printf("child stopped, signal number=%d\n", WSTOPSIG(status));
- }
- void sig_child(int signo)
- {
- int status;
- int ret;
- ret = wait(&status);
- printf("pid:%d, res:%d, status=%d, %s\n", getpid(), ret, status, strerror(errno));
- print_exit(status);
- }
- void sig_usr(int signo)
- {
- if (signo == SIGUSR1)
- printf("received SIGUSR1\n");
- else if (signo == SIGUSR2)
- printf("received SIGUSR2\n");
- else
- printf("received signal %d\n", signo);
- }
- int main(int argc, char** argv)
- {
- pid_t pid;
- int status;
- int ret;
- int remaintime=;
- struct sigaction act, oact;
- sigset_t oldmask;
- //signal(SIGCHLD, sig_child);
- //signal(SIGUSR1, sig_usr);
- act.sa_handler = sig_usr;
- sigemptyset(&act.sa_mask);
- act.sa_flags = |SA_INTERRUPT;
- sigaction(SIGUSR1, &act, &oact);
- if ((pid=fork()) < )
- {
- printf("fork error\n");
- return -;
- }
- else if (pid == )
- {
- printf("child:pid:%d\n", getpid());
- remaintime = sleep();
- printf("remiantime=%d\n", remaintime);
- //exit(0);
- //return 0;
- //
- //sleep(30);//SIGQUIT
- }
- else
- {
- printf("father:pid:%d\n", getpid());
- //while(1)
- //{
- // sleep(1);
- // printf("1111\n");
- //}
- ret = wait(&status);
- printf("res:%d, status=%d, %s\n", ret, status, strerror(errno));
- print_exit(status);
- }
- return ;
- }
wait()函数的详细分析的更多相关文章
- LinkedList详细分析
一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...
- C语言中的static 详细分析
转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...
- lk启动流程详细分析
转载请注明来源:cuixiaolei的技术博客 这篇文章是lk启动流程分析(以高通为例),将会详细介绍下面的内容: 1).正常开机引导流程 2).recovery引导流程 3).fastboot引导流 ...
- Android-Native-Server 启动和注册详细分析
Android-Native-Server 启动和注册详细分析 以mediaService为实例来讲解: mediaService的启动入口 是一个 传统的 main()函数 源码位置E:\ ...
- Http Pipeline详细分析(下)
Http Pipeline详细分析(下) 文章内容 接上面的章节,我们这篇要讲解的是Pipeline是执行的各种事件,我们知道,在自定义的HttpModule的Init方法里,我们可以添加自己的事件, ...
- HashMap 源码详细分析(JDK1.8)
一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...
- 海思uboot启动流程详细分析(三)【转】
1. 前言 书接上文(u-boot启动流程分析(二)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_init_r所代表的. ...
- scrapy爬虫具体案例步骤详细分析
scrapy爬虫具体案例详细分析 scrapy,它是一个整合了的爬虫框架, 有着非常健全的管理系统. 而且它也是分布式爬虫, 它的管理体系非常复杂. 但是特别高效.用途广泛,主要用于数据挖掘.检测以及 ...
- 详细分析MySQL事务日志(redo log和undo log)
innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...
随机推荐
- Spring-boot(二)yml文件的使用
上一章创建了一个简单的springboot项目,配置可以说非常的简单. 不过,在实际开发中不可能都用默认的配置,还是需要根据自己的实际项目需求有自定义的配置的. 比如:端口号需要变更,模板引擎的缓存开 ...
- DBNull与Null的区别
Null是.net中无效的对象引用. DBNull是一个类.DBNull.Value是它唯一的实例.它指数据库中数据为空(<NULL>)时,在.net中的值. null表示一个对象的指向无 ...
- SaaS产品成功学
『精益』和『敏捷』之类的方法论在产品实现方面消除了不少浪费,但面对SaaS产品,这些却并没有像银弹般有效. 国外的『Ramen』团队模仿Maslow的需求层次理论提出了SaaS产品的需求层次理论,可以 ...
- (原)阅读Android-Camera2Video的demo源码和调试心得
转载请注明出处:http://www.cnblogs.com/lihaiping/p/6142512.html 最近因为项目需要使用到camera的功能,所以针对官方的demo源码进行一番阅读,并 ...
- react 生命周期钩子里不要写逻辑,否则不生效
react 生命周期钩子里不要写逻辑,否则不生效,要把逻辑写在函数里,然后在钩子里调用函数,否则会出现问题.
- 如何把大段文字转为带html标签的文字
开发网页的时候,有时候会遇到大段的隐私声明,用户协议等等,我们呀要复制粘贴展示出来,必须加大量的p标签,h1,h2,空格符,br标签,这对我们来说无疑是泪崩的,有个很好的办法,可以快速给这些文字加标签 ...
- 使用Python管理压缩包
一. 使用tarfile库读取与创建tar包 1. 创建tar包 In [1]: import tarfile In [2]: with tarfile.open('demo.tar',mode='w ...
- Redis JdkSerializationRedisSerializer,stringRedisSerializer,ProtoBuf 体积,性能简单比较.
/** * User: laizhenwei * Date: 2018-04-10 Time: 14:17 * Description: */ @RunWith(SpringRunner.class) ...
- SQL Server2008 删除登录记录
SQL Server Management Studio登陆窗口 清空这些多余的登陆名 主要是删除SqlStudio.bin文件 WinXP: C:\Documents and Set ...
- [原]openstack-kilo--issue(十七) heat创建网络Quota exceeded for resources OverQuotaClient: resources.dmz_protected_network_sub
----- 1 ------- 在使用heat创建网络的时候,报错如下 INFO heat.engine.stack [-] Stack CREATE FAILED (mmsc_network_s ...