@bit::Shadow
✧(≖ ◡ ≖✿
目录
进程程序替换
定义:进程程序替换指使用一个新的程序 (可执行文件),替换当前进程的代码数据 栈 堆 内存段等 ,但保留 原进程的PID、某些环境及文件描述符等信息。替换完成后程序执行新的代码块,新代码块执行完成后,原代码块不再执行。
"程序"怎么理解?
一个存储在硬盘上的,静态的、可执行的指令和数据的集合,它是一个/多个死的文件。
shell内程序替换的接口
6+1
接口有6+1个前六个复用第七个,前六个作为库函数最后一个作为系统调用。
前六个:

后一个:

7者一旦返回(-1)代表执行失败。
观察发现它们的组成方式都是:"exec"+...... 参数几乎是一致的,实际上七者对应参数的功能都是一致的。
命名规律:
l(list) vs v(vector):指明命令行参数的传递方式
- l :采用参数列表的形式。像execl("usr/bin/ls", "ls", "-l", NULL)这样每个参数作为单独的参数依次列出,最后以NULL结尾。
- v:采用数组的方式。先将所有参数存储到一个指针数组内(包含NULL),然后把数组地址传递给函数。
p(path):指明可执行文件的查找方式
- 带p只需提供文件名如ls,函数会在自动环境变量PATH中搜索。
- 不带p:必须提供完整的文件路径(如:/usr/bin/ls)。
e(environment):指明环境变量的来源
- 带e:可以传入一个envp[]数组来自定义新程序的环境变量。(很少使用)
- 不带e:新程序会默认继承当前进程的环境变量。
第一个参数path/file
path:文件路径 ,使用这个路径查找可执行文件名,因此是完整的文件路径。
file:可以加路径也可以 不加路径,直接输入指令名称使得从环境变量中自动寻找指令但是要将后续的指令执行全部规范写出,不可因第一个指令而省略。
以execl()为例:
cpp
int execl(const char*path, const char* arg0, ......, (char*)0);
which与where查询位置
|---------|--------------------------------|---------------------|
| where | 所有找到的匹配项(包括别名、二进制、源码、帮助文档) | 更全面,适合查看命令的"所有存在形式" |
| which | 只输出第一个匹配的二进制文件路径 | 更简洁,只告诉你会执行哪个 |
第一个参数路径path的寻找:

由"l"推出参数列表:
运行:

运行结果:

file参数可直接写指令:

数组char* argv[]:

自定义shell命令解释器
设计导图(图片过大 无法全部呈现->smlShell仓库)

0.设置环境变量
由于无法获取系统层面的环境变量只能通过const char** environ来拷贝+putenv();
cpp
//设置环境变量
char* enVr[129];//内部存储char*类型"lll"
void EnvInit()
{
memset(enVr, 0, sizeof(enVr)); // 好的习惯
extern char** environ;//内部存储char**的数据类型environ[] "lll"
for(int i = 0;environ[i];i++)//i至多为20
{
enVr[i]=(char*)malloc(strlen(environ[i])+1);
//enVr[i]=(char*)malloc(sizeof(strlen(environ[i])+1));
strcpy(enVr[i],environ[i]);
putenv(enVr[i]);
}
}
1.命令行提示符
char* getenv(const char* EnvmVar); // 参数为环境变量名称 返回对应内容
cpp
//命令行
void CoutCommandLine()
{
std::cout << "[" << getenv("USER") \
<< "@" << getenv("HOSTNAME") << " " << getenv("PWD") << "]";
if(strcmp(getenv("USER"),"root")==0)
std::cout << "# ";//err当相等时返回0
else std::cout << "$ ";
}
2.获取用户输入
strtok()字符串截取函数
cpp
#include <string.h>
char *strtok(char *str, const char *delim);
char* strtok(char* str, const char* delim); // 用于分割字符串,delim为分割单位
具体功能:将原串delim处变作'\0',并返回分割得到的子串。
若想要多次分割(对同一字符串),第一个参数必须传入NULL。
例:
cpp
#include <stdio.h>
#include <string.h>
int main() {
char path[] = "/home/user/documents/file.txt";
// 分割路径
char *token = strtok(path, "/");
while (token != NULL) {
printf("目录/文件: %s\n", token);
token = strtok(NULL, "/");
}
return 0;
}
执行结果:
目录/文件: home
目录/文件: user
目录/文件: documents
目录/文件: file.txt
myshell模块功能实现:
cpp
//指令解析
void CommandParsed()
{
//std::cout << CommandLine ;
//1.不接纳数组传参的直接淘汰 ,
//2.file和path选file无需位置指定
//3.无需传环境变量所以选择
//int execvp(const char* file, char *const argv[])
//第一个命令的截取 若一个命令由多部分组成该怎么办?怎么区分指令与选项
int i = 0;
CommandStr[i++]=strtok(CommandLine," ");//自动将分隔符替换为'\0'
//截取剩余选项的主逻辑
while((bool)(CommandStr[i++]=strtok(NULL," ")));
}
3.指令执行(可优化封装)
strcmp()
cpp
#include <string.h>
int strcmp(const char *str1, const char *str2);
int strcmp(const char* str1, const char* str2);
// 字符串比较 函数,字符串 相等返回0。 // 注意:由于返回0所以if(!strcmp(str1, str2)) ;
strcpy()
cpp
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:将src复制到dest
dest要求:
| 要求 | 说明 | 违反后果 |
|---|---|---|
| 有效的内存地址 | 不能是 NULL 或野指针 | 段错误,程序崩溃 |
| 可写内存 | 不能是只读内存(如字符串常量) | 段错误或未定义行为 |
| 足够的空间 | 至少 strlen(src) + 1 字节 |
缓冲区溢出,安全漏洞 |
| 已分配空间 | 不能是未初始化的指针 | 未定义行为,崩溃 |
strcat()
对dest要求可以找到'\0'。
cpp
#include <string.h>
char *strcat(char *dest, const char *src);
☆☆☆snprintf()
作为字符串拷贝转移最安全的函数
cpp
#include<stdio.h>
int snprintf(char* str, size_t size, const char* format, ...);
参数
-
str:目标缓冲区 -
size:缓冲区大小(字节数) -
format:格式化字符串 -
...:可变参数
返回值:
- 成功时:返回应该写入的字符数(不包括'\0')。
- 失败时:返回负数。
关键特性:
-
✅ 自动添加
'\0'结尾 -
✅ 不会溢出 (超过
size-1的内容被截断) -
✅ 可替代
strcpy和strcat
使用实例:
cpp
#include <stdio.h>
int main()
{
char buffer[20];
// 基本使用
snprintf(buffer, sizeof(buffer), "Hello");
printf("%s\n", buffer); // Hello
}
myshell指令执行功能
cpp
//指令执行
void CommandRun()
{
//"ls" "ls" "-a" "-l" NULL
if(strcmp(CommandStr[0],"cd")==0)
{
if(!strcmp(CommandStr[1],getenv("HOME")))
strcpy(getenv("PWD"),getenv("HOME"));
else
strcpy(getenv("PWD"),CommandStr[1]);
}
else if(strcmp(CommandStr[0],"echo")==0)
{
//echo "字符串" 环境变量/本地变量 $?
std::string s = CommandStr[1];
//std::cout<<s;
if(s=="$?")
{
std::cout << exitCode << std::endl;
}
else if(s[0]=='$')
{
//CommandStr = "$PATH"
std::string s1 = s.substr(1);
std::cout << getenv(s1.c_str()) << std::endl;
}
else{
std::cout << s << std::endl;
}
}
else if(strcmp(CommandStr[0],"env")==0)
{
for(int i =0;enVr[i];i++)
std::cout << enVr[i] << std::endl;
std::cout << std::endl << std::endl;
}
else
execvp(CommandStr[0],CommandStr);
}
感谢观看
多多关注
