当前位置: 首页 > news >正文

Linux学习笔记(十)--进程替换与创建一个自己的简易版shell

进程替换:进程替换是指在当前进程的上下文中完全替换为执行另一个程序的过程。在Unix/Linux系统中,这通过exec系列函数实现。

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

exec系列函数:(l表示list列表,p表示path,e表示environment,v可以理解成vector数组)

函数

描述

参数格式

是否使用PATH

execl

参数列表

execl(path, arg0, arg1, ..., (char *)NULL)

execlp

参数列表,使用PATH

execlp(file, arg0, arg1, ..., (char *)NULL)

execle

参数列表,自定义环境

execle(path, arg0, arg1, ..., (char *)NULL, envp)

execv

参数数组

execv(path, argv)

execvp

参数数组,使用PATH

execvp(file, argv)

execvpe

参数数组,自定义环境

execvpe(file, argv, envp)

在shell中,所有进程都是bash的子进程,所有命令的启动都依赖于exec*系列的函数,exec*承担的是一个加载器的效果。

execl函数:

函数原型:

#include <unistd.h>int execl(const char *path, const char *arg0, ..., (char *) NULL);

参数:

path​​: 要执行的可执行文件的完整路径

arg0​​: 第一个参数通常是程序名称本身

..​​: 后续参数列表,以 (char *) NULL结束

返回值:(1)成功,不返回,进程已被替换(2)失败,返回-1,并设置error

execl函数从第二个参数开始,在命令行当中是怎么写的,就把参数依次传递给execl,只不过把空格替换成",",所有的exec系列的函数的第一个参数就是执行这个方法的位置,后面的参数用来如何执行程序(要不要涵盖选项,涵盖哪些选项)

#include <unistd.h>
#include <stdio.h>int main() {printf("准备执行ls命令...\n");// 参数列表必须以NULL结束execl("/bin/ls", "ls", "-l", "-a", NULL);// 如果exec成功,下面的代码不会执行perror("exec失败");return 1;
}

execlp函数:相比于execl多了个p,会在自己默认的path环境变量中查找。

#include <unistd.h>int execlp(const char *file, const char *arg0, ..., (char *) NULL);

参数:

ile​​: 要执行的文件名(不需要完整路径)

​​arg0​​: 第一个参数通常是程序名称本身

​​...​​: 后续参数列表,必须以 (char *) NULL结束

返回值与execl相同。

execlp("ls", "ls", "-l", "-a", NULL);

execlp中的两个ls有什么区别?
第一个ls : 要执行的程序名这是  execlp  函数的第一个参数。它的作用是告诉系统 “我要执行哪个程序”。execlp  函数会根据这个名称,在系统的  PATH  环境变量所指定的目录中搜索名为  ls  的可执行文件。
第二个ls: 传递给程序的第一个命令行参数,这是  execlp  函数的第二个参数。它的作用是作为命令行参数传递给即将被执行的  ls  程序。对于  ls  命令来说,这通常是它要操作的对象(比如文件或目录名),或者是一个选项标志(如  -l ,  -a )。在您的截图中,这个参数就是  ls  命令本身要执行时接收到的第一个参数。所以第二个ls通常可以任意更改,因为它对应的是argv[0]  通常被设置为程序自身的名称。所以我们在例子中看到的是  "ls" 。execv函数:

#include <unistd.h>int execv(const char *path, char *const argv[]);

参数:

​​path​​: 要执行的可执行文件的完整路径

​​argv​​: 参数数组,其中:(1)argv[0]通常是程序名称本身(2)最后一个元素必须是 NULL指针

返回值与execl相同。

示例:

#include <unistd.h>
#include <stdio.h>int main() {char *args[] = {"ls", "-l", "-a", NULL};printf("准备执行ls命令...\n");execv("/bin/ls", args);perror("execv失败");return 1;
}

execve:(e表示环境变量)

#include <unistd.h>int execve(const char *pathname, char *const argv[], char *const envp[]);

参数说明:

pathname​​: 要执行的可执行文件的完整路径

​​argv​​: 参数数组,格式与 execv()相同

envp​​(环境变量数组):(1)格式为 "VARNAME=value"的字符串(2)必须以 NULL指针结束

返回值与execl相同。

#include <unistd.h>
#include <stdio.h>int main() {char *args[] = {"ls", "-l", NULL};char *env[] = {"PATH=/bin:/usr/bin", "TERM=xterm-256color", NULL};printf("准备执行ls命令...\n");execve("/bin/ls", args, env);perror("execve失败");return 1;
}

我们知道,在makefile中在编译执行可执行文件时,哪个文件在前面就形成哪一个文件的可执行文件,那么我们如何用一个makefile一次形成两个可执行文件?我们这个时候就需要创立一个伪目标文件all。如下代码所示:

.PHONY: all
all: otherExe mycommandotherExe: otherExe.cppg++ -o $@ $^ -std=c++11mycommand: mycommand.cgcc -o $@ $^ -std=c99.PHONY: clean
clean:rm -f mycommand otherExe

作用:在一个可执行程序中调用另一个可执行程序。

所以结合我们所学知识,我们可创建一个属于自己的简易版shell。

要写一个shell,需要遵守以下循环:

1. 获取命令行

2. 解析命令行

3. 建立一个子进程(fork)

4. 替换子进程(execvp)

5. 父进程等待子进程退出(wait)

根据这些思路,和前面所学到的知识,我们就可以自己创建一个简易版的shell

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <ctype.h>#define MAX_CMD 1024
char command[MAX_CMD];int do_face()
{memset(command, 0x00, MAX_CMD);printf("minishell$ ");fflush(stdout);if (scanf("%[^\n]%*c", command) == 0) {getchar();return -1;}return 0;
}char **do_parse(char *buff)
{int argc = 0;static char *argv[32];char *ptr = buff;while (*ptr != '\0') {if (!isspace(*ptr)) {argv[argc++] = ptr;while ((!isspace(*ptr)) && (*ptr != '\0')) {ptr++;}if (*ptr == '\0') {break;}*ptr = '\0';ptr++;} else {ptr++;}}argv[argc] = NULL;return argv;
}int do_exec(char *buff)
{char **argv = NULL;pid_t pid = fork();if (pid == 0) {argv = do_parse(buff);if (argv[0] == NULL) {exit(-1);}execvp(argv[0], argv);perror("execvp");exit(-1);} else if (pid > 0) {waitpid(pid, NULL, 0);} else {perror("fork");return -1;}return 0;
}int main(int argc, char *argv[])
{while (1) {if (do_face() < 0) {continue;}do_exec(command);}return 0;
}

http://www.dtcms.com/a/511037.html

相关文章:

  • go语言实现 基于 Session 和 Redis 实现短信验证码登录
  • 福建网站建设制作阿里巴巴旗下跨境电商平台有哪些
  • 潇洒郎:最佳完美——Windows防火墙与端口管理工具——支持ipv6、ipv4端口转发管理
  • Elastic MCP 服务器:向任何 AI agent 暴露 Agent Builder 工具
  • 小说网站建设详细流程游戏开发有前途吗
  • echarts tooltip数据太长导致显示不全
  • 用户选剧情,AI写故事:Trae Solo+GLM-4.6实现沉浸式小说创作体验
  • 【Linux】初始Linux和Linux下基本指令:ls pwd cd touch mkdir rmdir rm 指令
  • 《Linux系统编程之入门基础》【Linux基础 理论+命令】(下)
  • 农业网站建设招标书网站导航条内容
  • LLAMA Factory 微调Qwen2.0-VL-2B视觉大模型
  • 婚纱网站建设案例wordpress默认主题twenty
  • 网站访问者qq企业网站备案名称窍门
  • 个人网站源码下载有口皆碑的域名备案加急
  • 农用地转建设用地结果查询网站苏州品牌网站设计
  • 做外贸的网站怎么建立wordpress建网站主页
  • 建设部网站中天人建筑工程有限公司网站设计是怎么做的
  • 网站建设管理工作经验介绍建美食网站有哪些原因
  • 电子商务网站后台功能wordpress thesis 开发
  • 自已做的网站怎么做域名解析沐众科技网站建设
  • 一款app开发需要多少钱郑州seo网络营销技术
  • 做动漫的网站合肥seo公司
  • 免费商城网站制作网站建设制度
  • 网站开发个人博客深圳企业年报网上申报入口
  • 企业营销型网站的内容网站开发设计总结
  • 深圳加盟网站建设wordpress路由正则
  • 陵水网站建设费用深圳还有网站
  • 网站建设的七夕文案国家免费职业培训平台
  • 用帝国cms做门户网站找客户资源的软件
  • 成都网站建设常见问题网站设置英文怎么说