进程分析2.0——进程退出、进程等待-Linux重要经典模块

目录

子进程创建中写时拷贝

子进程

写时拷贝:

进程退出

程序中途退出

执行结果:

退出码查询(strerror)

[运行后退出码查询指令echon ?](#运行后退出码查询指令echon ?)

自定义退出码

终结进程的两个exit()

[void exit(int status);](#void exit(int status);)

区别

非阻塞轮询与阻塞调用

进程等待

进程等待的必要性:

☆☆☆waitpid()

参数列表

id:

cdStatus:

option:

返回值

wait()

waitpid()使用示例展示

[int* status参数深度解析](#int* status参数深度解析)

1)正常中止

2)被信号所杀/异常中止(初识)

​编辑

status使用的固定格式:

致谢


子进程创建中写时拷贝

页表:原父进程,虚拟内存表经过页表映射着物理内存表。

在虚拟内存表中,存在数据段(全局变量+静态变量)和"只读"限制代码段(可执行程序+常量)。

子进程

子进程浅拷贝父进程后,先公用同一张物理内存表,但是二者共同对应的数据段映射的页表相应++数据权限收缩变为"只读"++。为清晰展示图解使用两张页表"但实际上是一张表"。

但是子进程内的数据可能会发生"写",既然已经"只读"修饰那么是怎么实现"写"的呢?

写时拷贝:

Ⅰ子进程最初创建下,限制页表数据为"只读"(因为代码执行与虚拟内存的对应必须经过页表传递,所以应限制页表数据)。

Ⅱ当发生"写"时,操作系统(OS)检测到"只读"权限,通过发生"报错"来触发子进程新物理内存页的创建,

进程退出

进程退出的场景:

  • 代码正常运行,后退出。
  • 代码运行到一半,退出。(像:return -1;)
  • 程序中途直接终止。(像:异常捕获)举例如下:

程序中途退出

异常捕获情景:

cpp 复制代码
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void sigsegv_handler(int sig) {
    printf("Caught signal %d (SIGSEGV): Invalid memory access!\n", sig);
    // 可以尝试恢复、记录日志、或优雅退出
    exit(1);
}

int main() {
    // 注册异常捕获函数
    signal(SIGSEGV, sigsegv_handler);

    int *p = NULL;
    *p = 42;  // 写入地址 0 → 触发页面错误 → 内核发送 SIGSEGV
              // 程序不会崩溃,而是进入 sigsegv_handler

    printf("This line will not be executed (unless handler returns)\n");
    return 0;
}

执行结果:

退出码查询(strerror)

退出码:程序退出时时的返回值,每个退出码对应一个退出描述

char* strterror(int index); #返回退出描述共[0,133]

cpp 复制代码
void test2()
{
    for(int i = 0;i < 140;i++)
        cout << i << "->" << strerror(i) << endl;
}

运行得到退出码表:

运行后退出码查询指令echon $?

查询最近一次程序运行后的退出码:

前言:在base下的每次命令执行后,就相当于一次子进程的运行自然有一次退出码。

++已知程序返回1++ ,使用echo $? ++连续查询两次++

自定义退出码

由:char* strterror(int index);

观察strerror可知我们可以手动定义自己的退出码,仅需将返回值内容修改即可。

终结进程的两个exit()

void exit(int status);

头文件:<stdlib.h>

参数:status #作为返回参数,返回对象为父进程。

功能:终结调用此函数的进程。状态值(status)的低八位被返回给父进程。

区别

1.exit()退出后会刷新缓冲区而_exit()函数退出后不会刷新缓冲区。

2.明显exit()是语言层面,而_exit()是系统层面------其实exit()的实现内部封装_exit()。

非阻塞轮询与阻塞调用

非阻塞轮询与阻塞调用的重大区别在于:检测进程(等待进程),是否在等待时(执行到waitpid()/wait())可以依旧执行自己的任务

++结合名字"阻塞调用"就是等待进程无法依旧执行自己的任务而"非阻塞轮询"下就可以执行自己的任务的同时监测待退出进程++。

☆因此"同时监测"就一定意味着等待进程在循环体 内,执行逻辑,使得"一直"对目标进程"询问"是否执行完毕?

进程等待

进程等待的必要性:

  • 产生僵尸进程:子进程退出后,父进程不管不顾的子进程状态。------"资源泄露"
  • 僵尸进程无法被kill -9去除,因为kill -9杀死的是活着的进程。
  • ++子进程完成的任务怎么样,我们必须要知道++。

进程等待的处理原则:

父进程通过等待的方式(执行函数 wait() / waitpid() )来回收子进程的资源+获取子进程的信息

☆☆☆waitpid()

头文件:<sys/wait.h>

函数原型:

cpp 复制代码
pid_t waitpid(pid_t id, int* cdStatus, int option);
//                     接纳子进程退出值   
参数列表
id:

|-----|-----------------|
| id | 含义 |
| -1 | 等待所有子进程 |
| >0 | 指定对象子进程id等待 |

cdStatus:

子进程退出值的接纳,便于后续位图操作。

option:

0:阻塞等待 子进程退出。

WNOHANG:非阻塞,父进程检测是否有子进程退出,如果无立即返回0

返回值

>0 已经退出子进程id

=0 设置了++WNOHANG++ ,且没有子进程退出(非阻塞返回)。

-1 出错,表示调用失败。

wait()

pid_t wait(int *status); // 等待所有的子进程

返回值:退出等待的中止进程id / -1 表示等待失败

*我们常用waitpid()

waitpid()使用示例展示

涉及进程分析此处推荐我的一篇呕心沥血文章 进程分析---从操作系统到Linux内核深入

父进程阻塞等待(option == 0),子进程运行四秒。

cpp 复制代码
void test3()
{
    //父进程等待4秒子进程退出 父进程回收
    pid_t id = fork();
    int count = 4;
    if(id == 0)
    {
        while(count)
        {
            cout << "子进程退出倒计时: " << count << endl;
            count--;
            sleep(1);
        }
    }
    else if(id > 0){
        int status=0;
        pid_t ret = waitpid(id, &status, 0);
        if(ret > 0) cout << "子进程退出后成功回收, 回收id为: " << ret << endl;
        else perror("失败\n");
    }
}

执行:

int* status参数深度解析

更深入功能:通过1*32位图分析,解析出"退出码"+"退出信号"。

其功能分作进程终结的三种情况来分析。

1)正常中止

32个比特位仅使用8+7位:

据其性质我们常使用位运算-> status>>8或者使用宏-WEXITSTATUS(status)来获取退出码,与strerror()参照。

子进程等待三秒,使用exit直接中止子进程,父进程捕获退出码与中止信号:

cpp 复制代码
void test()
{
    pid_t id = fork();
    if(id == 0)
    {
        //子进程
        int cut = 3;
        while(cut)
        {
            printf("我是一个子进程, pid:%d,ppid:%d\n", getpid(), getppid());
            sleep(1);
            cut--;
        }
        exit(10);//退出码被父进程捕获
    }

    //父进程
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        printf("wait success, rid:%d,exit code: %d, exit signal: %d\n",\
                 rid, status>>8, status&0x7F);
            // 退出码: status右移8位 退出信号: status低7位
    }
    else // ==0 WNOHANG下子进程未退出, 父进程未等待, 没阻塞
         // -1 出错
    {
        perror("wait failed\n");
    }
}

运行结果:

2)被信号所杀/异常中止(初识)

总结:取退出码是对waitpid的第二个参数status右移8位取值,取中止信号是取status的低7位(status&0x7F)。

status使用的固定格式:

致谢

感谢支持 努力终有回报

欢迎关注

(✪ω✪)

相关推荐
lihao lihao4 小时前
MFC知识点
c++·mfc
ch.ju4 小时前
Java Programming Chapter 4——Dynamic part
java·开发语言
阿正的梦工坊4 小时前
Kotlin 面试题全面解析:从基础到进阶
android·开发语言·kotlin
pengyi8710154 小时前
高匿代理核心原理详解,隐藏真实IP实现无痕网络访问
linux·运维·服务器·网络·tcp/ip
Irissgwe4 小时前
九、Linux信号机制(一)
linux·信号处理·信号·进程信号·信号的产生
沐知全栈开发4 小时前
TypeScript Map 对象
开发语言
皮卡蛋炒饭.4 小时前
数据链路层相关学习
linux·数据链路层
yujunl4 小时前
U9开发模式之一门面模式的理解
开发语言
奔跑的Ma~4 小时前
第6篇:蓝桥杯C++进阶突破(难题拆解+算法优化,冲刺国赛高奖)
c++·算法·蓝桥杯·#蓝桥杯备战·#c++编程·编程竞赛