18 进程替换

news2024/10/7 10:07:47

目录

1.什么是进程替换
2.替换原理
3.替换函数
4.函数解释
5.具体应用
6.makefile构建多个文件
7.运行自己程序
8.运行其他语言程序
9.简易shell

什么是进程替换

fork之后的父子程序共享代码,如果子进程想执行一个全新的程序。就用进程替换来完成这个功能,通过特定的接口,加载磁盘上的一个权限的程序(代码和数据),加载到调用程序的地址空间中

替换原理

子进程往往要调用exec函数执行另一个程序,当调用时,进程的用户空间代码和数据完全被新程序替换,从新程序的启动历程执行,调用exec不创建新进程,前后进程id并未变化
在这里插入图片描述

从磁盘上加载一个新程序,将它加载到内存中,通过替换和页表产生映射,让子进程执行这个新的程序

替换函数

总共有7个函数,功能大致类似,但传入参数和具体使用方式有些不同

#include <unistd.h>`
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[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

函数解释

这些函数如果调用成功不再返回,调用出错返回-1
只有出错的返回值没有成功的返回值

命名理解

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

函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,须自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,须自己组装环境变量

具体应用

execl

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

path是程序的路径
arg是传入的参数,传入结束,最后必须带一个NULL

下面是执行让程序执行ls命令

  #include <stdio.h>    
  #include <unistd.h>    
      
  int main()    
  {    
      printf("当前进程的开始代码\n");    
      execl("/usr/bin/ls", "ls", NULL;                                                                   
      printf("当前进程的结束代码\n");                                                                
                                                                                                     
      return 0;                                                                                      
  }  

在这里插入图片描述
程序在执行完打印开始后,执行了ls命令,结束没有打印。程序替换为了全新的程序,所有代码都替换了。后面的都不会执行

执行ls命令时还可以带入参数,设置带颜色显示

#include <stdio.h>
#include <unistd.h>

int main()
{
    printf("当前进程的开始代码\n");
   // execl("/usr/bin/ls", "ls", NULL);
   execl("/usr/bin/ls", "ls", "-a", "--color=auto", NULL);
    printf("当前进程的结束代码\n");

    return 0;
}

在这里插入图片描述

创建子进程,用子进程来运行ls命令,父进程阻塞等待,获取退出码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id =fork();
    if (id == 0)
    {
        //子进程
        printf("子进程开始执行\n");
        execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
        exit(11);
    }
    else 
    {
        //父进程
        printf("父进程开始执行\n");
        int status = 0;
        pid_t ret = wait(-1, &status, 0);   //阻塞等待,一定是子进程执行完毕,父进程才推出
        if (ret > 0)
        {
            printf("等待成功,返回码: %d\n", WEXITSTATUS(status));
            exit(0);
        }

    }
    return 0;
}

execv

int execv(const char *path, char *const argv[]);

参数用数组

 //子进程
        printf("子进程开始执行\n");
      // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
      char* ary[10] =
      {
          "ls",
          "-a",
          "-l"
      };
      execv("/usr/bin/ls", ary);

在这里插入图片描述

execlp

int execlp(const char *file, const char *arg, …);

第一个参数只需要传入程序,会自动在环境变量中查找

 //子进程
        printf("子进程开始执行\n");
      // execlp("ls", "ls", "-a", "-l", NULL);
      char* ary[10] =
      {
          "ls",
          "-a",
          "-l"
      };
      exelp(("ls", "ls", "-a", "-l", NULL);

        exit(11);

execvp

int execvp(const char *file, char *const argv[]);

//子进程
        printf("子进程开始执行\n");
      // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
      char* ary[10] =
      {
          "ls",
          "-a",
          "-l"
      };
      execvp("/usr/bin/ls", ary);

        exit(11);

execle

int execle(const char *path, const char *arg, …,char *const envp[]);

这个函数可以传入环境变量
被执行程序可以用getenv函数获取环境变量

mycmd.c的b参数函数里加一个打印环境变量

printf(“环境变量: %s\n”, getenv(“my_env”));

这个环境变量系统里是没有的,需要通过exec函数传入。修改exec.c文件

execle(file, “mycmd”, “-b”, NULL, _env);
char* _env[6] =
{
“my_env=3455”,
NULL
};

在这里插入图片描述
环境变量具有全局属性,父进程的环境变量子进程可以继承。所以main函数的环境变量也可以直接传递给子进程,也就是env参数

execve

这个是系统调用,其他几个是对它的封装

int execve(const char *filename, char *const argv[], char *const envp[]);

makefile构建多个源文件

.PHONY:all 
all: exec mycmd 

mycmd: mycmd.c
	gcc -o $@ $^
exec: exec.c
	gcc -o $@ $^
.PHONY:clean
clean:
	rm -f exec mycmd

exec和mycmd是源文件的名字

运行自己的程序

exec运行自己写的程序,也是可以的。先写一个根据传入的参数不同执行不同功能的源文件

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        printf("传入参数多少\n");
        exit(1);
    }

    if (strcmp(argv[1], "-a") == 0)
    {
        printf("hello -a\n");
    }
    else if (strcmp(argv[1], "-b")==0)
    {
        printf("hello -b\n");
    }

    return 0;
}

然后用刚才的子进程源文件执行上面的程序,pwd获得当前路径,程序名加上全路径,用execl函数

char* file = "/home/tmp/linux/06-进程替换/mycmd";

 //子进程
        printf("子进程开始执行\n");
     execl(file, "mycmd", "-b", NULL);
      char* ary[10] =
      {
          "ls",
          "-a",
          "-l"
      };
     // execvp("/usr/bin/ls", ary);

        exit(11);

执行exec程序,显示出了hello -b
在这里插入图片描述

全路径的方式也可以换为相对路径
char* file = “./mycmd”;

运行其他语言程序

写一个简单的py程序,打印hello
在这里插入图片描述
在这里插入图片描述

再写一个shell程序,输出hello
在这里插入图片描述
在这里插入图片描述

修改exec.c的执行函数

exelp(“python”, “python”, “test.py”, NULL);

在这里插入图片描述

execlp(“bash”, “bash”, “test.sh”, NULL);

python的运行也可以给其加上可执行权限,会自动查找python执行程序
在这里插入图片描述

简易shell

vim批量替换

新建一个myshell源文件,makefile也需要修改,可以批量替换

%s/要替换的/替换为/g

需要在底行模式下

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

//shell运行原理,通过让子进程执行命令,父进程等待 解析命令
int main()
{
    char* ary[64];
    //0. 命令解释器,是一个常驻进程,不退出
    while (1)
    {
        //1. 打印提示信息 [tmp@VM-16-7-cento myshell]#
        printf("[tmp@VM-16-7-cento myshell]# ");
        fflush(stdout);
        //2. 获取用户输入的指令和选项
        char buff[1024];
        memset(buff, '\0', sizeof buff);
        if (fgets(buff, sizeof buff, stdin) == NULL)
        {
            continue;
        }

        //printf("%s\n", buff);
        //缓冲区的回车去掉
        buff[strlen(buff) - 1] = '\0';
        //3. 分割命令行,解析
        ary[0] = strtok(buff, " ");   //第一次调用,传入原始字符串
        int index = 1;
        //ls增加颜色
        if (strcmp(ary[0], "ls") == 0)
        {
            ary[index++] = "--color=auto";
        }
        while(ary[index] = strtok(NULL, " "))
        {
            index++;
        }
        //3.5 内置命令,需要父进程执行的,如cd
        if (strcmp(ary[0], "cd")==0)
        {
            if (ary[1] != NULL)
            {
                chdir(ary[1]);
                continue;
            }
        }
        pid_t id = fork();
        if (id == 0)
        {
            //4. 子进程执行任务
            execvp(ary[0], ary);
            exit(1);
        }
        //父进程等待
        int status = 0;
       pid_t ret =  waitpid(id,&status,0 );
       if (ret > 0)
       {
           printf("等待成功,退出码: %d\n", WEXITSTATUS(status));
       }
       else
       {
           printf("等到失败\n");
       }
    }
    return 0;
}

在这里插入图片描述

思路

shell执行的命令通常有两种:
1.第三方提供的对应在磁盘中有具体二进制文件的可执行程序
2.shell内部,自己实现的方法,由父进程执行

分为下面几步:
1.shell程序必须是常驻的,不能退出,所以是一个死循环
2.打印提示信息,也就是用户版本号这些东西
3.获取用户输入
4.对用户输入内容分割,第一个是命令,后面的是参数,用strtok可以分割
5.对于有些命令特殊处理。需要父进程执行的,如cd,export等,如果是ls,可以自动设置颜色。export导环境变量,其实是向系统写入环境变量的地址,所以必须用一个字符串变量存储环境变量,写入它的地址,并保证不会随意修改
6.子进程执行得到的命令
7.父进程等待,回收资源

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

//shell运行原理,通过让子进程执行命令,父进程等待 解析命令
int main()
{
    char* ary[64];
    char _env[64]; //记录环境变量
    //0. 命令解释器,是一个常驻进程,不退出
    while (1)
    {
        //1. 打印提示信息 [tmp@VM-16-7-cento myshell]#
        printf("[tmp@VM-16-7-cento myshell]# ");
        fflush(stdout);
        //2. 获取用户输入的指令和选项
        char buff[1024];
        memset(buff, '\0', sizeof buff);
        if (fgets(buff, sizeof buff, stdin) == NULL)
        {
            continue;
        }

        //printf("%s\n", buff);
        //缓冲区的回车去掉
        buff[strlen(buff) - 1] = '\0';
        //3. 分割命令行,解析
        ary[0] = strtok(buff, " ");   //第一次调用,传入原始字符串
        int index = 1;
        //ls增加颜色
        if (strcmp(ary[0], "ls") == 0)
        {
            ary[index++] = "--color=auto";
        }
        while(ary[index] = strtok(NULL, " "))
        {
            index++;
        }
        //3.5 内置命令,需要父进程执行的,如cd
        if (strcmp(ary[0], "cd")==0)
        {
            if (ary[1] != NULL)
            {
                chdir(ary[1]);
                continue;
            }
        }

        if (strcmp(ary[0], "export") == 0 && ary[1] != NULL)
        {
            strcpy(_env, ary[1]);
            putenv(_env);
            continue;
        }
        pid_t id = fork();
        if (id == 0)
        {
            //4. 子进程执行任务
            execvp(ary[0], ary);
            exit(1);
        }
        //父进程等待
        int status = 0;
       pid_t ret =  waitpid(id,&status,0 );
       if (ret > 0)
       {
           printf("等待成功,退出码: %d\n", WEXITSTATUS(status));
       }
       else
       {
           printf("等到失败\n");
       }
    }
    return 0;
}

环境变量具有全局属性,子进程替换进程后,环境变量也不会修改

shell的环境变量时写在配置文件里的,shell启动的时候,读取配置文件获得起始环境变量

函数和进程的相似性
exec/exit就像call/return
一个c程序有很多函数构成,一个函数可以调用另一个函数,传入一些参数。被调用的函数执行一个操作,返回一个值,每个函数都有他的局部变量,不同的函数通过call/return进行通信
这种通过参数和返回值在拥有私有数据的函数间通信是结构化程序设计的基础。linux鼓励这种程序之内的模式扩展到程序之间,如下图:

在这里插入图片描述

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

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

相关文章

python3高级特性

1. 装饰器 装饰器是 Python 的一种高阶函数&#xff0c;它可以在不修改函数内部代码的情况下&#xff0c;给函数增加额外的功能。 案例&#xff1a;记录函数执行时间的装饰器 import time def timing_decorator(func): def wrapper(*args, **kwargs): start_time time.t…

Spring高手之路17——动态代理的艺术与实践

文章目录 1. 背景2. JDK动态代理2.1 定义和演示2.2 不同方法分别代理2.3 熔断限流和日志监控 3. CGLIB动态代理3.1 定义和演示3.2 不同方法分别代理&#xff08;对比JDK动态代理写法&#xff09;3.3 熔断限流和日志监控&#xff08;对比JDK动态代理写法&#xff09; 4. 动态代理…

SpringBoot多模块项目整合Shiro报错No bean of type ‘org.apache.shiro.realm.Realm‘ found.

环境 依赖版本 spring-boot-dependencies 2.7.6 shiro-spring-boot 1.13.0 问题 项目启动报错 *************************** APPLICATION FAILED TO START ***************************Description:No bean of type org.apache.shiro.realm.Realm found.Action:Please …

007Node.js安装自启动工具supervisor运行js文件

在vscode中&#xff0c;某些运行中的程序修改xx.js文件后&#xff0c;通过CtrlC终止再重新运行。supervisor是自启动工具&#xff0c;会不停的查看你的文件&#xff0c;一旦发现有修改&#xff0c;就立马重新载入运行。 我们可以通过安装supervisor代替node命令运行xx.js。终端…

卷积神经网络原来是这样实现图像识别的

积神经网络原来是这样实现图像识别的 图像识别是非常有趣和具有挑战性的研究领域。本文阐述了卷积神经网络用于图像识别的概念、应用和技术。 什么是图像识别&#xff0c;为什么要使用它&#xff1f; 在机器视觉领域&#xff0c;图像识别是指软件识别人物、场景、物体、动作和图…

单例模式以及常见的两种实现模式

单例模式是校招中最常考的设计模式之一. 设计模式其实就是类似于“规章制度”&#xff0c;按照这个套路来进行操作。 单例模式能保证某个类在程序中只存在唯一 一份实例。而不会创建出多个实例&#xff0c;如果创建出了多个实例&#xff0c;就会编译报错。而不会创建出多个实…

数据库SQL语言实战(二)

目录 检索查询 题目一 题目二 题目三 题目四 题目五 题目六 题目七 题目八 题目九&#xff08;本篇最难的题目&#xff09; 分析 实现&#xff08;两种方式&#xff09; 模板 总结 检索查询 按照要求查找数据库中的数据 题目一 找出没有选修任何课程的学…

【算法刷题 | 二叉树 06】4.10( 路径总和、路径总和 || )

文章目录 13.路径总和13.1问题13.2解法一&#xff1a;递归13.2.1递归思路&#xff08;1&#xff09;确定递归函数参数以及返回值&#xff08;2&#xff09;确定终止条件&#xff08;3&#xff09;确定递归逻辑 13.2.2代码实现 14.路径总和 ||14.1问题14.2解法一&#xff1a;递归…

【设计模式】聊聊观察者设计模式原理及应用

原理 观察者模式属于行为模式&#xff0c;行为模式主要解决类和对象之间交互问题。 含义&#xff1a;在对象之间定义一个一对多的依赖&#xff0c;当一个对象状态改变时&#xff0c;所有依赖的对象会自动通知。 被依赖的对象被观察者(Observable) &#xff0c;依赖的对象观察…

2024年广东省网络系统管理样题第3套网络部署部分

2024年广东省网络系统管理样题第3套网络部署部分 模块A&#xff1a;网络构建 极安云科专注职业教育技能培训4年&#xff0c;包含信息安全管理与评估、网络系统管理、网络搭建等多个赛项及各大CTF模块培训学习服务。本团队基于赛项知识点&#xff0c;提供完整全面的系统性理论教…

欧拉回路算法

1 基本概念 1.1 欧拉路径和欧拉回路 欧拉路径&#xff1a;欧拉路是指从图中任意一个点开始到图中任意一个点结束的路径&#xff0c;并且图中每条边通过的且只通过一次。 欧拉回路:欧拉回路是指起点和终点相同的欧拉路。 注意&#xff1a;如果欧拉回路&#xff0c;那么一定存在…

基于51单片机的无线病床呼叫系统设计—LCD1602显示

基于51单片机的无线病床呼叫系统 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.病人按下按键&#xff0c;LCD1602显示对应的床位号&#xff1b; 2.多人同时呼叫&#xff0c;显示屏同时显示&#xf…

5、JVM-G1详解

G1收集器 -XX:UseG1GC G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征. G1将Java堆划分为多个大小相等的独立区域&#xff08;Region&#xff09;&#xff0c;JVM目标…

001vscode为什么设置不了中文?

VSCode中文插件安装 在VSCode中设置中文的首要步骤是安装“Chinese (Simplified) Language Pack for Visual Studio Code”扩展插件。这一过程十分简单&#xff0c;只需打开VSCode&#xff0c;进入扩展市场&#xff0c;搜索“ Chinese (Simplified) Language Pack ”然后点击…

C语言高质量编程之assert()和const

目录 编程中常见的错误 assert() const 编程中常见的错误 在编程中我们通常会遇到三种错误形式&#xff0c;分别是&#xff1a;编译型错误&#xff0c;链接型错误&#xff0c;运行时错误。 编译型错误&#xff1a; 在编译阶段发生的错误&#xff0c;绝大多数情况是由语法错误…

【Golang学习笔记】从零开始搭建一个Web框架(二)

文章目录 模块化路由前缀树路由 前情提示&#xff1a; 【Golang学习笔记】从零开始搭建一个Web框架&#xff08;一&#xff09;-CSDN博客 模块化路由 路由在kilon.go文件中导致路由和引擎交织在一起&#xff0c;如果要实现路由功能的拓展增强&#xff0c;那将会非常麻烦&…

第二期书生浦语大模型训练营第三次作业

任务一&#xff1a;在茴香豆 Web 版中创建自己领域的知识问答助手 构建个人回答助手 进入web页面&#xff0c;传输属于自己的文件&#xff0c;此处进行输入大量投资领域资料&#xff0c;构建个人投资者问答助手 回答示例 茴香豆缺陷 此处会发现茴香豆仍然存在一些缺点&#…

CF938Div3(A-F)

A: 买n个酸奶&#xff0c;一次一瓶a元,一次买两瓶可以优惠价b元,也可以a元,问恰好买n瓶需要多少钱. void solve() {int n, a, b;cin >> n >> a >> b;int ans min(a * n, n / 2 * b n % 2 * a);cout << ans << endl; } B: 给你一个数组,问能…

手把手教你安装深度学习框架PyTorch:一键式安装指南

随着人工智能和深度学习的飞速发展&#xff0c;PyTorch作为一个强大而灵活的深度学习框架&#xff0c;受到了越来越多研究者和开发者的青睐。PyTorch不仅易于上手&#xff0c;而且支持动态计算图&#xff0c;使得调试和实验变得非常方便。本文将手把手教你如何安装PyTorch&…

Spark-机器学习(1)什么是机器学习与MLlib算法库的认识

从这一系列开始&#xff0c;我会带着大家一起了解我们的机器学习&#xff0c;了解我们spark机器学习中的MLIib算法库&#xff0c;知道它大概的模型&#xff0c;熟悉并认识它。同时&#xff0c;本篇文章为个人spark免费专栏的系列文章&#xff0c;有兴趣的可以收藏关注一下&…