当前位置: 首页 > article >正文

Linux 进程终止 进程等待

目录

进程终止

退出码

错误码

代码异常终止(信号详解)

exit

_exit

进程等待

概念

等待的原因

wait

函数原型

参数

返回值

 监控脚本

waitpid

概念

函数原型

 参数

返回值

WIFEXITED(status)

WEXITSTATUS(status)

问题

为什么不用全局变量获得子进程的退出信息?

父进程如何得知子进程的退出信息?

模拟非阻塞等待轮询逻辑

重点


进程终止

退出码

  • 退出码是程序在结束时返回给操作系统的一个状态码,表示程序执行的最终结果,退出码通常用于告知操作系统或调用进程该程序是否成功执行
  • echo $?:Shell自动维护的特殊变量,可以打印出最后一个子进程执行完毕后的退出码(echo是内建命令)

错误码

for(int i = 0; i < 134; i++)
    printf("%d: %s\n", i, strerror(i));
  •  errno是一个全局变量(通过errno.h引入),用于存储最近一次系统调用库函数执行失败时的错误代码,错误时会将错误码设置到errno中
  • 可以通过strerror将错误码转换为错误描述

代码异常终止(信号详解)

  • 出异常时不看错误码,因为异常机制本身已经传递了足够的信息来描述错误情况
  • 程序崩溃:进程调度中出现异常,异常信息会被OS检测到,OS通过发送信号的方式杀掉、终止(释放)对应的进程
  • 异常通常是程序员自己写出来的
  • 一个进程是否出现异常,看有无收到信号
  • 父进程通过  信号数字  和  退出码  来判断子进程任务完成的怎样

exit

  • exit(int status):status 进程的退出码
  • 等价于在main函数中直接return,任意地点调exit,表示进程退出,后序代码不执行,直接终止进程
  • exit时候,会将冲刷缓冲区
  • exit 函数的底层封装了 _exit 系统调用接口

_exit

  • 是一个系统调用接口,直接与操作系统内核交互
  • _exit时候,不会冲刷缓冲区,内核之上是系统调用接口,说明缓冲区(C语言提供的)不在内核中
  • ★是一个底层的、直接与内核交互的系统调用,它的目标是立即终止进程,而不是进行任何用户空间的清理工作

进程等待

概念

  • 通过wait/waitpid,让父进程对子进程进行资源回收的等待过程

等待的原因

  • 解决子进程的僵尸问题带来的内存泄露
  • 子进程要将父进程给的任务的完成结果(即,子进程的退出信息->进程的退出码,信号编号)返回给父进程
  • 简:OS回收子进程资源获得进程的退出信息

wait

函数原型

#include <sys/wait.h>

pid_t wait(int *status);

参数

  • 输出型参数
  • status:用于存储子进程的退出状态。父进程可以通过此参数判断子进程是正常退出还是因异常终止
  • 如果异常了,退出码就没用了;实测:子进程中途异常,exit code 为0

返回值

  • 成功时返回已终止子进程的进程 ID(pid_t 类型)
  • 如果没有子进程或调用失败,返回 -1,并设置 errno
void worker()
{
    int cnt = 3;
    while(cnt--)
    {
        printf("child process, pid:%d, ppid%d\n", getpid(), getppid());
        sleep(1);
    }
}

void creatSubProcess()
{
    pid_t id = fork();
    if(id == 0) 
    {
        worker();
        exit(0);
    }
    else
    {    
        sleep(6);    
        pid_t rid = wait(NULL);
    
        if(rid == id)    
        {                                                                                    
            printf("wait success:pid: %d, rid: %d\n", getpid(), rid);    
        }    
        sleep(3);    
    }   
}

 监控脚本

  • 父进程结束的时候,会自动回收子进程,但是也可以在父进程运行中途回收
  • pid_t wait(int *status); status:指向整数的指针,用于存储子进程的退出状态信息
  • wait是一个系统调用接口;声明,相关的宏和数据类型在 sys/wait.h
  • wait能够回收处于僵尸状态的子进程;如果执行到父进程执行到wait这行代码时,子进程没退出,父进程会在wait这行代码上阻塞等待,知道子进程运行完毕,wait会对其回收;是一种等待软件资源的阻塞,一个进程 等待另一个进程执行完成
  • 僵尸进程无法被信号杀掉,只能通过父进程回收

waitpid

概念

  • waitpid 是一种系统调用,使父进程可以等待特定子进程的终止,或按指定的条件等待所有子进程终止,waitpid 通过 pid 参数指定要等待的进程,提供了灵活的子进程管理方式

函数原型

#include <sys/wait.h>

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

 参数

  • pid:指定要等待的子进程
    • pid > 0:等待进程 ID 等于 pid 的子进程。
    • pid = 0:等待与调用进程在同一进程组的任一子进程
    • pid = -1:等待任一子进程(等价于 wait)
    • pid < -1:等待进程组 ID 等于 |pid| 的任一子进程

  • status:用于存储子进程的退出状态
  • options:控制等待行为
    • 0:阻塞等待子进程退出
    • WNOHANG非阻塞模式如果没有已退出的子进程,立即返回
    • WUNTRACED:如果子进程暂停(收到 SIGSTOP 等信号),则返回该子进程状态。
    • WCONTINUED:如果子进程被恢复运行(收到 SIGCONT 信号),则返回该子进程状态。

返回值

  • 成功时返回已终止的子进程的 pid。
  • 如果 WNOHANG 选项被设置,且没有已退出的子进程,返回 0。
  • 出错时返回 -1,并设置 errno

WIFEXITED(status)

  • #define WIFEXITED(status)   (((status) & 0x7F) == 0)
  • W:wait  IF:是否  EXITED:退出
  • 一个宏,用于检查一个子进程是否正常退出,返回非零值时,表示子进程通过 exitreturn 语句正常退出

WEXITSTATUS(status)

  • #define WEXITSTATUS(status)   (((status) >> 8) & 0xFF)
  • W:wait  EXIT:退出  STATUS:状态  
  • wait/waitpid的方式来等待的子进程的退出状态    其由退出码呈现
  • 用于获取子进程的退出状态码,只有在 WIFEXITED(status) 返回非零时(其实就是1),才能使用 WEXITSTATUS(status) 宏来获取子进程的退出码

问题

为什么不用全局变量获得子进程的退出信息?

  • 写实拷贝...进程间是相互独立的父进程无法直接拿到子进程的数据,只能通过系统调用获得
  • 像是一种通信

父进程如何得知子进程的退出信息?

  • 父进程调用系统调用接口来获得子进程的退出信息
  • 1.父进程给waitpid传status变量的地址 2.子进程结束后,代码的退出信息会回写到task_struct中,(task_struct中有这些字段,救就为了给父进程一个交代,也方面父进程去拿)
  • 之后再按照一定的方式回写给status  3.将子进程的exit_state改为X(X肯定是一个宏)
  • 我觉得回收资源是最重要的一点,这样可以创建更多的子进程帮我们完成任务;其次就是任务完成的结果
  • 父进程等待子进程的过程中,被链入子进程的等待队列;task_struct 本身不直接包含等待队列(wait_queue)成员

模拟非阻塞等待轮询逻辑

#include <stdio.h>    
#include <unistd.h>    
#include <stdlib.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
    
#define MAX_WORKER 5    
typedef void (*work)();    
    
void father_work1()    
{    
    printf("father doing work1\n");    
}    
    
void father_work2()    
{    
    printf("father doing work2\n");    
}    
    
void father_work3()    
{    
    printf("father doing work3\n");                                                                                                                                               
}    
void worker()    
{    
    int cnt = 3;    
    while(cnt--)    
    {    
        printf("child process, pid_t: %d, ppid_t: %d, cnt: %d\n", getpid(), getppid(), cnt);    
        sleep(1);    
    }    
}    
void initArray(work array[])    
{    
    for(size_t i = 0; i < MAX_WORKER; i++)    
        array[i] = NULL; 
}

void addWork(work array[], work w)
{
    for(size_t i = 0; i < MAX_WORKER; i++)
    {                                                                                                                                                                             
        if(array[i] == NULL)
        {
            array[i] = w;
            break;
        }
    }
    return;
}

void doingWork(work array[])
{
    for(size_t i = 0; i < MAX_WORKER; i++)
        if(array[i]) array[i](); //(*array[i])();也可以
    //*array[i]这只是解引用函数,() 这才是调用函数
}
int main()
{
    work array[MAX_WORKER]; 
    initArray(array);
    addWork(array, father_work1);
    addWork(array, father_work2);
    addWork(array, father_work3);
    pid_t id = fork();
    if(id == 0)
    {
        worker();
        exit(1);
    }
    else
    {
        while(1)
        {
            pid_t rid = waitpid(id, NULL, WNOHANG);
            if(rid > 0)
            {
                printf("wait success\n");
                break;
            }
            else if(rid == 0)
            {
                printf("father doing other thing\n");
                doingWork(array);
            }
            else
            {
                printf("wait failed\n");
                break;
            }
            sleep(1);
        }
    }
    return 0;
}
  • 代码风格是一个不错的点,尤其是对array的初始化和填值
  • 代码的核心目的就是简单的模拟:父进程不用一直等待没有被回收的子进程
  • waitpid直接可以返回,父进程去做别的工作,过一段时间再来检查子进程是否可以被回收

重点

进程终止

  1. 退出码,信号数字;错误码
  2. exit与_exit的区别

进程等待

  1. 进程等待的概念、原因
  2. wait与waitpid
  3. WIFEXITED与WEXITSTAUTS


http://www.kler.cn/a/380916.html

相关文章:

  • 《点点之歌》“意外”诞生记
  • 中地数码亮相2024武汉市数字经济应用场景对接大会
  • Spring Boot 配置Kafka
  • 20241230 机器学习ML -(1)线性回归(scikitlearn)
  • 【信息系统项目管理师】高分论文:论信息系统项目的成本管理(车站设备智能化管理平台)
  • leetcode之hot100---240搜索二维矩阵II(C++)
  • linux同步执行命令脚本 (xcall)
  • ubuntu工具 -- ubuntu服务器临时没有网络,急需联网下载东西怎么办? 使用手机提供网络
  • AI打造超写实虚拟人物:是科技奇迹还是伦理挑战?
  • 【Python爬虫实战】深入 Selenium:从节点信息提取到检测绕过的全攻略
  • 基于C语言实现的TCP客户端
  • 哈希表相关知识
  • 解决wsl重启后debian配置vm.max_map_count不生效问题以及设置docker开机自启
  • Kafka在大数据处理中的作用及其工作原理
  • 20.04Ubuntu配置opencv并使用头文件
  • CSS--综合练习
  • 商业数据库 - oracle -数据字典
  • SQL 语法学习
  • Spring MVC 完整生命周期和异常处理流程图
  • MySQL学习正式篇
  • 浙江深大智能科技有限公司管控平台服务端存在任意文件上传漏洞
  • nginx安装ssl模块教程
  • java-web-day11-登录校验JWT令牌+过滤器
  • C#实现傅里叶变换算法
  • Spring框架和Spring Boot框架都使用注解来简化配置和提高开发效率,但它们之间存在一些区别
  • Python MySQL - PyMySQL连接数据库和相关操作