【Linux进程】进程等待 与 进程替换 原理与函数使用

news2024/9/25 21:29:47

文章目录

  • 一、进程等待
    • 1.1 意义 / 必要性
    • 1.2 进程等待的函数(wait / waitpid)
    • 1.3 status参数
    • 1.4 获取子进程status
    • 1.5 进程的阻塞等待与非阻塞等待
  • 二、进程替换
    • 2.1 引言
    • 2.2 进程替换原理
    • 2.3 替换函数

一、进程等待

1.1 意义 / 必要性

为什么要有进程等待? 我们知道,当子进程退出时,如果父进程不对其进行操作,子进程会变为僵尸进程。

而且当子进程结束时,我们如何知道子进程的最后执行情况呢?

父进程通过进程等待,可以:

  1. 回收子进程资源
  2. 获取子进程退出信息
  3. 同步操作

1.2 进程等待的函数(wait / waitpid)

wait函数

pid_t wait(int *status);
  • 该函数会暂停当前进程的执行,直到一个子进程结束。
  • 如果子进程已经结束,该函数会立即返回。
  • 函数返回值:执行成功 返回被等待进程pid,失败返回-1。
  • status参数:获取子进程的退出状态,不需要则设为null。

waitpid函数

pid_t waitpid(pid_t pid, int *status, int options);
  • 参数:

    • pid
      pid = -1,表示等待任意子进程
      pid = 0,等待进程id=pid 的子进程
    • status
      WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
      WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
    • option
      WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
  • 该函数会暂停当前进程的执行,直到指定的子进程结束。

  • 返回值是结束子进程的进程ID,或者出错时的返回值。

主要的区别和用法总结如下:

  • wait函数只能等待任意子进程结束,而waitpid函数可以指定具体的子进程进行等待
  • waitpid函数提供了更多的选项,可以在等待子进程时指定不同的行为。
  • waitpid函数可以通过设置options参数来控制等待的方式,例如非阻塞、只等待指定状态等。
  • 在多进程的场景中,可以使用waitpid函数更灵活地管理子进程的状态。

1.3 status参数

  • waitwaitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果 传递NULL,表示不关心子进程的退出状态信息
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程

status是int型的整数,用于存储子进程的退出状态信息。其低7位(0-6位)第7位(第7位),其他位(8位及以上),分别存放不同信息。

进程正常终止情况下

  • 低八位(0-7位) 存储的是进程的退出状态。这个状态是由exit系统调用或者C语言中的exit函数传递给操作系统的。
  • 第八位:存储是否生成了core dump文件,如果生成了core dump文件,则该位会被置为1。
  • 高八位(16-31位) 通常被设置为0,用于标识进程的终止状态。

被信号终止情况下

  • 低八位 :存储的是导致进程终止的信号编号。当进程被信号终止时,状态的低八位会存储信号的编号,例如SIGSEGV表示段错误,SIGKILL表示进程被强制终止等。
  • 第8位 :存储是否生成了core dump文件,如果生成了core dump文件,则该位会被置为1。
  • 高八位 :通常被设置为0,用于标识进程的终止状态。

我们用位图来解释status

在这里插入图片描述

1.4 获取子进程status

我们通过下面一段代码 获取子进程status

// 程序获取进程status
int main() {
    pid_t id = fork();

    if (id == -1) {
        // 创建子进程失败
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (id == 0) {
        // 子进程
        printf("子进程正在运行\n");
        exit(10);  // 子进程退出状态设为 10
    } else {
        // 父进程
        int status;

        pid_t iid = waitpid(id, &status, 0);
        printf("After the wait function\n");
        if(iid == id)
        {
            printf("wait success, pid: %d, status: %d\n", iid, status);
        }
        sleep(10);
    }

    return 0;
}

代码执行结果如下

在这里插入图片描述

为什么我们给出退出码exit(10)而最后status值为2560?

根据上面status的存储分析:如果子进程正常退出,status 的值将会是子进程退出码的左移 8 位。

即前八位存储的是退出状态, 0000 1010 0000 0000,当我们返回其十进制后,即为2560。

1.5 进程的阻塞等待与非阻塞等待

意义

进程的阻塞等待和非阻塞等待是指在等待某个事件完成时,进程所采取的不同等待方式。

1. 阻塞等待(Blocking Wait)

  • 当一个进程进行阻塞等待时,它会暂时停止执行,并将控制权交给操作系统。进程将进入睡眠状态,直到所等待的事件发生或满足某个条件。
  • 在阻塞等待期间,操作系统会将进程从可运行状态转换为阻塞状态,并在事件发生后将进程重新唤醒。
  • 阻塞等待是一种同步等待方式,即进程会一直等待直到事件完成或超时。

2. 非阻塞等待(Non-blocking Wait)

  • 当一个进程进行非阻塞等待时,它会继续执行其他任务,而不会暂停等待。进程会周期性地检查所等待的事件是否已经发生或满足某个条件。
  • 如果事件已经发生,则进程可以立即处理该事件并继续执行。如果事件还未发生,则进程可能会继续轮询等待,或者进行其他操作。
  • 非阻塞等待是一种异步等待方式,即进程可以同时进行其他任务,而不必一直等待。

代码实现

1.5.1 进程阻塞等待

下面是代码实现:

int main()
{
    pid_t id = fork();

    if(id < 0) {
        // fork error
        perror("fork error");
        exit(EXIT_FAILURE);
    }
    else if(id == 0) {
        // 子进程
        std::cout << "子进程正在运行, pid: " << getpid() << std::endl;
        exit(10);
    }
    else{
        // 父进程
        int status;
        pid_t rid = waitpid(id, &status, 0); // 阻塞等待,options设为0
        std::cout << "after wait" <<std::endl;
        if(rid == id && WIFEXITED(status))
        {
            std::cout << "wait success, pid: " << rid << ", status: " << status << std::endl;
        }
        else
        {
            std::cout << "子进程等待失败, return." << std::endl;
            return 1;
        }
        sleep(10);
    }
    return 0;
}

代码执行结果如下

在这里插入图片描述

1.5.2 进程非阻塞等待

代码实现如下:

int main()
{
    pid_t id = fork();
    if(id < 0){
        // error
        perror("fork erorr");
        exit(EXIT_FAILURE);
    }else if(id == 0){
        printf("子进程正在运行,pid:%d \n", getpid());
        exit(10);
    }else{
        int status;

        pid_t rid;
        do{ // 有子进程退出时 rid返回其pid
            pid_t rid = waitpid(id, &status, WNOHANG);
            if(rid == 0)
            {
                printf("child process is running\n");
            }
            sleep(5);
        }while(rid == 0);
        
        if(rid == id && WIFEXITED(status))
        {
            printf("wait success, child return code is :%d.\n",WEXITSTATUS(status));
        }else{
            printf("wait child failed, return.\n");
            return 1;
        }
    }

    return 0;
}

代码执行结果

在这里插入图片描述


二、进程替换

2.1 引言

首先,我们看下面一段代码:

#include <iostream>
#include <unistd.h>

int main() {
    std::cout << "This is the original program." << std::endl;

    // 执行程序替换,将当前进程替换为/bin/ls(列出当前目录内容)
    char *args[] = {"/bin/ls", "-l", NULL};
    execv("/bin/ls", args);

    // 如果execv执行成功,下面的代码不会被执行
    std::cerr << "Failed to execute /bin/ls!" << std::endl;
    return 1;
}

代码的执行结果如下:

我们可以看到,程序运行后,执行了 ls -l 操作,而 Failed to execute /bin/ls! 并没有被打印到下面。

而ls本身也是一个程序,可以理解为,进程 被替换为了 ls 程序

2.2 进程替换原理

根据上面的图,我们分析进程替换原理,进程替换可以理解为下面的步骤:

  1. 加载新程序映像:操作系统会从磁盘上读取新程序的可执行文件,并将其加载到内存中。
  2. 创建新的页表:在加载新程序映像时,操作系统会创建一个新的页表来管理新程序所需的内存空间。
  3. 替换进程内容:一旦新程序映像加载到内存中,操作系统 会将当前进程的内容替换为新程序的内容(代码、数据和堆栈等)
  4. 开始执行新程序:替换完成后,控制权转移到新程序的入口点,新程序从那里开始执行

2.3 替换函数

像上面代码中所使用的execv函数,叫做程序替换函数:

下面列举这些程序替换函数,定义如下:

int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv[], char *const envp[]);

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);

这些函数都是程序替换函数,它们的区别在于传递参数的方式和环境变量的处理:

  1. execv:
  • 参数:接受一个参数数组 argv,以及程序路径 path。
  • 示例:execv("/bin/ls", argv);
  1. execvp:
  • 参数:与execv 类似,但是可以通过环境变量 PATH 搜索可执行文件。
  • 示例:execvp("ls", argv);
  1. execve:
  • 参数:接受一个参数数组 argv,一个环境变量数组 envp,以及程序路径 filename。
  • 示例:execve("/bin/ls", argv, envp);
  1. execl:
  • 参数:接受多个参数,最后一个参数必须是 NULL,不接受参数数组,需要将每个参数单独列出。
  • 示例:execl("/bin/ls", "ls", "-l", NULL);
  1. execlp:
  • 参数:与execl 类似,但是可以通过环境变量 PATH 搜索可执行文件。
  • 示例:execlp("ls", "ls", "-l", NULL);
  1. execle:
  • 参数:与execl 类似,但是可以传递自定义的环境变量数组 envp。
  • 示例:execle("/bin/ls", "ls", "-l", NULL, envp);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1227622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【【VDMA彩条显示实验之二】】

VDMA彩条显示实验之二 这一篇紧接上一篇文章 我们添加一个 VID_out 的 IP核 其实 相对来说 就是我们把 传进来的串行信号 转化成并行输出各个信号 &#xff08;把 Stream 的 输出信号流转化成在 RGB上 输出的 格式 &#xff09; 下面是对IP核的简介 AXI4-Stream to Video Out…

八、Linux关机重启和用户登录注销

1.Linux关机、重启 基本介绍 shutdown -h now 立即进行关机 shutdown -h 1 “hello&#xff0c;1分钟后会关机了”(h&#xff1a;halt) shutdown 默认就是&#xff08;shutdown -h 1&#xff09; shutdown -r now 现在重新启动计算机(r : reboot) halt 关机&#xff0c;作用和…

Tomcat无法映射到activiti-app导致activiti无法启动页面

原因之一&#xff1a;JDK版本与Tomcat版本不匹配&#xff0c;jdk8 yyds 我使用的是JDK11&#xff0c;Tomcat是9.0的&#xff0c;都是最新的&#xff0c;但还是不行&#xff0c;最后JDK改为8&#xff0c;tomcat的cmd后台没有报错&#xff0c;activiti-pp也可以正常访问了,很神奇…

基于RK3588全高端智能终端机器人主板

一、小尺寸板型设计 该款主板为小型板&#xff0c;尺寸仅为125*85mm&#xff0c;更小更紧凑&#xff0c;可完美适应各类高端智能自助终端&#xff1b; 二、八核高端处理器 采用RK3588S八核64位处理器&#xff0c;8nm LP制程&#xff0c;主频最高达2.4GHz&#xff0c;搭载Andr…

Python大数据之linux学习总结——day11_ZooKeeper

ZooKeeper ZK概述 ZooKeeper概念: Zookeeper是一个分布式协调服务的开源框架。本质上是一个分布式的小文件存储系统 ZooKeeper作用: 主要用来解决分布式集群中应用系统的一致性问题。 ZooKeeper结构: 采用树形层次结构&#xff0c;ZooKeeper树中的每个节点被称为—Znode。且树…

Web实战:基于Django与Bootstrap的在线计算器

文章目录 写在前面实验目标实验内容1. 创建项目2. 导入框架3. 配置项目前端代码后端代码 4. 运行项目 注意事项写在后面 写在前面 本期内容&#xff1a;基于Django与Bootstrap的在线计算器 实验环境&#xff1a; vscodepython(3.11.4)django(4.2.7)bootstrap(3.4.1)jquery(3…

​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第12章 信息系统架构设计理论与实践&#xff08;P420~465&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

什么是tomcat, tomcat该如何使用?(java)

tomcat是什么? tomcat翻译过来为汤姆猫, 但是他可不是猫和老鼠中的汤姆, 而是java中的tom, 虽然java中的tomcat没有猫和老鼠那么出名, 但是他仍然是java中的中流砥柱 下图为java中的tomcat, 也就是最右边这个黄色的猫: Tomcat是Apache 软件基金会&#xff08;Apache Software …

AI绘画使用Stable Diffusion(SDXL)绘制三星堆风格的图片

一、前言 三星堆文化是一种古老的中国文化&#xff0c;它以其精湛的青铜铸造技术闻名&#xff0c;出土文物中最著名的包括青铜面具、青铜人像、金杖、玉器等。这些文物具有独特的艺术风格&#xff0c;显示了高度的工艺水平和复杂的社会结构。 青铜面具的巨大眼睛和突出的颧骨&a…

【洛谷 B2002】Hello,World!(顺序结构)

Hello,World! 题目描述 编写一个能够输出 Hello,World! 的程序。 提示&#xff1a; 使用英文标点符号&#xff1b;Hello,World! 逗号后面没有空格。H 和 W 为大写字母。 输入格式 输出格式 样例 #1 样例输入 #1 无样例输出 #1 Hello,World!思路 #include 是一个预处…

MyBatis 快速入门

MyBatis 快速入门 前言什么是 MyBatis简介核心特性使用示例配置文件Mapper 接口SQL 映射文件使用 MyBatis 如果大家对以上的导读很懵怎么办&#xff01;没关系 往下阅读&#xff01; 1. MyBatis 介绍1.1. 什么是MyBatis1.2. 持久层1.3. 框架1.4. JDBC 弊端1.5.…

有成效的工作

从开始上班起&#xff0c;听到过工作是做不完得。 大概的意思&#xff0c;现在的工作做完了&#xff0c;就会分配新的工作。所以总也做不完。 如果是做不完的&#xff0c;那么是不是在一个岗位上就一直干着呢。既然这个很难成立。那其实工作是可以干得完的。 一个岗位的终结&am…

redis+python 建立免费http-ip代理池;验证+留接口

前言: 效果图: 对于网络上的一些免费代理ip,http的有效性还是不错的;但是,https的可谓是凤毛菱角; 正巧,有一个web可以用http访问,于是我就想到不如直接拿着免费的HTTP代理去做这个! 思路: 1.单页获取ipporttime (获取time主要是为了后面使用的时候,依照时效可以做文章) 2.整…

矩阵运算_矩阵的协方差矩阵/两个矩阵的协方差矩阵_求解详细步骤示例

1. 协方差矩阵定义 在统计学中&#xff0c;方差是用来度量单个随机变量的离散程度&#xff0c;而协方差则一般用来刻画两个随机变量的相似程度。 参考&#xff1a; 带你了解什么是Covariance Matrix协方差矩阵 - 知乎 2. 协方差矩阵计算过程 将输入数据A进行中心化处理得到A…

马斯克的SpaceX星舰又炸了!发射不久后失联自毁

就在几小时前&#xff0c;马斯克旗下SpaceX 发射了有史以来最强大的星舰&#xff0c;但在发射后不久发生爆炸。 在这次发射尝试中&#xff0c;星舰一二级成功进行了分离&#xff0c;但二级助推器和星舰都发生了快速意料之外的解体。在发射半小时后&#xff0c;SpaceX 宣布二级自…

7个最佳开源免费库存/仓库管理系统(WMS)

库存/仓库管理软件是一种用于帮助企业管理库存、仓储位置和交付过程的软件系统。这种类型的软件对于拥有大量库存和多个仓库的企业非常有用。 库存/仓库管理软件的作用包括以下几个方面&#xff1a; &#xff08;1&#xff09;减少库存节约成本 通过跟踪库存水平和存储位置&…

从零开始:Rust环境搭建指南

大家好&#xff01;我是lincyang。 今天&#xff0c;我们将一起探讨如何从零开始搭建Rust开发环境。 Rust环境搭建概览 Rust是一种系统编程语言&#xff0c;以其安全性、并发性和性能闻名。搭建Rust环境是学习和使用这一语言的第一步。 第一步&#xff1a;安装Rust Rust的…

二维码智慧门牌管理系统升级解决方案:查询功能大提升,让地址查找变得轻松便捷!

文章目录 前言一、支持地址名称、小区等信息进行模糊查询二、支持地图上绘制多边形、圆形、矩形进行范围查询三、高效的数据处理能力&#xff0c;保证查询速度四、灵活的应用场景&#xff0c;满足多种需求 前言 随着科技的快速发展和城市化的加速推进&#xff0c;传统的门牌管…

六.Linux远程登录

1.说明&#xff1a;公司开发的时候&#xff0c;具体的应用场景是这样的 1.linux服务器是开发小组共享 2.正式上线的项目是运行在公网 3.因此程序员需要远程登录到Linux进行项目管理或者开发 4.画出简单的网络拓扑示意图(帮助理解) 5.远程登录客户端有Xshell6、Xftp6&#xff0…

MyISAM和innoDB两种引擎的对比

innoDB 3.23就有了innoDB引擎&#xff0c;5.5成为了默认引擎&#xff0c;支持外键 是一种事务型引擎&#xff0c;可以保证完整提交和回滚 更新、删除比较多的场景&#xff0c;推荐使用innoDB 不过innoDB对内存要求高&#xff0c;因为索引和数据存到一个表了&#xff1b;写操作…