zoukankan      html  css  js  c++  java
  • 2.1 Linux中wait、system 分析

    wait与waitpid:

      当子进程退出的时候,内核会向父进程发送SIGCHID信号,子进程的退出是一个异步事件(子进程可以在父进程运行的任何时刻终止)。

      子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

      父进程查询子进程的退出状态可以用wait/waitpid函数。

    当我们用fork启动一个进程时,子进程就有了自己的生命,并将独立的运行,有时候,我们需要知道某个子进程是否已经结束了,可以通过wait安排父进程等待子进程结束。

    wait函数原型:

      pid_t wait(&status)

    status:该参数可以获得你等待子进程的信息

    执行成功就会返回子进程pid。

    wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。

    返回的是子进程的pid,它通常是结束的子进程。

    状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。

    如果status不是空指针,状态信息将被写入它指向的位置。

    wait获取status后检测处理:

      WIFEXITED(status)如果子进程正常结束,返回一个非零值

        WEXITSTATUS(status)如果WIFEXITED非零,返回子进程退出码

      WIFSIGNALED(status)  子进程因为捕获信号而终止,返回非零值

        WTERMSIG(status) 如果WIFSIGNALED非零,返回信号代码

      WIFSTOPPED(status) 如果子进程被暂停,返回一个非零值

        WSTOPSIG(status) 如果WIFSTOPPED非零,返回一个信号代码

    示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 
     8 #include <signal.h>
     9 #include <errno.h>
    10 
    11 #include <sys/stat.h>
    12 #include <fcntl.h>
    13 #include <sys/wait.h>
    14 
    15 int main(int argc, char *argv[])
    16 {
    17     pid_t pid;
    18     
    19     pid = fork();
    20     
    21     if(pid == -1)
    22     {
    23         perror("exit error");
    24         exit(-1);
    25     }
    26     
    27     if(pid == 0)
    28     {
    29         sleep(3);
    30         printf("this is child
    ");
    31         exit(100);
    32         
    33     }
    34     
    35     int ret = 0;
    36     printf("this is parent
    ");
    37     int status;
    38     ret = wait(&status);
    39     //ret = waitpid(pid, &status, 0);
    40     printf("ret = %d  pid = %d
    ", ret, pid);
    41     
    42     
    43     if(WIFEXITED(status))
    44     {
    45         printf("child exited normal exit status = %d
    ", WEXITSTATUS(status));
    46     }
    47     else if(WIFSIGNALED(status))
    48     {
    49         printf("child exited abnormal signal number = %d
    ", WTERMSIG(status));
    50     }
    51     else if(WIFSTOPPED(status))
    52     {
    53         printf("child stopped signal number = %d
    ", WSTOPSIG(status));
    54     }
    55     
    56     
    57     return 0;
    58 }

    子进程退出码为100,我们在父进程中将这个退出码检测出来,执行程序,结果如下:

    可见exit(100)是正常退出,而且检测出了退出码100。

    将31行的exit(100)改为abort(),再次执行程序,结果如下:

    可见abort()是异常退出,父进程中也是走的第二个分支,属于异常退出。

      wait函数执行成功的话返回被处理的进程的pid,失败返回-1,并设置errno, wait使主进程进入睡眠,但是在子进程死亡之前可能会被信号中断,这时候就会返回-1。

      如果我们有10个子进程,那么父进程怎么等待所有子进程全部结束呢?如果只用一个wait,那么只要一个子进程结束,父进程就会被唤醒,然后结束运行,这样的话其他的子进程还是会成为僵尸进程(如果在父进程结束前,子进程全死了的话,因为父进程的一个wait只会处理一个子进程,父进程结束的时候,如果还有活着的子进程,那么它们会挂到1号进程上,1号进程将来会给它们收尸)。

      那么我们是否可以通过一个循环来调用wait等待所有的子进程呢?

      示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 
     8 #include <signal.h>
     9 #include <errno.h>
    10 
    11 void TestFunc(int num)
    12 {
    13     printf("TestFunc : %d
    ", num);
    14 }
    15 
    16 int main(void)
    17 {
    18     int i = 0;
    19     int j = 0;
    20     
    21     int ret = 0;
    22     
    23     int ProcNum = 0;
    24     int LoopNum = 0;
    25     
    26     printf("please enter the ProcNum : 
    ");
    27     scanf("%d", &ProcNum);
    28     
    29     printf("please enter the LoopNum : 
    ");
    30     scanf("%d", &LoopNum);
    31     
    32     pid_t pid;
    33     
    34     for(i = 0; i < ProcNum; i++)
    35     {
    36         pid = fork();
    37         
    38         if(0 == pid)
    39         {
    40             for(j = 0; j < LoopNum; j++)
    41             {
    42                 TestFunc(j);
    43             }
    44             sleep(10);
    45             exit(0);
    46         }
    47     }
    48 
    49     while(1)
    50     {
    51         ret = wait(NULL);
    52         
    53         if(ret == -1)
    54         {
    55             if(errno == EINTR)
    56             {
    57                 continue;
    58             }
    59             
    60             break;
    61         }
    62     }    
    63         
    64     printf("parent process exit
    ");
    65     return 0;
    66 }

    49-62行通过一个循环等待所有子进程结束,wait有可能被其他信号中断,中断时返回-1,并设置errno,所以返回-1时我们进一步判断errno,如果是被中断了,那么我们继续wait,如果返回-1,但不是被信号中断的,那就说明所有子进程都已经结束了(当没有活着的子进程时,调用wait直接返回-1)。那么我们就可以跳出循环了。

       waitpid可以用来等待某个特定的进程,函数原型如下:

    pid_t waitpid(pid_t pid, int *status, int options)

      status如果不为空,会把状态信息写到它指向的位置

      options允许改变pid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

      如果执行成功,返回等待子进程的pid,失败返回-1

      第一个参数pid的解释:

        pid == -1,等待任一子进程,这样和wait等效

        pid > 0,等待特定的子进程

        pid == 0,等待组id等于调用进程组id的任一子进程,换句话说就是与调用进程同在一个组的进程

        pid < -1,等待其组id等于pid的绝对值的任一子进程

    wait和waitpid的区别和联系:

      在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞

      waitpid并不等待第一个终止的子进程,它有若干个选项,可以控制它等待特定的进程

      实际上wait函数是waitpid的一个特例

    示例程序如下:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 
     8 #include <signal.h>
     9 #include <errno.h>
    10 
    11 #include <sys/stat.h>
    12 #include <fcntl.h>
    13 #include <sys/wait.h>
    14 
    15 int main(int argc, char *argv[])
    16 {
    17     pid_t pid;
    18     
    19     pid = fork();
    20     
    21     if(pid == -1)
    22     {
    23         perror("exit error");
    24         exit(-1);
    25     }
    26     
    27     if(pid == 0)
    28     {
    29         sleep(3);
    30         printf("this is child
    ");
    31         //exit(100);
    32         abort();
    33     }
    34     
    35     int ret = 0;
    36     printf("this is parent
    ");
    37     int status;
    38     //ret = wait(&status);
    39     ret = waitpid(pid, &status, 0);
    40     printf("ret = %d  pid = %d
    ", ret, pid);
    41     
    42     
    43     if(WIFEXITED(status))
    44     {
    45         printf("child exited normal exit status = %d
    ", WEXITSTATUS(status));
    46     }
    47     else if(WIFSIGNALED(status))
    48     {
    49         printf("child exited abnormal signal number = %d
    ", WTERMSIG(status));
    50     }
    51     else if(WIFSTOPPED(status))
    52     {
    53         printf("child stopped signal number = %d
    ", WSTOPSIG(status));
    54     }
    55     
    56     
    57     return 0;
    58 }

    38行的wait改成39行的waitpid,执行结果如下:

    system C库函数:

      system()函数调用“bin/sh -c command”执行特定的命令,阻塞当前进程,直到command执行完毕。原型如下:

      int system(const char* command)

      返回值:

      如果无法启动shell运行命令,system返回127,出现不能执行system调用的其他错误时返回-1,如果system顺利执行,返回那个命令的退出码。

      system函数执行时,会调用fork、execve、waitpid等函数。

    我们自己编写一个my_system函数,功能和system相同,如下所示:

     1 #include <sys/types.h>
     2 #include <unistd.h>
     3 
     4 #include <stdlib.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 
     8 #include <signal.h>
     9 #include <errno.h>
    10 
    11 #include <sys/stat.h>
    12 #include <fcntl.h>
    13 #include <sys/wait.h>
    14 
    15 int my_system(char *command)
    16 {
    17     pid_t pid;
    18     int status;
    19     
    20     if(command == NULL)
    21     {
    22         return 1;
    23     }
    24     
    25     pid = fork();
    26     
    27     if(pid < 0)
    28     {
    29         status = -1;
    30     }
    31     else if(pid == 0)
    32     {
    33         execl("/bin/sh", "sh", "-c", command, NULL);
    34         exit(127);
    35     }
    36     else
    37     {
    38         while(waitpid(pid, &status, 0) < 0)
    39         {
    40             if(errno == EINTR)
    41             {
    42                 continue;
    43             }
    44             
    45             status = -1;
    46             break;
    47             
    48         }
    49     }
    50     
    51     return status;
    52 }
    53 
    54 int main()
    55 {
    56     my_system("ls -l");
    57         
    58     return 0;    
    59 }

    执行结果如下:

      第38-48的while循环表示等待子进程结束,并获取子进程结束状态。

    可以看到 ls -l 命令成功执行了。 sh -c其中的-c表示执行系统命令,用sh执行一个shell脚本时不用加-c。我们也可以在命令行直接使用sh -c执行一个命令,如下所示:

  • 相关阅读:
    AutomaticallyProfile 自动化引擎 MyBatis和DB沟通的引擎 (根据数据库信息自动给生成实体类那些...)
    经典aop,
    IOC和DI区别,aop的第一个案例,注入方式(7种),aop的7个专业术语,注解的DI,代理(动态代理,静态代理)
    AOP(AOP概念,AOP专业术语,单例模式,bean的id和name属性,基于xml的DI, 构造注入,命名空间p注入,集合属性注入, List 配置文件)
    ajax
    spring基础
    一对多,多对一,自关联,多对多,一级缓存,二级缓存
    hql语法
    sql操作语言
    Oracle函数
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9368476.html
Copyright © 2011-2022 走看看