进程程序替换((>_<)子进程跑了),模拟编写一个入门shell
目录
进程程序替换
1.进程替换的原理
2.怎么来另起炉灶执行另一个程序
①execl
②execv
③execlp
④execvp编辑
⑤execle
⑥execvpe 和 execve
3.执行一下自己的程序
4.丐版shell
①显示提示符
②获取用户输入编辑
③.将字符串切分
④.创建子进程,执行
⑤.程序替换
⑥内建命令
进程程序替换
先前,我们可以用fork()创建一个子进程,子进程代码继承父进程代码,执行跟父进程相同的代码,我们能不能让子进程离经叛道,另起炉灶干一番事业,脱离父进程的束缚,当然欧克啦。
我们可以让它执行c/c++、Java、Php、shell、Python呀什么的...可神奇了。
1.进程替换的原理
在没有让子进程另起炉灶之前,没有写时拷贝时,他们是这样存在的。
突然,子进程撂挑子了。来了个b.exe就把他拐跑了
原理就是:
①.将磁盘的程序,加载到内存结构中。
②.页表重新建立映射,指向另一块加载了程序的内存空间。(让父子进程彻底分离,再也没有关系,让子进程执行一个新的程序)((>_<)变心了明明)
2.怎么来另起炉灶执行另一个程序
当然还是要借助他人的援手,上述替换的过程都是由操作系统来完成,所以我们要借助os之手,分离他们,我们来学习第一个函数接口。这些函数都是系统调用。
①execl
程序替换:
1.调用函数去替换,第一步我们已经做好了。
2.接下来就是要找这个程序在哪,这便是第一个参数所要干的事情。
3.我们找到了程序之后,需要程序怎么做,这是第二个参数要干的。第二参数是可变参数,我们可以不拘泥于传一个参数。比如我们想执行ls这个进程(ls为什么是进程,因为在命令行上启动的进程都是BASH进程的子进程),我们可以传 ls,也可以传ls -a,可以传ls -a -l,还可以传ls -a -l -i。命令行上怎么打,就怎么传,当然要有标识程序执行完毕的NULL放在最后。
我们来试试:
结果如下图:
哟,到这大家有没有发现一个问题,最后一个printf没有打印这是为什么呢?
因为execl一旦替换成功,execl以下的代码要全部被替换,printf被替换走了,当然执行不了。
这个execl函数,有返回值,那我们要不要关注返回值呢?
根本就不用,函数成功之后都会直接执行要执行的进程,就不会有返回值。失败了,execl后面的代码就不会被替换,自然会执行后面的代码。一旦执行后面的代码,只有一种结果,程序替换失败了。然后通过返回值判断什么原因导致失败。
先前这些都是自己的进程去替换执行一个进程,execl后面的代码不会执行,接下来咋们要通过子进程,来执行一个进程,让子进程被拐走,不影响父进程的代码。
代码如下:
结果如图:
成功证明子进程跑了(>_<)、
大家有没有注意为什么退出码是零,明明我设置了退出码是12呀。
因为上文说过一旦执行另一个进程,下面的代码都会被替换,这时exit(12)已经没有了,这时接收的返回值是execl执行完 ls -l -a 这个进程的退出码。注意,我是让子进程去替换进程(执行一个新的进程),对父进程没有任何影响,这全然是因为写实拷贝的作用。上文有讲。
②execv
它和execl没有本质的区别
execl: “l” 是list 链表
execv:“v” 是vector 数组
path:需要执行的程序的位置
argv[]:刚刚是可变参数(arg ...),现在是数组。
意味着我们可以这样:将数组传给第二个参数
我们传入数组结果是相同的:
③execlp
我觉得挺好用的一个同类型的函数。
它命名中的“p”,指的是path 环境变量,意味着我们不用去明确的输入目标进程的地址,我们只用输入想执行的进程,前提是这个进程在你所在的环境变量目录中,这样os才能找到,目标的进程。
代码如下:
结果如下:
千万要注意,一定要写NULL,来指明进程执行完毕。
不然会成这样:
这里如愿的拿到了12这个退出码,说明execlp这个函数寄了,进程没执行成功。
④execvp![](/assets/blank.gif)
大同小异,这就不细讲了
⑤execle
用这个函数,我们可以将环境变量传给将要执行的进程。
代码如下:
这是父进程:
这是要被执行的进程:
结果如下:
要注意我们的PATH会将原本进程的PATH给覆盖掉。
我们如果用execl的话,不传MYPATH,它自己的PATH会打印出来。
我们用excele
将自己的MYPATH传过去,PATH将会被覆盖
后面的MYPATH也打不出了。
我们要想PATH被打出,自己的MYPATH也被打出,可以这样:
然后在父进程中这样写:
这样就成功咯,大家可以私下试试
⑥execvpe 和 execve
这俩和上面的都大同小异。
exec +l/v(链表or数组) +p(可以不输入地址直接输入进程名称) +e(可以自己传环境变量给子进程)
3.执行一下自己的程序
其实上面有两个实例我已经用子进程去调用自己的程序了,下面正式说一下。
平常用vs不能直接运行两个exe,今天我们用一个进程去调动另一个进程。
其中的一个circulate 程序:
我们要用test的子进程去调用circulate
黄天不负有心人,我们这里的WEXITSTATUS(status)终于能获得我们设置的退出码了,关于退出码,上一篇文章有讲。
结果:
成功了,当然也可以去调动Java、Python呀什么的程序。大家可以去尝试下。
4.丐版shell
虽然是精简版,但是它可以创建 终止 等待 程序替换。
这些是头文件和宏定义:
①显示提示符
②获取用户输入![](/assets/blank.gif)
哎?为什么会换两次行,我明明printf中只有一个\n。
因为,我们再输入命令的时候,最后都要敲一个回车,来表示执行DOS命令。
这个回车(不可见字符)自然也被存到了command_line字符数组中。
我们可以做的操作就是将这个回车符赋值为‘\0’。
这样就不会换行了。
③.将字符串切分
因为我们平常输入的命令之间都是带有空格的。比如:ls -a -l。
strtok函数可以让字符串以指定的分隔符分开。
while的循环条件很怪,为什么要这样写呢?
strtok函数截取成功,返回字符串起始地址。
截取失败返回NULL,正好赋给command_args。
那为什么在循环条件中的strtok中第一个参数要传NULL?
strtok的函数原型为char *strtok(char *s, char *delim),功能为“Parse S into tokens separated by characters in DELIM.If S is NULL, the saved pointer in SAVE_PTR is used as the next starting point. ” 翻译:作用于字符串s,以包含在delim中的字符为分界符,将s切分成一个个子串;如果,s为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。 (摘自百度百科)
意味着,我第一次用传要分隔的字符串,第二次及以后如果传入NULL,那么函数的上一次保存的地址将在下一次调用作为起始地址。
我们将分割好的字符串打印一下:
结果是:
④.创建子进程,执行
⑤.程序替换
将上面我们所学的内容汇聚在一起,以及上一篇文章的内容。
这一切尽在代码中,我们实测一下效果。
⑥内建命令
在我们的shell里,通过cd .. 去回到上一级目录的方法是不可行的。
我们通过exec*去执行cd命令,只是让子进程的路径发生变化,之后子进程运行结束,接下来通过“pwd”这个进程来查看当前路径,当前路径没有发生任何变化。因为这时的路径是通过父进程继承下来的,执行cd命令的进程只是改变了自己的进程,然而对父进程的路径发生不了改变。要想改变成功,只能通过在父进程中执行内建命令。一些命令只能通过父进程执行,不能让子进程执行,这就是内建命令。
我们修改下代码:在分隔开字符串之后,去判断是否要求子进程进行cd
之后使用chdir函数
传入想转换到的路径,执行成功后,会使调用者的当前路径变成传入的路径。
我们看下效果:
同样:
export也是一个内建命令。它的作用是在系统环境变量中,导入自己的环境变量。
在环境变量中就能看到,我们导入的环境变量。
看看在我们的shell中行不行,不用实验肯定不行,环境变量具有全局属性,它可以被子进程继承。在我们的shell中,用一个子进程去导入环境变量,只会将环境导入子进程的所处的环境变量之中。根本不会修改父进程的环境变量。
我们在父进程修改下,让我们自己的shell也能添加环境变量。
putenv是一个将传入参数加入环境变量的函数。
为什么还是不行?
因为,系统的环境变量在char**数组中,是全局的,被系统所维护。而我们的环境变量在我们的command_args中,但是我们shell是循环执行,我们是将所要的环境变量导出了,但是到下一次我们又去执行新的子进程时,comman_args又被清空了,当然环境变量也没了。如何改变,其实可以很简单,我们可以在循环外面定义一个数组将我们的环境变量存起来,无论怎么循环,都影响不到全局变量。
成功了!
我们的简易shell到此就完善的差不多了,希望以后有机会,还能再次完善他,感谢观看,我们下次再见。
下面是simpleshell的所有代码:至于编译时会warning,不用在乎,能成功运行。
进程程序替换((>_<)子进程跑了),模拟编写一个入门shell相关推荐
- Linux-进程控制详解(进程创建+进程终止+进程等待+进程程序替换)
Linux进程控制 1. 进程创建 1.1 fork 1.2 vfork 2. 进程终止 3. 进程等待 3.1 为什么要进程等待 3.2 wait 3.3 waitpid 3.4 获取子进程退出信息 ...
- Linux系统编程18:超详解进程程序替换exec函数的一些用法
文章目录 (1)进程程序替换是什么 (2)exec-替换函数 (3)实例展示-了解exec函数的替换原理 A:execl和execv B:execlp和execvp C:替换自己的程序和execle ...
- 【Linux】linux进程--进程控制:进程创建、进程终止、进程等待、进程程序替换
目录 1.进程创建 1)重温fork():让正在运行的进程创建出来一个子进程:从已存在的进程中创建一个新的进程,新进程为子进程而远进程为父进程. 2)fork内部完成的事情 3)用户空间 & ...
- [Linux-进程控制] 进程创建进程终止进程等待进程程序替换简易shell
[Linux-进程控制] 进程创建&进程终止&进程等待&进程程序替换&简易shell 进程创建 fork函数回顾 双返回值 为什么要给子进程返回0,给父进程返回子进程的 ...
- Java黑皮书课后题第6章:*6.15(金融应用:打印税表)程序清单3-5给出了计算税款的程序。使用下面的方法体编写一个计算税款的方法。使用这个方法编写程序,打印可征税人从50000到60000间隔
*6.15(金融应用:打印税表)程序清单3-5给出了计算税款的程序.使用下面的方法体编写一个计算税款的方法.使用这个方法编写程序 题目 题目描述 破题 程序清单3-5(非本题):代码不全 补充代码:编 ...
- python的简单程序代码_小白学编程?从一个简单的程序开始学习Python编程
笔者思虑再三还是决定选择图文(因为百家的视频发布画质真不怎么样[囧]). 笔者学习编程的时间也挺长的,因为业余,因为时间不多,各种原因,自学编程的路特别难走.然后笔者发现,自己能为小白贡献一些力量,然 ...
- bash 将二进制转换为十进制_用‘栈的思想编写一个十进制转换二进制、八进制或十六进制的程序...
用'栈'的思想编写一个十进制转换二进制.八进制或十六进制的程序 根据进制转换方法,如十进制向二进制转换,将转换的十进制整数除以二进制基数(2),得到余数和商,如果商不为0,该商继续做被除数,除以基数, ...
- 定义并调用函数 十进制转二进制_用‘栈的思想编写一个十进制转换二进制、八进制或十六进制的程序...
用'栈'的思想编写一个十进制转换二进制.八进制或十六进制的程序 根据进制转换方法,如十进制向二进制转换,将转换的十进制整数除以二进制基数(2),得到余数和商,如果商不为0,该商继续做被除数,除以基数, ...
- python脚本编写_如何用Python包编写一个简单的脚本,表达你对父母的爱?
全文共2800字,预计学习时长6分钟 在繁忙的工作生活中,我们经常忘记给所爱的人发WhatsApp.本教程将使用Python包Twilio编写一个简单的Python脚本来发送WhatsApp消息.我们 ...
最新文章
- What do you need at home?
- .net源代码已经可以调试
- C++易于实现的有趣项目【附上完整教程】
- python @staticmethod和@classmethod的作用
- java web: Servlet JSP MVC
- nodejs mysql 创建连接池
- python输出字符串的后两位_Python字符串三种格式化输出
- 单片机c语言数码显示实验报告,单片机动态显示技术实验报告.doc
- myeclipse 8.5最新注册码(过期时间到2016年)
- 在.NET Core中使用MySQL5.7的JSON类型字段
- LeetCode 566 Reshape the Matrix 解题报告
- XP停止服务,共建网络安全大环境
- html日历框架,日历.html
- Corel VideoStudio X7 (64bit)安装
- 怎样设置路由器禁用其他设备
- LeetCode-1873. 计算特殊奖金
- 工程师的基本功是什么?如何练习?—学习心得分享
- Unity 多机器的视频不卡帧同步
- 一口“臊子面”的背后,是西安小吃产业发展的缩影
- Shutdown自定义自动关机软件