IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux下自定义shell(简单shell) -> 正文阅读

[系统运维]Linux下自定义shell(简单shell)

一.操作流程及原理

既然要自定义shell,那么咱们自定义的shell肯定要有与LInux的shell有相同的功能,那么接下来说说怎么操作及其原理,咱们首先运用fgets函数来获取stdin输入流,即获取你要输入的命令,然后把你输入的命令进行解析,不过在解析命令前要先把命令进行组织起来,这是咱们可以通过数组来把命令进行组织(下文会说明为什么要进行组织),然后就是解析,在Linux中咱们输入命令的过程是不会终止的,即当你输入一个命令并按下回车键后,咱们还可以接着输入命令,那为了实现这个功能,咱们可以通过循环语句还有进程的特性(独立性)来进行持续性解析命令,当咱们把输入的命令组织到一个数组中后(如args),咱们可以通过exec函数这个系统调用函数来进行解析,具体的解析过程其实很简单,咱们只需要把args中的元素传入exec函数即可(因为咱们组织的时候咱们输入的命令和参数已经输入到了args数组中),例如:首先输入命令cmd:ls -a -l,然后经过组织后数组args[]={“ls”,“-a”,"-l"},然后咱们使用exec函数中的execvp函数来进行解析,咱们只需execvp(args[0],args);即可

二.问题

问题一:为什么要进行组织?

eg:咱们在命令行 输入命令 ls -a -l 或者 ls -a -l 时大家可以发现在Linux下它为你启动的bash中这两种命令的输入方式有明显的不同,但是最终输出的结果都是一样的,这是为什么?这是因为在bash上述两种命令最后都被组织成了一种数组的形式 如 args[] = {"ls","-a","-l"} ,而为什么要进行组织就是为了如果有人把命令输入成 ls -a -l 这种形式后,操作系统还可以对命令进行成功的解析,增强了操作系统中命令行输入的健壮性。

问题二:具体解析的过程是怎样的?

所谓的解析其实就是利用exec这类系统调用函数来解析命令,在咱们组织完成之后,其实只需要把包涵命令的数组args传入exec函数即可,但是这里面有一个问题,那就是exec函数是一种进程替换函数,在你使用exec这个函数后,你当前进程会被替换成你调用命令的进程,即你这个进程的代码与数据都会被替换,而且在替换完这个进程后,这个进程会直接结束,如果在当前进程,即咱们运行敲代码的这个进程(咱们把它称为父进程),直接使用exec函数的话,那么在exec函数替换完成后,这个进程就结束了,那么意味着咱们这个shell只能输入一次命令,因为输入完命令再调用exec函数后该进程直接结束,但是咱们在Linux的bash中可以持续的输入命令,即一个命令执行完成后,可以再执行别的命令,所以为了完善咱们的自制shell,咱们就必须要解决这个问题。当然其实解决方法也很简单,咱们只需要在这个父进程中再创建一个子进程,并且利用子进程来执行组织和解析的过程即可。

问题三:什么是进程替换函数(什么是exec函数族)?

我们通常把exec函数称为进程替换函数,它的作用:exec函数族提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了(以上摘自百度百科)。简而言之,当一个进程调用exec函数来替换进程的时候,当前进程的代码与数据都会被替换进程的代码与数据替代,并且页表所映射的物理地址也会被替代,但是呢这个过程并没有创建新的进程,所以在使用exec函数调换前后该进程的id(包括pid,进程组号等等)没有改变,改变的只是代码与数据还有页表的映射关系。

ps:exec函数的原型:

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

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

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

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

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

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

其中一些参数:

l - list 参数采用列表,即把参数直接一个一个传入exec函数。

v - vector 参数采用数组,即把这些参数都加入到一个数组中去,然后再把数组传入函数。

p - path?有p自动搜索环境变量PATH,即如果你带了p选项,那么就可以不用传入命令所在的路径

e - env 自己维护环境变量

三,代码:

?

    1 #include <stdio.h>                                                                                                                          
    2 #include <string.h>
    3 #include <unistd.h>
    4 #include <stdlib.h>
    5 
    6 #define MAX_CMD 1024
    7 #define MAX_PAR 32
    8 
    9 char cmd[MAX_CMD];
   10 char* args[MAX_PAR];
   11 
   12 int main()
   13 {
   14   const char* identifier = "[dcly@localhost file]^_^ ";
   15   while(1){
   16     memset(cmd,0x00,MAX_CMD);
   17     printf("%s",identifier);
   18     fgets(cmd,MAX_CMD,stdin);
   19     cmd[strlen(cmd) - 1] = '\0';
   20     args[0] = strtok(cmd, " ");
   21     int i = 1;
   22     do{
   23       args[i] = strtok(NULL," ");
   24       if(args[i++] == NULL){
   25         break;
   26       }
   27     }while(1);
   28 
   29     pid_t id = fork();
   30     if(id < 0){
   31       printf("fork error!\n");
   32       continue;
   33     }
   34     if(id == 0){
   35       execvp(args[0], args);
   36       exit(1);
   37     }
   38     int status = 0;
   39     pid_t ret = waitpid(id, &status, 0);
   40     if(ret > 0 && status & 0x7f == 0){
   41       printf("child status code : %d\n",(status >> 8)&0xff);
   42     }
   43   }
   44   return 0;
   45 }                                           

四,解析代码

在上述代码中,cmd这个数组的作用是接收命令,args这个指针数组的作用是接收组织完成后的命令,MAX_CMD与MAX_PAR这两个宏的作用,前者是用来表示接收命令的字符数量最大值,后者表示最终组织完成后命令及其参数的字符串数量的最大值。identifier就是用来自定义如[dcly@localhost file]$ 这样的命令标识符的,你可以把它设置成你喜欢的样子。 当然大家也可以通过如gethostname 获取本主机名称?getuid 获取用户标识号 等等之类的系统调用命令来完善或者说是丰富大家的命令标识符,比如 dcly - 用户名 localhost - 主机名 file - 当前目录

?

?fgets函数是类似如scanf之类的输入函数。strtok函数是用来把你输入的如 ls? -a? -l 命令进行分解,最终分解成 " ls " " -a " " -l "这三个字符串(如果有不熟悉strtok函数的朋友可以看看这个链接),strtok - C++ Referencehttp://www.cplusplus.com/reference/cstring/strtok/?kw=strtokfork函数用来创建子进程,id 用来接收fork函数的返回值,如果id等于0的时候就代表该进程为子进程,那么就可以进行进程替代了。咱们使用的是execvp函数,这个函数只需要传入命令与命令与参数所在的数组即可(在我们进行组织的时候是从左向右来进行的,也就是说" ls "命令首先被传入到args[ 0 ]了,所以咱们传参的时候把args[0]即命令传入第一个参数位)

ps:这只是一个比较简单比较粗糙的自定义shell,其中本应该有的 > 重定向和?| 管道等等都还没有实现,如果对自定义shell感兴趣的朋友可以关注我,我以后会推出高级版本的自定义shell供大家了解。

ps:这篇文章中有常见的系统调用函数,如 gethostname之类的。

linux常见系统调用函数列表_最爱酸豆角的博客-CSDN博客_linux系统调用函数大全以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数。这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的。按照惯例,这个列表以man pages第2节,即系统调用节为蓝本。按照笔者的理解,对其作了大致的分类,同时也作了一些小小的修改,删去了几个仅供内核使用,不允许用户调用的系统调用,对个别本人稍觉不妥的地方作了一些小的修改,并对所有列出的系统调用附上简要注释。其中有一些函数的作用完全相同,只https://blog.csdn.net/ztp123456/article/details/107845118

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-12 16:43:29  更:2022-05-12 16:43:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:22:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码