雁过留声 发表于 2024-7-11 00:58:36

【Linux详解】进程等待 | 非阻塞轮询

引入:
为什么?是什么?怎么办
是什么?

进程等待是指父进程暂停自己的实行,直到某个特定的子进程结束或发生某些特定的事件。
为什么?



[*]僵尸进程刀枪不入,不可被杀死,存在内存泄漏
[*]获取进程的实行情况,知道我部署给子进程的任务,它完成的怎么样了–可选
怎么办

wait 函数
#include <sys/types.h>
#include <sys/wait.h>

pid
_t wait(int* status);
测试:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main (
    void
    )
{
    pid
_t id = fork();
    if (id == 0) {
      // child
      while (1) {
            printf("我是子进程,我正在运行... Pid: %d\n", getpid
());
            sleep(1);
      }
    }
    else {
      printf("我是父进程: pid
: %d,我将耐心地等待子进程!\n", getpid
());
      sleep(20);   // 为了便于观察,我们让父进程休眠20s

      // 苏醒后,父进程执行 wait,耐心地等待子进程
      pid
_t ret = wait(NULL);// 暂且将status参数设置为NULL
      if (ret < 0) {
            printf("等待失败!\n");
      }
      else {
            printf("等待成功!\n");   // 此时 Z → X
      }

      sleep(20);// 子进程退出后,再让父进程存在一段时间
    }
}
https://i-blog.csdnimg.cn/direct/1c7d9b44899a4996a74b0be61943a1bc.png
为什么是瓜代型,不是一个实行完?//相识循环的细节
在 fork 创建子进程后,父进程会首先实行并打印这条信息,其中 <父进程的pid
> 是父进程的进程ID。截不下就没截了


[*]父进程打印一次消息后休眠20秒:

[*]父进程仅在创建子进程后打印一次消息,然后休眠20秒等待观察子进程的运动,不再进行其他操纵。

通过这种方式,父进程和子进程可以或许并发地实行各自的任务,并且我们通过延迟父进程的退出能观察到子进程的连续运动。
整个程序实行流程如下:


[*] 父进程打印:
yamlCopy code
我是父进程: pid
: 1234,我将耐心地等待子进程!

[*] 子进程每秒打印一次:
yamlCopy code
我是子进程,我正在运行... Pid: 5678
(此循环连续到子进程被制止)
[*] 通过终端输入 kill 5678 制止子进程。
[*] 父进程20秒后苏醒并等待子进程结束后,打印:
textCopy code
等待成功!

之后,父进程再休眠20秒并继续存在一段时间退却出
waitpid


刚才讲的 wait 并不是主角,因为其功能比力简单,在进程等待时用的更多的是 waitpid

waitpid
可以把 wait 完全包含,wait 是 waitpid
的一个子功能。
https://i-blog.csdnimg.cn/direct/d9b6f5b7413c427fb62fa9726829b1c7.png
参数:


[*] pid
:要等待的子进程的进程ID。根据传入的值可以指定等待任何子进程、特定进程ID的子进程、任何同一进程组的子进程或者任何同一会话的子进程。

[*]假如 pid
大于零,waitpid
将等待指定进程ID的子进程结束。
[*]假如 pid
等于 -1,waitpid
将等待任意子进程结束,等同于 wait 函数。

[*] status:一个指向整型的指针,是一个输出型参数,它将用于存储子进程的制止状态。
[*] options
:用于指定等待行为的附加选项。

[*]传入0表现以默认行为等待子进程。(阻塞等待中再讲)

返回值:


[*]假如 waitpid
成功,返回值是已制止子进程的进程ID。
[*]假如出现错误,返回-1,并且会设置 errno 变量来指示具体错误缘故原由。
   Z状态,其本质上就是将自己的 task_struct 维护起来(代码可以释放,但是 task_struct 必须维护)。所谓的 wait/waitpid
的退出信息,实际上就是从子进程的 task_struct 中拿出来的,即 从子进程的 task_struct 中拿出子进程退出的退出码,拷贝到父进程中
https://i-blog.csdnimg.cn/direct/ab62a5538958436f922c32697ba79f43.png
status

该参数是一个 输出型参数 (即通过调用该函数,从函数内部拿出来特定的数据)。整数的低 16 位,其中又可以分为 最低八位 和 次低八位(具体细节看图):
https://img-blog.csdnimg.cn/2b3fa4a223264626854f93eda08b0595.png
1.次低八位:拿子进程退出码

#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/wait.h> int main (    void    ) {    pid
_t id = fork();    if (id == 0) {      int cnt = 5;   // 循环5次      // child      while (1) {            // 五秒之内运行状态            printf("我是子进程,我正在运行... Pid: %d\n", getpid
());            sleep(1);             // 五秒之后子进程制止            cnt--;            if (cnt == 0) {                break;             }      }         exit(233);   // 方便辨识,退出码我们设置为233,这是我们的预期结果    }    else {      printf("我是父进程: pid
: %d,我将耐心地等待子进程!\n", getpid
());                // ***** 使用waitpid
进行进程等待      int status = 0;// 接收 waitpid
的 status 参数         pid
_t ret = waitpid
(id, &status, 0);      if (ret > 0) {   // 等待成功            printf (                "等待成功,ret: %d, 我所等待的子进程退出码: %d\n",               ret,                (status>>8)&0xFF            );      }   }} status 并不是整体使用的,而是区域性使用的,我们要取其次低八位。我们可以用 位操纵 来完成,将 status右移八位再按位与上 0XFF,即 (status>>8)&0xFF ,就可以提取到 status 的次低八位了。
https://i-blog.csdnimg.cn/direct/ddc7c3ee9d3049c3aa490baa90e8dd61.png
waitpid
经过体系调用,来读取子进程的pcb(eg. task_st…),这是为什么呢
   操纵体系不相信任何人,父进程用户无法直接读取子进程的 pcb ,要通过体系调用的接口
https://i-blog.csdnimg.cn/direct/bc0c88b7d8ab428e8e88b004f52db666.png
2.初识 core dump(焦点转储)

https://i-blog.csdnimg.cn/direct/fd957c7c18fa481fa6770fb7f94e1546.png
它是操纵体系在进程收到某些信号而制止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。现在只需要知道,该信息是用于调试的。
3.最低七位:提取子进程的退出信号

刚才我们讲的 wait/waitpid
和次低八位的时侯,都是关于进程(exit) 的 正常退出。
假如进程 非常退出 呢?我们来模仿一下进程的非常退出。
页: [1]
查看完整版本: 【Linux详解】进程等待 | 非阻塞轮询