【Linux】进程控制之进程程序替换

目录

前言

替换的原理

替换函数

记忆技巧

函数使用

execl

execlp

execv

execvp 

execle

execvpe 

调用其它语言的程序

模拟实现一个shell


前言

关于本文可以先去看看上一篇【Linux】进程控制详解-CSDN博客可以更好的理解这里的内容

学完本篇文章,你就可以自己设计一个mini版的shell解释器,还可以用你写自己的代码区执行其它语言的程序。

替换的原理

用fork创建子进程后执行的是和父进程相同的代码,但有可能需要执行不同的代码分支,那么子进程往往要调用一种exec系列函数以执行另一个全新程序。当进程调用一种exec系列函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec系列函数并不创建新进程,所以调用exec系列函数前后该进程的ID并未改变。

替换函数

返回值:如果调用成功则加载新的程序从启动代码开始执行,不在返回;调用失败则返回-1。

记忆技巧

  • l(list):表示参数采用列表
  • v(vector):表示参数采用数组
  • p(path):第一个参数path不用输入路径,给出命令名即可,它会在环境变量PATH当中搜索对应的命令
  • e(env):将自己维护的环境变量传递给需要替换的进程
函数名参数格式是否带路径是否使用当前环境变量
execl列表
execlp列表
execle列表否,需自己维护环境变量
execv数组
execvp数组
execvpe数组否,需自己维护环境变量

函数使用

一旦发生了替换,那么替换函数后面的代码就不会再执行了。

int main()
{
  printf("当前进程的开始代码\n");
  execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
  printf("当前进程的结束代码\n");
  return 0:
}

 

注:在调用替换函数时末尾最好加上NULL代表结束。

虽然我们可以不用创建子进程来使用替换函数,但是我们创建了子进程,替换的进程就是子进程而父进程不受影响,那么父进程就可以聚焦在读取数据,解析数据,指派进程执行代码等功能了。 

execl

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

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

 

execlp

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

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execlp("ls", "ls", "-a", "-l", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execv

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

#define NUM 16

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    char* const _argv[NUM] = {
      (char*)"ls",
      (char*)"-a",
      (char*)"-l",
      NULL 
    };
    execv("/usr/bin/ls", _argv);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execvp 

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

#define NUM 16

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    //execlp("ls", "ls", "-a", "-l", NULL);
    char* const _argv[NUM] = {
      (char*)"ls",
      (char*)"-a",
      (char*)"-l",
      NULL 
    };
    execvp("ls", _argv);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

execle

 mycmd.c

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

int main(int argc, char* argv[])
{
  if(argc != 2)
  {
    printf("can not execute\n");
    exit(1);
  }

  printf("获取环境变量:MY_VALUE:%s\n", getenv("MA_VALUE"));
  if(strcmp(argv[1], "-a") == 0)
  {
    printf("hello 我是a\n");
  }
  else if(strcmp(argv[1], "-b") == 0)
  {
    printf("hello 我是b\n");
  }
  else 
  {
    printf("defalut\n");
  }
  return 0;
}

myproc.c 

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

#define NUM 16
const char* myfile = "/home/hjx/for_linuxtest/test43/mycmd";

int main(int argc, char* argv[], char* env[])
{

  char* const _env[NUM] = {
    (char*)"MY_VALUE=332335454",
    NULL 
  };
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execle(myfile, "mycmd", "-a", NULL, _env);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

 

所以mycmd.c就拿到了这里的环境变量

execvpe 

和上面的类似就不再演示了

 其实系统调用的接口只有一个——execve

而以上介绍的函数是操作系统是为了满足不同的调用场景提供的基本封装。

调用其它语言的程序

print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
print("hello python")
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    exit(2);//子进程创建失败
  }
  else if(id == 0)
  {
    //子进程
    printf("子进程开始执行,pid:%d\n", getpid());
    execlp("python", "python", "test.py", NULL);
    exit(1);//替换失败则终止进程
  }
  else 
  {
    //父进程
    printf("父进程开始执行,pid:%d\n", getpid());
    int status;
    pid_t wid = waitpid(-1, &status, 0);//阻塞等待
    if(wid > 0)
    {
      printf("wait success, exit code:%d\n", WEXITSTATUS(status));
    }
  }
  return 0;
}

模拟实现一个shell

有了上面的这些知识,那么我们可以自己设计一个简易版的shell。

shell代码链接:minishell

效果展示


今天的分享就到这里了,如果内容有错的话,还望指出,谢谢!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux操作系统】,Linux运维面试自我介绍

16、右击创建好的虚拟机&#xff0c;打开设置 17、添加自己的CentOS系统文件 18、开启虚拟机 19、使用方向键选择&#xff0c;enter键确定 20、选择中文 21、设置好系统时间以及语言&#xff0c;点击“安装位置” 22、选择自定义分区 23、点击“”&#xff0c;定义分区 24、设置…

jdbc工具类

jdbc 工具类&#xff0c;具体见下面代码&#xff0c;直接可以用。 /*** version 1.0* descpription: jdbc工具类* date 2024/4/6*/ public class JDBCUtils {private static final String URL "jdbc:mysql://127.0.0.1:3306/mybatis";private static final String …

InnoDB中的索引方案

文章目录 InnoDB中的索引方案 InnoDB支持多种类型的索引&#xff0c;包括B-tree索引、全文索引、哈希索引等。B-tree索引是InnoDB存储引擎的默认索引类型&#xff0c;适用于所有的数据类型&#xff0c;包括字符串、数字和日期等。 以下是创建InnoDB表及其B-tree索引的示例代码…

用一个程序解决SQLite常见的各项操作(实用篇)

文章说明&#xff1a; 本篇文章是在之前的一篇文章SQLite3进行数据库各项常用操作基础上写的&#xff0c;将SQLite涉及到的常用的几种操作&#xff0c;以函数的形式处理成相互调用的形式。 因为之前的文章对基础操作已经解释过了&#xff0c;所以这里直接放置可执行代码和结果…

Upload file to cloud server

Procedure is trifle&#xff0c;concentrate on current affair. Via js step1: book a website Official guidebook https://support.huaweicloud.com/usermanual-cloudsite/cloudsite_01_4140.html 流程如下: 为什么这么难&#xff0c;死活不知道怎么备案 /(ㄒoㄒ)/~~

Linux:数据链路层

文章目录 路由表数据链路层分片mac帧报头ARP协议ARP的周边话题 路由表 当主机a想要发送消息到主机b&#xff0c;这一整个过程中&#xff0c;数据报文在进行传输的过程实际上是一跳一跳的过去的&#xff0c;而报文可能会经过公网进行传递&#xff0c;本质上这些网络都是靠对应的…

使用vue开发的前后台框架推荐

对于Vue后台前台框架&#xff0c;以下是几个值得推荐的选项&#xff1a; Element UI&#xff1a;一个基于Vue.js的桌面端组件库&#xff0c;提供了丰富的UI组件和交互方式&#xff0c;非常适合构建后台管理系统。 Element UI是一套为开发者、设计师和产品经理准备的基于Vue 2.…

【计算机毕业设计】五台山景点购票系统,后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

LeetCode-热题100:118. 杨辉三角

题目描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1]]…

什么是SYN攻击,有什么办法防御SYN攻击

自进入数字化互联网时代&#xff0c;网络技术给我们带来了许多服务&#xff0c;为人们的生活增添了许多便利。但同时&#xff0c;网络安全问题也日益凸显&#xff0c;其中DDoS攻击&#xff0c;即分布式拒绝服务攻击&#xff0c;已经成为一种常见的网络威胁。这种攻击方式通过控…

anaconda虚拟环境安装apex0.1教程win10

我安装apex0.1的环境是&#xff1a;torch&#xff08;gpu&#xff09;1.8.0&#xff0c;cuda10.2&#xff0c;cuda7.6.5。 第一步&#xff1a;下载对应的pytorch、cuda、cudnn版本 这里就不详细介绍了&#xff0c;具体可以参考我的这篇博文win10中anaconda创建虚拟环境配置py…

「 典型安全漏洞系列 」10.跨域资源共享CORS漏洞详解

跨域资源共享&#xff08;Cross-origin Resource Sharing&#xff0c;CORS&#xff09;是一种浏览器机制&#xff0c;可以对于给定域之外的资源进行受控访问。它扩展并增加了同源政策&#xff08;Same-origin Policy&#xff0c;SOP&#xff09;的灵活性。然而&#xff0c;如果…

知识融合与消歧:完善知识图谱的关键步骤

知识融合与消歧&#xff1a;完善知识图谱的关键步骤 一、引言&#xff1a;知识融合与消歧的重要性 在今天的数据驱动时代&#xff0c;知识图谱已成为组织和理解海量信息的关键技术。它们使得复杂的数据关系可视化&#xff0c;为人工智能提供了丰富的知识基础。然而&#xff0c…

Qt | 元对象系统

一、QByteArray 类简介 1、QByteArray 类简介  该类是一个用于处理字符串的类似于 C++的 string 类型的类,在 Qt 中,对字符串的处理,经常使用的是 QString 类,该类保证字符串以\0结尾,并使用隐式共享(copy-on-write)来减少内存用量和不必要的数据复制。  QByteArra…

【智能算法】原子搜索算法(ASO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2019年&#xff0c;Zhao等人受到原子运动规律启发&#xff0c;提出了原子搜索算法&#xff08;Atom Search Algorithm&#xff0c;ASO&#xff09;。 2.算法原理 2.1算法思想 ASO根据分子动力学中…

Qt学习记录(C++)——Day 2

目录 一、作业 要求&#xff1a; 实现&#xff1a; 1.创建新的窗口类 2. 主窗口中实现 二、 窗口菜单设计 效果展示图 三、图片资源的导入 步骤&#xff1a; 举例&#xff1a; 四、 对话框 1.模拟对话框 2. 非模态对话框 3.错误对话框 4.信息对话框 5.提问对话…

【C++】缺省参数和函数重载

目录 1.缺省参数 1.1缺省参数的定义 1.2 缺省参数的简单应用 1.3 缺省参数分类&#xff1a;全缺省参数和半缺省参数 1.3.1半缺省参数 1.3.2全缺省参数 3.缺省参数注意事项&#xff1a;缺省参数不能在函数声明和定义中同时出现 4.函数重载 4.1 函数重载概念 4.2 函数参数类型…

matlab使用教程(35)—求解时滞微分方程(3)

1中立型 DDE 以下示例说明如何使用 ddensd 求解中立型 DDE&#xff08;时滞微分方程&#xff09;&#xff0c;其中时滞出现在导数项中。此问题最初由 Paul [1] 提出。方程是&#xff1a; 由于该方程在 y ′ 项中存在时滞&#xff0c;因此该方程称为中立型 DDE。如果时滞仅出现…

基于SSM的基于个人需求和地域特色的外卖推荐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的基于个人需求和地域特色的外卖推荐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

10 - 三态门和寄存器

1. 三态门 1.1 引入背景 现在我们需要封装一个单字节存储器。 Clear 清零&#xff0c;Pre 置 1&#xff0c;CP 的上升沿将输入 DI 数据写入。 问题&#xff1a;总线冲突。如果将多个存储器连到一起&#xff0c;就会出现一个输入连到多个输出发生数据冲突的问题。 需要三态门…