操作系统之shell实现(下)

🌟 各位看官好,我是maomi_9526

🌍 种一棵树最好是十年前,其次是现在!

🚀 今天来学习C语言的相关知识。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

目录

1. 进程程序替换

2.exec函数

2.1 execl 

2.2 execlp 

2.3 execle 

2.4 execv 

2.5 execvp 

2.6 execvpe 

 2.7execve

2.8命名理解

3.进程替换

3.1进程替换原理

4. 自主Shell命令行解释器

4.1获取当前环境信息

4.2输出命令行提示符

4.3获取命令行输入

4.4执行命令行

4.4.1执行内建命令 

4.4.2执行外部命令

4.5更新环境变量

3. Shell 实现完整代码


1. 进程程序替换

  • fork() 系统调用创建一个子进程,父子进程开始执行相同的程序代码。若子进程要执行一个不同的程序,可以使用 exec 系列函数来实现程序的替换。

  • 这些 exec 函数会加载一个全新的程序(包括代码和数据)到子进程的地址空间中,并从新程序的入口点开始执行,原有的程序代码被替换掉。exec 函数系列中最常用的是 execve,其他的 execl, execlp, execv, execvp, execle 等只是 execve 的不同封装。

2.exec函数

头文件:#include<unistd.h>

返回值:当失败时返回-1

2.1 execl 

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

execl("/usr/bin/ls","ls","-l",NULL);
2.2 execlp 

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

execlp("ls","ls","-l",NULL);
2.3 execle 

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

extern char**environ;//声明全局环境变量
execle("/usr/bin/ls","1s","-l","-a",NULL,environ};
2.4 execv 

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

char*argv[]={"1s","-l","-a",NULL};execv("/usr/bin/ls",argv);
2.5 execvp 

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

char*argv[]={"1s","-l","-a",NULL};execvp("ls",argv);
2.6 execvpe 

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


char*argv[]={"ls","-a","-l",NULL};
execvpe("ls",argv,environ);
     2.7execve

    系统调用函数execve

    上面的exec系列函数本质上都不是系统级别的调用,都是对execve的语言级别的封装

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

     

    2.8命名理解
    • l(list) : 表示参数采用列表
    • v(vector) : 参数用数组
    • p(path) : 有p自动搜索环境变量PATH
    • e(env) : 表示自己维护环境变量
    函数级别函数名列表传参是否带路径是否使用当前环境变量
    语言级别execl列表
    execlp列表
    execle列表
    execv数组
    execvp数组
    execvpe数组
    系统级别execve数组

    3.进程替换

    3.1进程替换原理

    当进程执行了代码替换操作后,原先加载的代码会被新的代码所替换。

    此时,原有的代码不再存在于进程的地址空间中,执行流转向新的代码。具体来说,在进程替换时,原代码的内存空间被新的代码段覆盖,新的代码开始运行。此过程的本质是将进程的代码区域替换为新的内容,从而导致原有代码失效并不可再访问。

    所以原来代码我的进程执行完毕并不会出现。 

    4. 自主Shell命令行解释器

    • 通过实现一个自定义的 shell,可以处理命令行输入,并根据输入执行对应的命令。Shell 需要有以下功能:

    4.1获取当前环境信息

    getenv() 是一个 C 标准库函数,用于从环境变量中获取指定名称的值。环境变量是系统级的变量,它们存储了操作系统和程序运行时需要的配置信息,比如系统路径、用户设置等。getenv() 函数通过读取这些环境变量,允许程序动态地获取环境设置。

    头文件:#include<stdlib.h>

    函数:char *getenv(const char *name);

    返回值:

    • 成功:如果找到了指定名称的环境变量,getenv() 会返回该变量的值(一个指向字符数组的指针,代表该环境变量的值)。

    • 失败:如果未找到指定的环境变量,getenv() 返回 NULL

    代码实现:

    //获取当前环境信息
    const char* GETPWD()
    {char *pwd=getenv("PWD");return pwd==NULL?"None":pwd;
    }//获取用户信息
    const char*GETUSER()
    {char*user=getenv("USER");return user==NULL?"None":user;
    }//获取系统信息
    const char*GETHOSTNAME()
    {char*hostname=getenv("HOSTNAME");return hostname==NULL?"None":hostname;
    }
    
    4.2输出命令行提示符

    snprintf 是 C 语言标准库中的一个函数,属于 stdio.h 头文件。它的作用是将格式化的数据输出到一个字符数组中,并且保证不会发生缓冲区溢出。snprintf 函数是对 sprintf 的一种改进,主要是增加了一个最大字符数的限制,避免了 sprintf 在没有足够空间时造成内存溢出的风险。 

    头文件:#include<stdio.h>

    int snprintf(char *str, size_t size, const char *format, ...);

    返回值:

    • 成功:返回写入字节数(当被写入内容超过写入大小,发生截断)

    • 失败:返回负数 

    #define COMMAND_SIZE 1024
    #define FORMAT "[%s@%s %s]#"
    void MakeCMDPrompt(char cmdprompt[],size_t size)//制作命令行提示符
    {snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),GETPWD());
    }
    void PrintCMDPrompt()//打印命令行提示符
    {char prompt[COMMAND_SIZE];MakeCMDPrompt(prompt,sizeof(prompt));printf("%s",prompt);
    }
    
    4.3获取命令行输入

    fgets 是 C 语言标准库中的一个函数,属于 stdio.h 头文件。它的作用是从指定的文件流中读取一行字符串,并将读取的内容存储到一个字符数组中。与 gets 不同,fgets 可以避免缓冲区溢出的问题,因为它会限制读取的字符数。 

    头文件:#include<stdio.h>

    char *fgets(char *s, int size, FILE *stream);

    返回值:

    • 成功 :返回写入的s的位置
    • 失败:返回NULL

     代码实现:

    //接受命令行
    bool MakeCMDLine(char*out,size_t size)
    {char*line=fgets(out,size,stdin);if(line==NULL) return false;//返回值为空,写入失败out[strlen(out)-1]=0;//去除输入的换行符if(strlen(out)==0) return false;return true;
    }

    4.2解析命令行

    将用户输入的命令解析成可执行的命令和参数。

    strtok 是 C 语言标准库中的一个函数,属于 string.h 头文件。它用于将一个字符串分割成一系列子字符串(tokens),根据指定的分隔符。该函数通常用于处理由空格、逗号、换行符等字符分隔的文本数据。

    char *strtok(char *str, const char *delim);

    • str:待分割的字符串。如果是第一次调用 strtok,该参数应为需要分割的字符串;如果是后续调用,应该传递 NULL,以继续分割上一次传入的字符串。

    • delim:分隔符字符串,定义了用于分割字符串的字符集合。可以是单个字符,也可以是多个字符,strtok 会将字符串中的任何一个分隔符都视为分隔点。

    //分割字符串
    bool CMDLinePrase(char *line)
    {
    #define ADC " "g_argc=0;//每次初始化为0,确保每个命令都是从首位开始g_argv[g_argc++]=strtok(line,ADC);while(g_argv[g_argc++]=strtok(nullptr,ADC));g_argc--;return true;
    }
    
    4.4执行命令行
    4.4.1执行内建命令 

    通过父进程本身来进行执行:(cd命令)

    头文件:#include<unistd.h>

     int chdir(const char *path);

    bool CheckBuiltIn()
    {std::string cmd=g_argv[0];if(cmd=="cd"){if(g_argc==1){chdir(GETHOME());return true;}else{std::string pwd=g_argv[1];chdir(pwd.c_str());}return true;}return false;
    }
    
    4.4.2执行外部命令

     通过子进程来进行执行:

    //子程序进行进程替换执行命令
    int Execute()
    {int id=fork();if(id==0){//chileexecvp(g_argv[0],g_argv);exit(1);}//fatherint idd=waitpid(id,NULL,0);//阻塞等待(void)idd;//使用避免报错return 0;
    }
    
    4.5更新环境变量

    getcwdunistd.h 头文件中的一个函数,用于获取当前工作目录。 

     #include<unistd.h>

    char *getcwd(char *buf, size_t size);

    • buf:一个字符数组的指针,用来存储获取的当前工作目录的路径。你需要在调用 getcwd 之前分配足够的内存空间来存储路径。

    • sizebuf 指针指向的字符数组的大小。它指定了 buf 能够存储的最大字符数。

    char g_env[1024];
    char g_cwd[1024];void ChangEnv()
    {const char*cwd=getcwd(g_cwd,sizeof(g_cwd));if(cwd!=nullptr){snprintf(g_env,sizeof(g_env),"PWD=%s",g_cwd);putenv(g_env);}
    }
    

    3. Shell 实现完整代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #define COMMAND_SIZE 1024
    #define FORMAT "[%s@%s %s]#"
    #define MAXARGC 128
    char g_env[1024];
    char g_cwd[1024];
    char* g_argv[MAXARGC];
    int g_argc=0;
    const char* GETPWD()
    {char *pwd=getenv("PWD");return pwd==NULL?"None":pwd;
    }
    const char*GETUSER()
    {char*user=getenv("USER");return user==NULL?"None":user;
    }
    const char*GETHOSTNAME()
    {char*hostname=getenv("HOSTNAME");return hostname==NULL?"None":hostname;
    }
    const char*GETHOME()
    {char*home=getenv("HOME");return home==NULL?"None":home;
    }
    void ChangEnv()
    {const char*cwd=getcwd(g_cwd,sizeof(g_cwd));if(cwd!=nullptr){snprintf(g_env,sizeof(g_env),"PWD=%s",g_cwd);putenv(g_env);}
    }
    bool CheckBuiltIn()
    {std::string cmd=g_argv[0];if(cmd=="cd"){if(g_argc==1){chdir(GETHOME());return true;}else{std::string pwd=g_argv[1];chdir(pwd.c_str());}ChangEnv();return true;}return false;
    }
    std::string DirName(const char* pwd)
    {
    #define SLASH "/"std::string dir=pwd;auto pose=dir.rfind(SLASH);if(pose==std::string::npos) return "BUG?";return dir.substr(pose+1);
    }
    void MakeCMDPrompt(char cmdprompt[],size_t size)
    {//snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),GETPWD());snprintf(cmdprompt,size,FORMAT,GETUSER(),GETHOSTNAME(),DirName(GETPWD()).c_str());
    }
    void PrintCMDPrompt()
    {char prompt[COMMAND_SIZE];MakeCMDPrompt(prompt,sizeof(prompt));printf("%s",prompt);
    }
    bool MakeCMDLine(char*out,size_t size)
    {char*line=fgets(out,size,stdin);if(line==NULL) return false;out[strlen(out)-1]=0;if(strlen(out)==0) return false;return true;
    }
    bool CMDLinePrase(char *line)
    {
    #define ADC " "g_argc=0;g_argv[g_argc++]=strtok(line,ADC);while(g_argv[g_argc++]=strtok(nullptr,ADC));g_argc--;return g_argc==0?false:true;
    }
    void PrintCMDLinePrase()
    {for(int i=0;g_argv[i];i++){printf("argv[%d]->%s\n",i,g_argv[i]);}printf("argc :%d\n",g_argc);
    }
    void Print()
    {char cmdline[COMMAND_SIZE];if( MakeCMDLine(cmdline,sizeof(cmdline))){printf("%s",cmdline);}
    }
    int Execute()
    {int id=fork();if(id==0){//chileexecvp(g_argv[0],g_argv);exit(1);}//fatherint idd=waitpid(id,NULL,0);//阻塞等待(void)idd;//使用避免报错return 0;
    }
    int main()
    {while(true){PrintCMDPrompt();char cmdline[COMMAND_SIZE];if(! MakeCMDLine(cmdline,sizeof(cmdline))){continue;}if(!CMDLinePrase(cmdline)){continue;}if(CheckBuiltIn()){continue;}Execute();}return 0;
    }
    

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

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

    相关文章

    Spark,流量统计案例

    提前创好一个文件夹分为四个类 FlowBean中的代码内容为&#xff1a;package org.example.flow; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; //hadoop 序列化 //三个属性&#xff1a;手机…

    下载油管视频 - yt-dlp

    文章目录 1. yt-dlp与you-get介绍1.1 主要功能对比1.2 使用场景1.3 安装 2. 基本命令介绍2.1 默认下载视频2.2 指定画质和格式规则2.3 下载播放列表2.4 备注 3. 参考资料 之前只使用you-get下载b站视频&#xff0c;当时了解you-get也可下载油管视频&#xff0c;但之前无此需求&…

    基于javaweb的SSM+Maven教材管理系统设计与实现(源码+文档+部署讲解)

    技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

    VS2022+QT环境配置及基本操作

    参考文章 2025最新&#xff01;Visual Studio 2022 QT6.7 环境配置全攻略&#xff1a;一键搞定安装与乱码问题&#xff0c;开发效率翻倍&#xff01;&#xff08;全网最详细教程&#xff0c;手把手教你搭建完美开发环境&#xff01;&#xff09;_vs2022 qt-CSDN博客 下载QT …

    使用percona-toolkit同步mysql表数据

    背景 做了主备mysql的配置以后&#xff0c;可能因为切换过程造成不一致的情况&#xff0c;这个时候可以处理的方式是全量导入再导出&#xff0c;这个有个问题就是操作的数据太多了 我们只需要数据补全同步即可 mysql的同步是基于binlog的&#xff0c;如果没有记录的部分的数据…

    MDG 实现后端主数据变更后快照自动刷新的相关设置

    文章目录 前言实现过程BGRFC期初配置&#xff08;可选&#xff09;设置 MDG快照 BGRFC维护BP出站功能模块 监控 前言 众所周知&#xff0c;在MDG变更请求创建的同时&#xff0c;所有reuse模型实体对应的快照snapshot数据都会记录下来。随后在CR中&#xff0c;用户可以修改这些…

    重装系统 之 Dell戴尔服务器 PowerEdge R750xs + window server2012r2 || 2016

    因要求需要给新服务器装个 win server2012或者2016系统 XXX使用U盘制作PE系统U盘安装系统不行&#xff0c;适合普通win8&#xff0c;win10&#xff0c;win11U盘制作PE系统U盘安装win10系统教程U盘制作PE系统U盘安装win10系统教程https://mp.weixin.qq.com/s/t0W8aNJaHPAU8T78nh…

    基于Spring Security 6的OAuth2 系列之二十六 - 终章

    之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

    一键配置多用户VNC远程桌面:自动化脚本详解

    在当今远程工作盛行的时代,高效且安全地管理多用户远程桌面访问变得至关重要。本文将介绍一个强大的自动化脚本,该脚本能够快速创建用户并配置VNC远程桌面环境,大大简化了系统管理员的工作。 一、背景介绍 在Linux系统中,手动配置VNC服务器通常需要执行多个步骤,包括创建…

    IOT项目——双轴追光系统

    双轴太阳能追光系统 - ESP32实现 系统概述 这个系统使用&#xff1a; ESP32开发板2个舵机&#xff08;水平方向和垂直方向&#xff09;4个光敏电阻&#xff08;用于检测光照方向&#xff09;适当的电阻&#xff08;用于光敏电阻分压&#xff09; 接线示意图 --------------…

    Maven集成模块打包使用

    文章目录 1.问题思考&#xff08;如何对集成模块进行打包&#xff09;2.问题解决 &#xff08;如何对集成模块进行打包&#xff09;3.使用者使用该jar包(jar包安装本地仓库和使用) 1.问题思考&#xff08;如何对集成模块进行打包&#xff09; 思考&#xff1a;假设有这么一个场…

    OpenVINO教程(二):图片目标检测推理应用

    YOLO模型物体检测 下面是一个简单的python程序,他的功能是使用yolo11n模型对coco_bike.jpg照片进行检测,并显示检测结果 代码步骤如下: coco_bike.jpg照片加载yolo模型使用模型进行detect推理显示推理结果 下面是完整的代码 from pathlib import Pathimport urllib.request…

    聚类算法(K-means、DBSCAN)

    聚类算法 K-means 算法 算法原理 K-means 是一种基于类内距离最小化的划分式聚类算法&#xff0c;其核心思想是通过迭代优化将数据划分为 K 个簇。目标函数为最小化平方误差&#xff08;SSE&#xff09;&#xff1a; S S E ∑ i 1 K ∑ x ∈ C i ∣ ∣ x − μ i ∣ ∣ 2…

    Oracle在ERP市场击败SAP

    2024年&#xff0c;甲骨文&#xff08;Oracle&#xff09;以87亿美元的ERP收入和6.63%的市场份额&#xff0c;首次超越SAP&#xff0c;成为全球最大的ERP应用软件供应商&#xff0c;结束了SAP自上世纪80年代以来在该领域的长期霸主地位。据APPS RUN THE WORLD的市场调研&#x…

    嵌入式面试高频笔试题目解析

    一、基础概念与 C 语言核心题 1. 指针与内存操作 典型题目: char str[] = "hello"; char *ptr = "world"; str[0] = H; // 合法吗? ptr[0] = W; // 合法吗?为什么?解析: str 是栈上数组,可修改内容,str[0]=H 合法。ptr 指向常量字符串区,修改会…

    【Python】Selenium切换网页的标签页的写法(全!!!)

    在使用selenium做网站爬取测试的时候&#xff0c;我们经常会遇到一些需要点击的元素&#xff0c;才能点击到我们想要进入的页面&#xff0c; 于是我们就要模拟 不断地 点点点击 鼠标的样子。 这个时候网页上就会有很多的标签页&#xff0c;你的浏览器网页标签栏 be like: 那…

    MySQL GTID模式主从同步配置全指南:从配置到故障转移

    前言 MySQL主从复制是企业级数据库架构的基础&#xff0c;而GTID(Global Transaction Identifier)模式则是MySQL 5.6版本后推出的革命性复制技术。本文将详细介绍如何配置基于GTID的主从同步&#xff0c;并包含实用的故障转移操作指南。 一、GTID模式核心优势 相比传统基于…

    MAC系统下完全卸载Android Studio

    删除以下文件 /Applications/Android Studio.app /Users/用户名/Library/Application Support/Google/AndroidStudio2024.2 /Users/用户名/Library/Google/AndroidStudio /Users/用户名/Library/Preferences/com.google.android.studio.plist /Users/用户名/Library/Cache…

    <C#>.NET WebAPI 的 FromBody ,FromForm ,FromServices等详细解释

    在 .NET 8 Web API 中&#xff0c;[FromBody]、[FromForm]、[FromHeader]、[FromKeyedServices]、[FromQuery]、[FromRoute] 和 [FromServices] 这些都是用于绑定控制器动作方法参数的特性&#xff0c;下面为你详细解释这些特性。 1. [FromBody] 作用&#xff1a;从 HTTP 请求…

    # 透视 Linux 内核:Socket 机制的底层架构与运行逻辑深度解析

    在由 Linux 操作系统构建的庞大网络生态中&#xff0c;Socket 作为网络通信的核心枢纽&#xff0c;承载着不同主机间应用进程的数据交互重任。无论是日常的网页浏览、在线游戏&#xff0c;还是复杂的分布式系统通信&#xff0c;Socket 都在幕后扮演着关键角色。尽管多数开发者对…