| |
|
开发:
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 自己维护环境变量 三,代码:?
四,解析代码在上述代码中,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之类的。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
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- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |