从0开始自己用C语言写个shell__01_整体的框架以及fork和exec族函数的理解
最近才忙完了一个操作系统的作业,让我们用C语言实现一个Shell。总的来说,其实就是让我们 对系统调用有比较深的了解。
首先 介绍一下我的Shell 所实现的功能。
1.运行可执行程序 即输入某个 标志符号 使得其能在我的Shell中运行,并且不退出当前shell。
2.获得某个程序的中某个字符串的个数(其实就是调用了/bin/里面的grep)
3.使用管道,实现两个子进程之间的联系,当然不能连shell自己都退出了。。
4.定向输出到某个个文本文件中。
但是在这之前,我想先谈谈我对fork()这个函数的理解。
首先我们把最开始的程序叫做F,然后 我们开始运行这个 程序,让我们一条条指令运行!当我们的运行到fork的时候,我们OS将整个程序 复制出几乎完全一样的一个程序(子程序)!注意在此之前的命令已经执行完了,所有的数据空间中的数据都将被复制一份,供子程序使用(包括fork()这个函数也会被复制一份。)。注意是复制一份,并不是让子程序共用父程序的数据空间!(再直白一点就是同一个变量,你在子程序里面调用改变了他,但是当你在父程序里面打印出它时,数值仍然是改变前的)。
前面提到的父子程序,难免会让人产生很多疑惑!有人肯定会问:“你不是说我的父程序 和子程序是完全一样的吗?那我fork出一个子程序有什么用呢?反正都是干的同一件事" 或者问:“那我们怎么区分父子进程呢?”。
我逐个解释。
首先第一 前面我提过,fork也会被复制到子进程中,那么可想而之,两个fork会返回两次,注意不是一个fork返回两次(只是看起来像而已),这就是fork神奇的地方。 fork的调用不需要任何参数,
而他的返回值 是一个整数。
当这个整数大于0时,表示着,当前进程为父程序,并且这个返回的整数表示它的子进程的ID(第几个进程)。
当返回值小于0的时候,表示没能成功创建出子进程。
当等于0的时候表示着,我们的当前进程是子进程。
网上大佬都说 把整个过程看成链表即可,返回值指向下一个进程。
所以人们往往就运用这个特性来设计父程序干啥,程序干啥。这样每次判断一下当前是在父程序还是子程序 就可做不同的事情。也就是说两个进程完全可以干不同的事情。
需要注意的是系统调用函数头文件<unistd.h>
可能又有老哥会问“那执行的时候先执行谁啊?”
当然是“同时”执行,由于concurrency 的存在,中文貌似翻译是并发,这就是指CPU能在极高速度的运转下,不停切换处理对象,让我们觉得貌似他俩同时执行,其实每次cpu都只能对某一个进程进行处理。
至于父子进程谁先执行,我觉得应该问 他俩到底谁先执行完。 这个就难判断了,他们需要执行的内容不一样,出结果的时间当然就不一样,最后谁先结束我们不得而知。
所以往往,我们会在父进程中加入wait 或者waitpid函数,来让父进程来等待子进程。这样能做确保我们Shell的功能实现了以后(当前在父进程),不会立即打断子进程的输出。(这个稍后解释)
实际我们操作fork的时候往往需要搭配exec族的系统调用来配合使用,我们往往希望我们的子程序能运行一些自己的内容或者说是另一个程序。
所以接下里 我想稍微花点时间一个个解释这几个函数。
exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数。
也就是说最终我们调用的其实还是execve而它的库函数,仅仅是传递参数的方法不同。
我先解释execve的参数传递要求
函数定义:
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
第一个参数: filename 字符串所代表的文件路径。(这个文件路径包括了 此用户的环境变量中的 PATH路径,和当前程序所在目录。即在这两种路径中寻找符合filename的文件,然后执行它)。
第二个参数:是利用指针数组来传递给执行文件。
第三个参数:传递给执行文件的新环境变量数组。
我还想在这多啰嗦一句 这个环境变量的意思。注意这里我指的全是Linux 环境下的环境变量。
所谓环境变量,其实是一个很大的集合,它包含了一个或者多个应用程序所将使用到的信息。
那一个程序所将用到哪些信息呢?
常见的环境变量
PATH:决定了shell将到哪些目录中寻找命令或程序
HOME:当前用户主目录
MAIL:是指当前用户的邮件存放目录。
SHELL:是指当前用户用的是哪种Shell。
HISTSIZE:是指保存历史命令记录的条数
LOGNAME:是指当前用户的登录名。
HOSTNAME:是指主机的名称,许多应用程序如果要用到主机名的话,通常是从这个环境变量中来取得的。
LANG/LANGUGE:是和语言相关的环境变量,使用多种语言的用户可以修改此环境变量
https://blog.csdn.net/l494926429/article/details/52816334 详细的介绍可以看这里。
这里我们用到了 环境变量 中的PATH。
现在我们先看看,如果我们想运行程序 需要在哪些地方寻找目标文件。
用 echo $PATH 可以查看这些路径,这里有很多路径,但是我们主要关注/usr/bin这个路径,接下来我们再看看这里面有啥。
可以看到都是些exec 文件,换句话说每当我们在终端里使用ls cd 等命令时起时调用的都是这里面程序,注意这是系统关键文件,不可以随便乱动哦!bin目录下都是二进制可执行文件。/bin目录放置的是最基本的一些命令的可执行文件,比如cp、mv、mkdir、chmod、chown等等;/usr下面也有一个bin目录:/usr/bin,它里面的文件也是一些命令的可执行文件;如果是用户自己安装的软件,软件的主程序文件就会在/usr/local/bin这个目录里面(或者是用户自己指定的安装目录,比如/usr/local/apache/bin)。
https://zhidao.baidu.com/question/1707850194618091740.html 转自这里。
花了点时间解释了环境变量,现在让我继续解释一下其他类型的exec族函数。
int execlp(const char * file,const char * arg,....) 这个函数可以百度到其中的解释。
execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,注意参数的个数有设计者决定。你调用的那个程序需要多少参数你就传几个。最后一个参数必须用空指针(NULL)作结束。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则它将解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数就将出错。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了. 转自百度百科
int execvp(const char * path,const char * arg,....,char *const envp[]);
这个参数起时没有特别大的不同,只不过我们的第二个参数,从一个个给目标程序传递参数,变成了一次性传递一组字符串指针,也就是一次性把所有参数传完。
举个栗子。(注意这里传递的参数的结尾同样也需要NULL)
char *args[] = {"./hello","hello" ,NULL};
execvp("./hello",args);
其实 其他的exec族函数基本用法都差不多。大家可以自行百度。
这里我就先暂时总结到这,下一章节,我将大面积解释Shell 功能的具体实现。
从0开始自己用C语言写个shell__01_整体的框架以及fork和exec族函数的理解的更多相关文章
- linux c语言 fork() 和 exec 函数的简介和用法
linux c语言 fork() 和 exec 函数的简介和用法 假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢? 其实在<std ...
- 自己用C语言写dsPIC / PIC24 serial bootloader
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...
- 自己用C语言写单片机PIC18 serial bootloader
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...
- 自己用C语言写单片机PIC16 serial bootloader
了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 为什么自己写bootl ...
- PIC12F629帮我用C语言写个程序,控制三个LED亮灭
http://power.baidu.com/question/240873584599025684.html?entry=browse_difficult PIC12F629帮我用C语言写个程序,控 ...
- 用C语言写个程序推算出是星期几?(用泰勒公式实现)
在日常生活中,我们常常遇到要知道某一天是星期几的问题.有时候,我们还想知道历史上某一天是星期几.比如: “你出生的那一天是星期几啊?” “明年五一是不是星期天?我去找你玩?” 通常,解决这个问题的最简 ...
- 一个用 C 语言写的迷你版 2048 游戏,仅仅有 500个字符
Jay Chan 用 C 语言写的一个迷你版 2048 游戏,仅仅有 487 个字符. 来围观吧 M[16],X=16,W,k;main(){T(system("stty cbreak&qu ...
- 用C语言写解释器(一)——我们的目标
声明 为提高教学质量,我所在的学院正在筹划编写C语言教材.<用C语言写解释器>系列文章经整理后将收入书中"综合实验"一章.因此该系列的文章主要阅读对象定为刚学完C语言的 ...
- 在windows下用C语言写socket通讯实例
原文:在windows下用C语言写socket通讯实例 From:Microsoft Dev Center #undef UNICODE #define WIN32_LEAN_AND_MEAN #in ...
随机推荐
- Django2.2连接MySQL问题解决
报错一: django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have ...
- 模块讲解---numpymo模块,matplotlib模块,pandas模块
目录 numpy模块 matplotlib模块 pandas模块 numpy模块 numpy模块:用来做数据分析,对numpy数组(既有行又有列)--矩阵进行科学运算 在使用的时候,使用方法与其他的模 ...
- POJ 2893 M × N Puzzle——八数码有解条件
题意:给定M*N的数码图,问能否移动到最终状态 分析 有解的判定条件可见 八数码有解条件 值得一提的是,这道题求逆序对卡树状数组,只能用归并排序. #include<cstdio> #in ...
- BZOJ 3931 / Luogu P3171 [CQOI2015]网络吞吐量 (最大流板题)
题面 中文题目,不解释: BZOJ传送门 Luogu传送门 分析 这题建图是显然的,拆点后iii和i′i'i′连容量为吞吐量的边,根据题目要求,111和nnn的吞吐量看作∞\infty∞. 然后用di ...
- Appium自动化测试教程-自学网-SDK
SDK:软件开发工具包,被软件开发工程师用于特定的软件包.软件框架.硬件平台.操作系统等建立应用软件的开发工具的集合. 因此,Android SDK指的是Android专属的软件开发工具包. 1,安装 ...
- The method setCharacterEncoding(String) is undefined for the type HttpServletResponse
今天将以前做的一个web项目从不笔记本上移到台式机上,import项目后出现“The method setCharacterEncoding(String) is undefined for the ...
- 定时器TIM,pwm
一.定时器 1. 定义 设置等待时间,到达后则执行指定操作的硬件. 2. STM32F407的定时器有以下特征 具有基本的定时功能,也有PWM输出(灯光控制.电机的转速).脉冲捕获功能( ...
- 运算符重载之new与delete
关于new/delete,援引C++ Primer中的一段话: 某些应用程序对内存分配有特殊的要求,因此我们无法直接将标准的内存管理机制直接应用于这些程序.他们常常需要自定义内存分配的细节,比如使用关 ...
- bbs-admin-自定义admin(二)
本文内容 目的:模仿admin默认配置,自定义配置类 一 查 1 查看数据 2 查看表头 3 分页器 4 search(搜索框) 5 action(批量处理) 6 filter(分类) ...
- linux下查看内存频率,内核函数,cpu频率
查看CPU: cat /proc/cpuinfo # 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理 ...