Linux(编程-进程基础):09:---进程等待(wait、waitpid、waitid、wait3、wait4)

wait、waitpid的作用:

  • 当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知。
  • 父进程可以忽略该信号,或者提供一个该信号发生时即被调用执行的函数 (信号处理程序)。对于这种信号的系统默认动作是忽略它
  • 当父进程接收到子进程发来的SIGCHLD信号时,可调用wait、waitpid函数来接受子进程的终止状态

终止状态判断宏

  • 概念:wait()、waitpid()的参数可以保存子进程的终止状态,#include<sys/wait.h>提供了4个互斥的宏,把终止状态当做宏参数,哪一个宏的值为真,就可选用其他宏来取得退出状态、信号编号等
WIFEXITED(status)

若为正常终止子进程返回的状态,则为真

对于这种情况可执行WEXITSTATUS(status)取子进程传送给exit、_exit、_Exit参数的低8位

WIFSIGNALED(status)

若为异常终止子进程返回的状态,则为真(接到一个不捕捉的信号)

对于 这种情况,可执行WTERMSIG(status) 取使子进程终止的信号编号

另外,有些实现(非Signle UNIX Specification)定义宏WCOREDUMP(status),若已产生终止进程的core文件,则它返回真

WIFSTOPPED(status)

若为当前暂停子进程的返回的状态,则为真

对于这种情况,可执行WSTOPSIG(status)获取使子进程暂停的信号编号

WIFCONTINUED(status) 若在作业控制暂停后已经继续的子进程返回了状态,则为真(POSIX.1的XSI扩展;仅用于waitpid)

一、wait()

#include <sys/wait.h>
pid_t wait(int *statloc);
//返回:成功返回子进程ID;出错,返回0/-1

1.wait的参数与返回值

参数:

  • 一个int值,用来接受子进程的终止状态

返回值:

  • 返回终止子进程的进程ID

注意事项:

  • wait()如果不关心子进程的终止状态,参数可以设置为NULL
  • 因为返回值为子进程的进程ID,所以父进程就可以根据这个返回值来判断是哪个子进程终止的

2.wait的特点

  • 如果一个进程没有任何子进程,若该进程调用wait(),则wait()立即出错返回
  • wait()调用被一个信号中断时,也可能返回另一种出错
  • 如果父进程的子进程都还在运行时调用wait(),则父进程就处于阻塞,等待子进程终止并返回一个终止状态
  • 如果父进程调用wait(),当任意一个子进程终止时,wait()就获得该子进程的终止状态立即返回

3.演示案例

  • wait()获取子进程终止状态,然后使用pr_exit打印终止状态的类型
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
void pr_exit(int exit)
{
    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));
}

int main(void)
{
    pid_t pid;
    int status;

    if((pid=fork()<0){
        perror("fork error");
    }
    else if(pid==0)
        exit(7);
    if(wait(&status)!=pid)
        perror("wait error");
    pr_exit(status);

    if((pid=fork()<0){
            perror("fork error");
        }
    else if(pid==0)
        abort();
    if(wait(&status)!=pid)
        perror("wait error");
    pr_exit(status);

    if((pid=fork()<0){
        perror("fork error");
     }
    else if(pid==0)
        status /=0;
    if(wait(&status)!=pid)
        perror("wait error");
    pr_exit(status);

    return 0;
}
  • 运行结果:

二、waitpid()

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);
//返回:成功返回子进程ID;出错,返回0/-1

1.与wait()的不同之处

  • 父进程调用wait(),当任意一个子进程终止时,wait()就获得该子进程的终止状态立即返回。因此用来等待指定的子进程。早起的UNIX必须调用wait(),然后将其返回的进程ID和所期望的进程ID相比较,直至等待到特定的进程时才获取相关信息,比较麻烦
  • waitpid()提供等待指定子进程的功能

2.参数

参数1:

  • pid== -1:等待任一子进程。于是在这一功能方面waitpid与wait等效
  • pid>0:等待其进程ID与pid相等的子进程
  • pid==0:等待其组ID等于调用进程的组ID的任一子进程
  • pi <-1:等待其组ID等于pid 的绝对值的任一子进程

参数2:

  • 存放子进程的终止状态

参数3:

  • options参数使我们能进一步控制waitpid的操作。此参数或者是0,或者是下表中常数的按位或运算的结果
WCONTINUED 若实现支持作业控制,那么由pid指定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态(POSIX.1的XSI扩展)
WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0
WUNTRACED 若某实现支持作业控制,而由pid指定的任一子进程已处于停止状态,并且其状态自停止依赖还未报告过,则返回其状态,WIFSTOPPED宏确定返回值是否对应于一个停止的子进程

3.特点

  • 对于waitpid,如果指定的进程或进程组不存在,或者参数pid指定的进程不是调用进程的子进程,都可能出错
  • 相对于wiat(),waitpid提供了一个wait的非阻塞版本,有时候希望获取一个子进程的状态,但不想阻塞
  • waitpid通过WUNTRACED、WCONTINUED选项支持作业控制

4.演示案例

  • 代码注释:我们不想等待子进程终止,也不希望第一个子进程处于僵死状态直到父进程终止,实现这一要求的诀窍是调用fork两次
  • 第二个子进程调用sleep以保证在打印父进程ID时,第一个子进程已终止
  • 在fork之后,父、子进程都可以继续执行,并且无法预知哪一个会先执行。在fork之后,如果不使第2个子进程休眠,那么它可能比其父进程先执行,于是它先打印的父进程ID将是创建它的父进程,而不是init进程
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/wait.h>
int main(void)
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork error");
    } 
    else if (pid == 0) /* first child */
    { 
        if ((pid = fork()) < 0)
            perror("fork error");
        else if (pid > 0)
            exit(0);

        sleep(2);
        printf("second child, parent pid = %ld\n", (long)getppid());
        exit(0);
    }
    if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
        perror("waitpid error");

    exit(0);
}

运行结果:

  • 当原先的进程(也就是exec本程序的进程)终止时,shell打印其命令行指示符:因为主进程通过waitpid等待的是第一个子进程,第一个子进程比第二个父进程先结束,所以当第一个子进程结束时,父进程通过waitpid接收到终止状态也接着结束了。所以程序结束先打印命令行提示符(因为主进程结束了)
  • 当打印完命令行提示符之后:后面的语句再打印出来,因为第二个子进程被init接管了,最后执行打印,并且会让你再按一下回车结束

三、waitid()

#include <sys/wait.h>
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
//返回值:成功返回0;出错,返回-1

1.功能

  • 与waitpid相似,waitid允许一个进程指定要等待的子进程,但它使用两个单独的参数表示要等待的子进程所属的类型,而不是将此与进程ID或进程组ID组合成一个参数。
  • id参数的作用域idtype的值相关

2.参数

参数1:

P_PID 等待一特定进程,id包含要等待子进程的进程ID
P_PGID 等待一特定进程组的任一子进程;id包含要等待子进程的进程组ID
P_ALL 等待任一子进程;忽略id
  • 参数2:
  • 参数3:指向siginfo结构的指针。该结构包含了造成子进程状态改变有关信号的详细信息

参数4:

  • 下面各标志的按位或运算,指示调用者关注哪些状态变化
  • WCONTINUED、WEXITED、WSTOPPED这3个常量之一必须在options参数中指定
WCONTINUED 等待一进程,它以前曾被停止,此后又已继续,但其状态尚未报告
WEXITED 等待已退出的进程
WNOHANG 如无可用的子进程退出状态,立即返回而非阻塞
WNOWAIT 不破坏子进程退出状态。该子进程退出状态可由后续的wait、waitid、waitpid调用取得
WSTOPPED 等待一进程,它已经停止,但其状态尚未报告

3.注意事项

  • Linux 3.2.0、Mac OS X 10.6.8、Solaris 10支持waitid。Mac OS X 10.6.8并没有设置siginfo结构中的所有信息

四、wait3()、wait4()

#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);

//返回值:成功返回进程ID。出错,返回-1

1.功能

  • 大多数UNIX系统提供了另外两个函数wait3和wait4。这两个函数是从UNIX系统的BSD分支延袭下来的。它们提供的功能比POSIX.函数wait和waitpid、waitod所提供的分别要多一个,这与附加参数rusage有关。该参数要求内核返回由终止进程及其所有子进程使用的资源概况
  • 资源信息包括用户CPU时间总量、系统CPU时间总量、缺页次数、接收到信号的次数等

2.注意事项

  • 有关细节请参阅getrusage(2)手册页。这些资源信息只包括终止子进程,并不包括处于停止状态的子进程(这种资源信息与7.11节中所述的资源限制不同)

3.列出了各个wait函数所支 持的不同的参数

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/88992605