【Linux】详解进程控制 ( 再谈进程退出 | 程序替换exec*类型函数 )

news2024/11/15 7:19:47

  • 再谈进程退出
  • 进程程序替换
    • 引入
    • 程序替换原理
    • 有哪些替换函数
      • execl:
      • execlp:
      • execv:
      • execvp:
      • execle:
    • execve:

接续上篇博客 “详解进程控制 ( fork函数 | 写时拷贝 | 进程退出 | 进程等待 )”

再谈进程退出

  1. 进程退出会变成僵尸状态,将自己的推出结果写入task_struct
  2. wait与waitpid是一个系统调用,操作系统有资格也有能力去读取子进程的task_struct
  3. wait与waitpid就是从退出子进程的task_struct中获取的

感性认识阻塞与非阻塞:

假设张三喊李四去吃饭,张三给李四打电话,但是李四说他还有事大概需要等30分钟,这时候张三:
1.张三不挂电话,一直等到李四说我好了然后去吃饭 ----- 阻塞
2.张三把电话挂了,然后去做自己的事情边做边等,过个几分钟给李四打电话问他好了没,过个几分钟又打电话……-----每一次打电话都是一次非阻塞等待(多次非阻塞等待\轮询
打电话对应的是系统调用wait\waitpid, 张三(父进程)、李四(子进程)

轮询访问父进程代码:

//父进程
int status = 0;
while(1)
{
    pid_t ret = waitpid(id, &status, WNOHANG); //WNOHANG: 非阻塞-> 子进程没有退出, 父进程检测时候,立即返回
    if(ret == 0)
    {
        // waitpid调用成功 && 子进程没退出
        printf("wait done, but child is running...., parent running other things\n");
    }
    else if(ret > 0)
    {
        // 1.waitpid调用成功 && 子进程退出了
        printf("wait success, exit code: %d, sig: %d\n", (status>>8)&0xFF, status & 0x7F);
        break;
    }
    else
    {
        // waitpid调用失败,比如我的waitpid的函数参数id写错
        printf("waitpid call failed\n");
        break;
    }
    sleep(1);
}

运行结果:
在这里插入图片描述

非阻塞状态有什么好处?不会占用父进程的所有精力,可以在轮询期间干干别的事情。

进程程序替换

创建子进程目的:

  • 想让子进程执行父进程代码的一部分(执行父进程对应的磁盘代码中的一部分)
  • 想让子进程执行一个全新的程序(让子进程想办法,加载磁盘上指定的程序,执行新程序的代码和数据)

引入

int execl(const char *path, const char *arg, ...);
//将指定的程序加载到内存中,让指定进程进行执行
//path是用来找到这个程序,如何执行就是后面的arg传入的选项

比如:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 int main()
  4 {
  5     printf("process is running...\n");
  6 
  7     execl("/usr/bin/ls","ls",NULL);
  8 
  9     printf("process is done...\n");
 10     return 0;
 11 }

在这里插入图片描述
运行结果:
在这里插入图片描述
我将增加几个执行选项,并加上颜色显示:

execl("/usr/bin/ls","ls","--color=auto","-a","-l",NULL); 

在这里插入图片描述

上述过程就是程序替换,我们只要能找到这个程序,然后添加它的运行选项就可以执行。

程序替换原理

我们看之前的代码打印出来的结果:
在这里插入图片描述
程序执行图:
在这里插入图片描述
为什么后一个printf没有执行?
 printf也是代码,且在execl函数之后,execl函数执行完毕的时候,代码已经全部被复制,开始执行新的程序的代码了,所以printf就无法执行。
 execl是一个函数,而一个函数的调用就有可能成功有可能失败,调用失败就是没有替换成功,也就是没替换,所以会继续执行原代码:
在这里插入图片描述
 exec*没有成功返回值,因为成功了就与接下来的原代码无关,判断没有意义。execl只要返回了,一定是错误了。
在这里插入图片描述
在这里插入图片描述

父进程创建子进程,子进程的替换会影响父进程吗?

  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <stdlib.h>
  4 #include <assert.h>
  5 #include <sys/types.h>
  6 #include <sys/wait.h>
  7 
  8 int main()
  9 {
 10     printf("process is running...\n");
 11     pid_t id  = fork();
 12     assert(id != -1);
 13     if(id==0)
 14     {
 15         sleep(1);
 16         execl("/usr/bin/ls","ls","-a",NULL);                                                                                                                                                                  
 17         exit(1);                                    
 18     }                                               
 19     int status=0;                                   
 20     pid_t ret = waitpid(id,&status,0);              
 21     if(ret>0)                                       
 22     {                                               
 23         printf("wait sucess : exit code:%d, signal:%d\n",(status>>8)&0xFF,status & 0x7F);
 24     }
 25		return 0;
 26 }

在这里插入图片描述

在这里插入图片描述
 进程具有独立性,子进程成功调用exec*会发生写时拷贝,在物理内存里面重新加载代码与数据,然后更改子进程的页表映射,此过程不会影响父进程。

虚拟地址空间+页表 : 保证了进程独立性,一旦有执行流想替换代码或数据,就会发生写时拷贝

有哪些替换函数

使用man指令查看execl函数
在这里插入图片描述
 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

这些函数开头都是exec,而后面跟的字符可以代表不同含义:

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

execl:

int execl(const char *path, const char *arg, ...);

 l:list:将参数一个一个的传入exec*

替换自己写的可执行程序:

execl("./myexe","myexe",NULL);

在这里插入图片描述

程序替换,可以使用程序替换,调用任何后端语言对应的可执行–程序

execlp:

int execlp(const char *file, const char *arg, ...);
//带p无需写全路径,可以直接使用环境变量PATH
execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);

 这里有两个ls, 重复吗?不重复,一个是告诉系统我要执行谁?一个是告诉系统,我想怎么执行
 p:path:如何找到程序的功能,带p字符的函数,不用告诉我程序的路径,你只要告诉我是谁,我会自动在环境变量PATH,进行可执行程序的查找!

execv:

int execv(const char *path, char *const argv[]);
char *const argv_[] = {
            "ls",
            "-a",
            "-l",
            "--color=auto",
            NULL
        };
execv("/usr/bin/ls", argv_);

 v:vector:可以将所有的执行参数,放入数组中,统一传递,而不用进行使用可变参数方案

execvp:

int execvp(const char *file, char *const argv[]);
//将 v 与 p 的特性结合起来
char *const argv_[] = {
            "ls",
            "-a",
            "-l",
            "--color=auto",
            NULL
        };
execvp("ls", argv_);
int main(int argc, char *argv[])
// ./exec ls -a -l -> "./exec" "ls" "-a" "-l"
execvp(argv[1], &argv[1]);

运行结果:
在这里插入图片描述

execle:

int execle(const char *path, const char *arg, ...,char *const envp[]);
//需要自己组装环境变量
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execle("ps", "ps", "-ef", NULL, envp)

 自定义环境变量举个例子:
在这里插入图片描述
 为了即使用全局环境变量又添加自己的环境变量,我们使用putenv来增加以一个环境变量:
在这里插入图片描述

在这里插入图片描述
先加载execle后执行main:
在这里插入图片描述

execve:

我们使用man手册打开它,可以知道它在2号手册里,代表他是系统调用
在这里插入图片描述

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节
程序替换中execve为系统调用,其它都是封装,为了让我们有更多的选择性


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

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

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

相关文章

芯片等高科技制造业 如何实现安全的跨网数据交换?

芯片是信息产业的基础&#xff0c;一直以来占据全球半导体产品超过80%的销售额&#xff0c;在计算机、家用电器、数码电子、自动化、电气、通信、交通、医疗、航空航天等几乎所有的电子设备领域中都有使用。 所以&#xff0c;对于芯片这种高科技制造业来说&#xff0c;数据的安…

装饰模式(Decorator)

别名 装饰者模式&#xff08;Wrapper&#xff09;。 定义 装饰是一种结构型设计模式&#xff0c;允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 前言 1. 问题 假设你正在开发一个提供通知功能的库&#xff0c;其他程序可使用它向用户发送关于重…

Debezium系列之:Debezium 通知

Debezium系列之&#xff1a;Debezium 通知 一、概述二、Debezium 通知格式三、可用的通知四、启用 Debezium 通知五、访问 Debezium JMX 通知六、自定义通知渠道七、配置自定义通知渠道八、Debezium 核心模块依赖项九、部署自定义通知渠道十、配置连接器以使用自定义通知通道 一…

MFC加载3ds模型初步

网上下一个资源&#xff0c;名为 OpenGL三维场景绘制.rar&#xff1b; 看一下它是用MFC和opengl&#xff0c;自己绘制三维场景&#xff1b; 运行一下&#xff0c;有一个exe可以运行&#xff1b; 有一个较新版本的不能运行&#xff1b;这应是缺少VC运行库&#xff1b; 下面单独…

Linux下RPM软件包管理

目录 1、软件包管理介绍1.1、软件包分类1.2、源码包1.3、RPM包 2、RPM包管理-包命名和依赖性2.1、RPM命名规则2.2、RPM包依赖性 3、RPM包管理-安装升级和与卸载3.1、包全名与包名3.2、RPM安装3.3、RPM包升级3.4、卸载 4、RPM包管理-查询4.1、查询是否安装4.2、查询软件包详细信…

Gitlab将本地代码推送到远程空仓库

目录 引言 1、设置Git为源代码管理插件 2、创建Git仓库 3、设置多个远程仓库 引言 如果我们的本地代码想上传到公司内部的服务器&#xff0c;首先我们需要在VS2022中创建Git仓库&#xff0c;然后设置远程仓库的地址&#xff0c;才能将本地代码推送到远端。在远端会根据你本地…

表格式表单-table式from表单-合并行-合并列

效果: 使用【colspan】合并行 和【rowspan】合并列 html: <!-- 添加或修改报告数据库对话框 --><el-dialog :title"title" :visible.sync"open" width"1500px" append-to-body><el-form ref"form" :model"form&q…

Android Jetpack Compose之Checkbox的使用

Android Jetpack Compose 是一个现代化的 UI 工具包&#xff0c;为开发者提供了一种声明式的方式来构建出美观且功能强大的 Android 应用。在本文中&#xff0c;我们将详细介绍其中的一个重要组件——Checkbox。 一. Checkbox 简介 Checkbox 是 Jetpack Compose 中的一个组件&…

STM32单片机(三)第四节:GPIO输入练习2(光敏传感器控制蜂鸣器)

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

一文学会如何使用Docker

Docker常见使用 1、Docker安装 ## 下载阿里源repo文件 $ curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo $ curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo$ yum clean …

详解8种不同类型的防火墙

什么是防火墙&#xff1f; 防火墙是一种监视网络流量并检测潜在威胁的安全设备或程序&#xff0c;作为一道保护屏障&#xff0c;它只允许非威胁性流量进入&#xff0c;阻止危险流量进入。 防火墙是client-server模型中网络安全的基础之一&#xff0c;但它们容易受到以下方面的攻…

选择低代码平台的正确方式

传统开发模式早已不能满足大多数追求效率的企业的要求&#xff0c;低代码平台的出现正是可以缓解相应的开发压力&#xff0c;作为使用者我们更应该擦亮眼睛&#xff0c;选择合适的平台产品&#xff0c;充分利用新技术带来的新价值。小编在以前的文章有介绍过低代码平台应该如何…

php中的双引号与单引号的基本使用

字符串,在各类编程语言中都是一个非常重要的数据类型 网页当中的图片,文字,特殊符号,HTMl标签,英文等都属于字符串 PHP字符串变量用于存储并处理文本, 在创建字符串之后&#xff0c;我们就可以对它进行操作。我们可以直接在函数中使用字符串&#xff0c;或者把它存储在变量中 字…

360手机命令行进入fastboot线刷模式 360手机刷机

360手机命令行进入fastboot线刷模式 360手机刷机 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机&#xff1a;360rom.github.io 【前言】 因360手机特殊&#xff1b;且因机器情况而异&#xff1b;导致360手机进不去fastboot线刷模式、360手机进…

基于Java+Swing实现坦克大战游戏

基于JavaSwing实现坦克大战游戏 一、系统介绍二、功能展示三、其他系统四、获取源码 一、系统介绍 此系统是使用Java语言实现坦克大战游戏程序&#xff0c;玩家通过连接访问进入游戏&#xff0c;通过操纵坦克来守卫基地&#xff0c;玩家还可以获得超级武器来提升坦克的属性&am…

点云特征描述子概述、PFH描述子提取

1、 6种点云特征描述子简概 NARF&#xff08;Normal Aligned Radial Feature&#xff09;特征点描述子&#xff1a;NARF描述子是一种基于法线对齐的径向特征描述子。它通过将点云表面分割为小的网格单元&#xff0c;并计算每个单元中的法线直方图&#xff0c;从而提取特征。NA…

【网站监控】如何监控自己的网站(接口)

网站监控-如何监控自己的网站 前言一、开始使用1、使用API进行监控数据采集?2、请求参数3、如何查看监控效果? 二、注意点 前端必备工具&#xff08;免费图床、API、chatAI等&#xff09;推荐网站LuckyCola: https://luckycola.com.cn/ 前言 网站接口监控是指对接口的状态进…

Keil为啥比IAR更受欢迎?

关注星标公众号&#xff0c;不错过精彩内容 作者 | strongerHuang 微信公众号 | strongerHuang 最近交流群在讨论【选择Keil和IAR的问题】&#xff0c;这就顺便展开来说下。 你可能觉得Keil、IAR这种集成开发环境界面比较古老&#xff0c;又不好用。 但是&#xff0c;这里告诉大…

ConcurrentModificationException异常分析与解决

ConcurrentModificationException异常分析与解决 1、场景重现&#xff0c;制造ConcurrentModificationException异常 Testpublic void ConcurrentModificationExceptionTest() {JSONArray jsonArray new JSONArray();JSONObject jsonObject new JSONObject();jsonObject.put…

改写cocos2d的ProgressTimer实现任意起始点的Radial进度条

解释一下要做的事&#xff1a; 原生ProgressTimer控件的进度起始点只能是在&#xff08;0.5&#xff0c;1&#xff09;的位置&#xff0c;如下&#xff1a; 我们要改成可以将矩形边上的任意点作为起始点&#xff0c;如下&#xff1a; 首先讲一下绘制的逻辑&#xff1a; 先根…