进程的程序替换exec*函数和shell实现

ops/2024/11/18 1:27:12/

进程程序替换函数:execl - - - 让进程使用 exec 函数把自己得到代码和程序替换掉,执行新的程序*。不会直接更改原进程PCB页表等,只会把原进程PCB原本所映射页表映射到的物理内存的代码和数据覆盖成 execl 所填写的程序数据。只要替换成功,原本代码之后的就不会运行,替换失败了才会向后运行。
也就是 利用老程序的壳子,覆盖成新程序的内容。exec* 类似于一种 linux 中的加载函数。
在这里插入图片描述

使用如下:当运行自己写的进程时,跑出来的时 ls 命令,而且test end… 也没有运行。
在这里插入图片描述

当程序被替换,自己的后面的代码不能运行,就可以利用fork函数创建子进程,让子进程去替换,父进程wait等待子进程就可以全部运行了。
在这里插入图片描述
创建子进程目的是让子进程完成任务:1.子进程执行代码的一部分 2.让子进程完成一个全新的程序,只要进程程序替换成功,代码和数据都会写时拷贝一个空间让子进程去覆盖重新开空间去让子进程执行。

exec* 函数出错,返回值为-1,错误码(errno)被设置。成功没有返回值。

execl(想执行程序的路径,怎样执行这个程序,… ,NULL结尾)
l:在命令行怎样调程序,则传参数怎样传。
在这里插入图片描述

execv(想执行程序的路径,需要执行的命令以Vector传入)
在这里插入图片描述

execvp(不用传路径 传程序名 系统在PATH路径中查找这个程序,需要执行的命令以Vector传入),execlp同理。
在这里插入图片描述

C的子程序调用执行程序,不是创建新的进程,而是改变代码和数据,进程pid不会变。下面的C调用C++就是例子。
在这里插入图片描述

execvpe(直接传文件名,传数组里面存放需要调用的程序名,传环境变量)
e:传入环境变量后有三种情景
1.直接全部替换自己写的环境变量
2.传入父进程的父进程的环境变量,也就是bash给的环境变量
3.把bash给的环境变量改一改传入,putenv(XXX) - - - 把XXX环境变量添加到bash的环境变量中,然后传bash环境变量时就会有XXX这个环境变量。
eg:在testexec中调用mytest.cc函数,使用execvpe传的三个参数就给到了.cc函数的main函数的参数列表中,分别就是argc,argv,env三个参数。
在这里插入图片描述
真正的系统调用函数是 execve,而上面的进程替换不是系统调用,而是3号手册的C函数取调用 execve 函数。

自己写一个shell:
char * getenv(“XXX”) - - - 得到环境变量中名字是XXX的环境变量对应的值。
snprintf(A,B,“C”) - - - 把C中的东西写进A中,A的大小为B,B就是sizeof(A)
fflush(stdout) - - - 标准输出,冲刷缓冲区显示到显示器。
fgets(A,B,FILE* stream) - - - 按行从特定的文件流中获取内容填到A中,A的大小为B。为了得到一行内容空格不做分离符。
eg:char* s=fgets(A, sizeof(A), stdin) - - - 从标准输入中读取一行类容填写到A中。
strtok(str, const char* “b”) - - - 把字符串str,按照b字符去分割,返回值是从左向右分割的第一个子串,只需要第一次调用时传str,最后传NULL,就可以分割完。一般是先赋值再分割,只有这样可以让str最后一个元素无法分割时,把值先给出去了,而不是返回的NULL。
chdir(const char*path) - - - 更改当前的工作路径
putenv() - - - 导入环境变量
getcwd - - - 获取当前的绝对路径

#define SkipPath§ do{p += (strlen§-1); while(*p !=‘/’) p–;}while(0)
do{ … }while(0) - - - 在宏函数中,这个东西可以让宏函数在调用的时候更像是一个函数,调用宏函数时可以随意在后面加 ; ,不会带来影响。

linux代码如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{p += (strlen(p)-1); while(*p !='/') p--;}while(0)const char *GetUserName() //获取用户名
{const char*name = getenv("USER");//getenv("XXX") 得到XXX对应的环境变量if(name == NULL) return "None";return name;
}
const char *GetHostName()//获取主机名
{const char*hostname = getenv("HOSTNAME");if(hostname == NULL) return "None";return hostname;
}
const char *GetCwd()//获取路径名
{const char*cwd=getenv("PWD");if(cwd == NULL) return "None";return cwd;
}// 1
void MakeCommandLineAndPrient()//得到命令行并打到屏幕
{char line[SIZE];const char*username=GetUserName();const char*hostname=GetHostName();const char*cwd=GetCwd();SkipPath(cwd);snprintf(line,sizeof(line),"[%s@%s %s]>",username,hostname,strlen(cwd) == 1 ? "/" : cwd+1);printf("%s",line);fflush(stdout);
}//2
char *gArgv[NUM]; //存放分割出来的命令字符串
void SplitCommand(char command[],int size)
{// "ls -a -l"-> "ls" "-a" "-l"gArgv[0] = strtok(command,SEP);int index=1;while((gArgv[index++]=strtok(NULL,SEP)));
}
int GetUserCommand(char command[], int n)
{char *s = fgets(command, n, stdin);if(s == NULL) return 1;command[strlen(command)-1]=ZERO;return strlen(command);
}int lastcode =0;
// 4
char cwd[SIZE*2];
const char* GetHome()
{const char *home = getenv("HOME");return home;
}
void Cd()
{const char* path = gArgv[1];if(path == NULL) path = GetHome();// cd 后面一定有东西chdir(path); //走到这里说明是cd命令,路径会变化因此得更新pwd// 刷新环境变量char temp[SIZE*2];getcwd(temp, sizeof(temp));snprintf(cwd, sizeof(cwd), "PWD=%s", temp);putenv(cwd);}
int CheckBuildin()
{int yes = 0;const char* enter_cmd=gArgv[0];if(strcmp(enter_cmd, "cd") == 0){yes = 1;Cd();}else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1],"$?") == 0){yes = 1;printf("%d\n", lastcode);lastcode = 0;}return yes; 
}//5 
void ExecuteCommand()//运行程序
{pid_t id = fork();if(id < 0){exit(-1);}else if(id == 0) //子进程{execvp(gArgv[0],gArgv);exit(errno); //如果程序替换失败就会有返回码}else {int status =0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);if(lastcode != 0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);}}
}
int main()
{int quit=0;while(!quit){//1.输出一个命令行,需要得到用户名,主机名,当前目录MakeCommandLineAndPrient();//2.输入命令,获取命令字符串存到usercommandchar usercommand[SIZE];int n = GetUserCommand(usercommand, sizeof(usercommand));(void)n;if(n<=0) return 1;//3.命令行字符串分割 SplitCommand(usercommand,sizeof(usercommand));//4.检测命令是否是内建命令n = CheckBuildin();if(n) continue; //如果n是1 为真 说明是内建命令,则在Cd已经执行过了//5.执行命令 ExecuteCommand(); }return 0;
}

基本分析如下图
在这里插入图片描述


http://www.ppmy.cn/ops/134589.html

相关文章

深入解析贪心算法及其应用实例

标题&#xff1a;深入解析贪心算法及其应用实例 一、引言 贪心算法&#xff08;Greedy Algorithm&#xff09;是一类简单、直观的算法设计策略&#xff0c;广泛应用于优化问题中。其基本思想是每一步都选择当前状态下最优的选择&#xff0c;即在每一步做出局部最优的决策&…

lua实现雪花算法

lua实现雪花算法 雪花算法介绍组成部分优点缺点 代码示例 雪花算法介绍 雪花算法&#xff08;Snowflake Algorithm&#xff09;是一种用于生成唯一ID的分布式生成算法&#xff0c;最初由Twitter开发。它的主要目的是在分布式系统中生成唯一的、时间有序的ID&#xff0c;这些ID通…

【IEEE出版 | 中国石油大学(华东)主办】第六届信息与计算机前沿术国际学术会议(ICFTIC 2024,12月13-15日)

第六届信息与计算机前沿术国际学术会议(ICFTIC 2024) 2024 6th International Conference on Frontier Technologies of Information and Computer 官方信息 会议官网&#xff1a;WWW.ICFTIC.ORG 2024 6th International Conference on Frontier Technologies of Information…

Python爬虫项目 | 一、网易云音乐热歌榜歌曲

文章目录 1.文章概要1.1 实现方法1.2 实现代码1.3 最终效果 2.具体讲解2.1 使用的Python库2.2 代码说明2.2.1 创建目录保存文件2.2.2 爬取网易云音乐热歌榜单歌曲 2.3 过程展示 3 总结 1.文章概要 学习Python爬虫知识&#xff0c;实现简单的一个小案例&#xff0c;网易云音乐热…

【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码解析 目录 一、经典的组建家庭影院流程 二、传统方式解决影院管理 2.1 实现方案&#xff1a;客户端直接调用各流程 2.2 …

css鼠标移动效果高亮追随效果

如图所示&#xff0c;鼠标移动有一块高亮随着鼠标移动。代码如下&#xff1a;(vue3篇) <div class"container"><span class"use-hover-hglh-element trail" :style"isShow ? dyStyle : { opacity: 0 }"></span></div>…

【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数

CL13 最大的数(20 分) 输入一个有 n 个无重复元素的整数数组 a&#xff0c;输出数组中最大的数。提示&#xff1a;如使用排序库函数 sort()&#xff0c;需要包含头文件#include 。输入&#xff1a; 第一行是一个正整数 n(2<n<20)&#xff1b; 第二行包含 n 个不重复的整…

JWT深度解析:Java Web中的安全传输与身份验证

标题&#xff1a;JWT深度解析&#xff1a;Java Web中的安全传输与身份验证 引言 JSON Web Token&#xff08;JWT&#xff09;是一种轻量级的身份验证和授权标准&#xff0c;它允许在各方之间安全地传输信息。在Java Web开发中&#xff0c;JWT因其无状态、可扩展性和跨域支持而…