APUE读书笔记-08进程控制(3)

6、wait和waitpid

当一个进程结束的时候,无论是正常的还是非正常的,内核都会通过发送SIGCHLD信号来通知其父进程。因为child的终止是一个异步事件,所以它能够在父进程运行的任何时候发生,这个SIGCHLD信号是内核到父进程的异步通知。父进程可以选择忽略这个信号,或者提供一个函数来定义发生这个信号时候的处理动作。默认这个信号会被忽略。我们现在需要注意的是当我们调用wait或者waitpid的时候,调用的进程会:

  • 阻塞:如果进程的所有子进程正在运行的话。
  • 立即返回:这时候如果有一个子进程终止了,并且它等待自己的终止状态被获取,那么会同时返回这个子进程的终止状态。
  • 立即返回并且设置错误码:进程没有子进程的时候会出现这种情况。

如果我们由于收到了SIGCHLD信号而调用wait,那么我们可以期望wait函数会立即返回,但是如果我们在一个任意的时间调用wait,那么这会导致阻塞。

#include <sys/wait.h>
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);

两者在成功的时候都会返回进程ID,0或者在错误的时候返回1(其值一般为-1)。

两者的区别是:

  • wait函数会导致阻塞直到有一个子进程结束,waitpid有一个选项可以防止阻塞。
  • waitpid函数不是等待第一个结束的进程,它有一系列的选项可以指定等待哪些进程。

如果子进程已经结束并且成为了僵尸进程,那么wait函数会立即以那个子进程的状态返回。否则它会阻塞,直到一个子进程终止。 如果调用者阻塞了并且它有多个子进程,那么wait会在一个子进程终止的时候返回。我们是能够判断那个子进程结束的,因为wait函数的返回值就是结束进程的进程pid。

对于这两个函数,statloc参数是一个指向整数类型的参数,这个参数不是空,那么会返回结束进程的进程状态;如果我们不关心结束进程的状态,那么可以设置这个参数是空。

一般来说整数的状态值由实现来定义其含义。例如一些位代表正常的exit状态,另外一些位代表信号数(对于非正常退出的情况),有一个位代表是否生成core file.等等。POSIX.1指定退出状态可由<sys/wait.h>中定义的一些宏来进行查看。 四个互斥的宏可以告诉我们进程是如何结束的,它们都以WIF来开始,基于是从这四个宏中返回true的那个宏,其它的宏可以用来获取退出码,信号,以及其它的信息。这四个互斥的宏我们可以参见:前面的本章第5节所讲的进程exit中说明的四个宏。

我们在后面的作业控制中将会讨论如何stop进程。下面的例子就给出了如何使用这些宏:

#include <sys/wait.h>
void my_func()
{
...
        if (wait(&status) != pid)       /* wait for child */
        {
                ...
        }
    pr_exit(status);
}
void pr_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));
}

以前我们使用wait来等待一个指定的进程的时候,需要根据wait的返回来判断是否是目标pid,如果不是那么我们就把这个返回的pid保存起来然后继续wait,这样重复直到指定的进程结束;然后我们再次等待指定的进程结束之前需要首先遍历之前wait的时候保存的已经结束的pid列表。这很麻烦,而且缺点很多。现在我们可以利用POSIX.1中的waitpid来实现这个功能。

waitpid中pid参数的含义如下:

  • pid==1 :等待任何子进程,这时候waitpid和wait的含义是一样的。
  • pid>0 :等待pid值为参数pid的子进程。
  • pid==0 :等待进程组id和调用进程一样的任何子进程。
  • pid<0 :等待进程组id和参数pid的绝对值一样的任何子进程。

waitpid返回结束的子进程的pid并且将子进程结束状态存放在参数statloc所指的地址中。对于wait来说,出现错误的情况是调用进程没有子进程(也可能这个wait函数调用被信号所打断)。对于waitpid来说,可能出现错误的情况是指定的进程pid或者指定组id不存在,或者相应的进程pid不是调用进程的子进程。(这里的进程组在后面进程关系中有所会详细提及)

options参数可以额外控制watipid的行为,这个参数的值要么是0要么是以下这些值的按位或:

  • WCONTINUED: 如果实现支持作业控制,那么pid指定的任何子进程(这个子进程在stop之后被continue但是它的状态没有被报告)的状态会被返回。
  • WNOHANG: 如果指定的pid的子进程不是立即可用的,那么waitpid函数不会阻塞,这时候,返回值是0。
  • WUNTRACED: 如果实现支持作业控制,那么任何指定pid的子进程(这个子进程被stopped了,并且它的状态在它被stopped的时候没有被报告)的状态被返回。WIFSTOPPED宏可以确定返回值是否和一个停止的子进程相关。

另外,Solaris 支持一个额外的不是很标准的选项常量。WNOWAIT会使得系统进程(这个进程的终止状态通过waitpid被返回)进入一个wait的状态,这样这个进程可以再次被waited.

函数waitpid提供了三个wait没有的特性:

  • waitpid函数使我们等待一个特定的进程,而wait却返回任何终止进程的终止状态。
  • waitpid函数提供了一个非阻塞的wait版本,有时候我们想要获取一个子进程的状态,但是我们不需要阻塞。
  • waitpid函数提供了对作业控制的支持,这是通过WUNTRACED和WCONTINUED选项来做到的。

举例:

在前面说过如果子进程结束了,父进程没有对它进行wait,那么子进程会变成僵尸进程。如果我们不想wait子进程,也不想让子进程变成僵尸进程,那么我们有一个手段:在父进程中fork两次。

具体为调用fork产生子进程,子进程再调用fork产生孙进程,然后在孙子进程执行子进程想要的动作,而子进程仅仅是创建孙子进程之后就退出,父进程仅仅wait子进程,不管孙进程,因为孙进程最后父进程变成了init.大概如下:

if( fork() == 0)
{
        if(fork()> 0)
        {
                exit(0);
        }
        sleep(5);
        ...do child things...
}
wait(...);

译者注

原文参考

参考: APUE2/ch08lev1sec6.html

7、waitid

Single UNIX Specification 的XSI extension包含了一个额外的函数用来取回进程的退出状态。waitid函数和waitpid类似但是更为灵活。

#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

类似waitpid,waitid允许进程指定等待那个子进程。但是waitid不是把指定的信息(例如子进程id,id组)都集中到一个参数里而是利用两个参数来进行指定。参数id的解释方式取决于idtype的值。如下列出了idtype各种取值时候id的解释方式:

  • P_PID idtype取这个值的时候,id表示等待一个指定的子进程id。
  • P_PGID idtype取这个值的时候,表示等待任何在指定进程组的子进程,而id就包含了要等待的子进程的进程组id。
  • P_ALL idtype取这个值的时候,表示等待任何子进程,此时id参数被忽略。

options参数是一些标志的比特位,表示调用者关心哪些状态的变化。如下所示:

  • WCONTINUED 表示等待一个进程,这个进程之前被stopped了并且被continued了,并且它的status没有被报告。
  • WEXITED 表示等待已经exited的进程。
  • WNOHANG 表示如果没有可用的子进程退出状态,那么就立即返回而不是阻塞。
  • WNOWAIT 表示不会破坏子进程的退出状态码。子进程的退出状态可以在后来的wait,waitid,或者waitpid调用中取到.
  • WSTOPPED 表示等待一个被stopped的子进程,这个子进程的状态没有被报告。

参数infop是个结构指针。这个结构包含导致子进程变化的发生的信号的详细信息,后面会讨论这个结构。

在本书讨论的四个系统中,只有solaris支持这个waitid。

译者注

原文参考

参考: APUE2/ch08lev1sec7.html

8、wait3和wait4函数

大多数unix系统的实现提供了wait3和wait4函数,这两个函数本来是从Unix中的BSD分支中继承过来的。这两个函数提供的wait,waitid,waitpid所没有的特性是它多了一个额外的参数允许内核返回结束进程和它所有的子进程所消耗的资源统计。

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
pid_t wait3(int *statloc, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);

所谓资源信息,包含了诸如用户CPU时间数量的统计,系统CPU时间数量的统计,接受的信号数量,页相关的统计信息等等。可以参照geTRusage的man手册来获取更多的信息。
下面是5个wait函数的参数对比情况:

+---------------------------------------------------------------------------------------------------------+
| Function | pid | options | rusage | POSIX.1 | Free BSD 5.2.1  | Linux 2.4.22 | Mac OSX 10.3 | Solaris 9 |
|----------+-----+---------+--------+---------+-----------------+--------------+--------------+-----------|
| wait     |     |         |        |    •    |        •        |      •       |      •       |     •     |
|----------+-----+---------+--------+---------+-----------------+--------------+--------------+-----------|
| waitid   |  •  |    •    |        |   XSI   |                 |              |              |     •     |
|----------+-----+---------+--------+---------+-----------------+--------------+--------------+-----------|
| waitpid  |  •  |    •    |        |    •    |        •        |      •       |      •       |     •     |
|----------+-----+---------+--------+---------+-----------------+--------------+--------------+-----------|
| wait3    |     |    •    |   •    |         |        •        |      •       |      •       |     •     |
|----------+-----+---------+--------+---------+-----------------+--------------+--------------+-----------|
| wait4    |  •  |    •    |   •    |         |        •        |      •       |      •       |     •     |
+---------------------------------------------------------------------------------------------------------+

wait3函数是在早期的Single UNIX Specification版本中包含的,在第2个版本中wait3就被归类为遗留类的函数了在版本3中wait3就被去掉了。

译者注

原文参考

参考: APUE2/ch08lev1sec8.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352