最近才忙完了一个操作系统的作业,让我们用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族函数的理解的更多相关文章

  1. linux c语言 fork() 和 exec 函数的简介和用法

    linux c语言 fork() 和 exec 函数的简介和用法   假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢? 其实在<std ...

  2. 自己用C语言写dsPIC / PIC24 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...

  3. 自己用C语言写单片机PIC18 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). HyperBootlo ...

  4. 自己用C语言写单片机PIC16 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 为什么自己写bootl ...

  5. PIC12F629帮我用C语言写个程序,控制三个LED亮灭

    http://power.baidu.com/question/240873584599025684.html?entry=browse_difficult PIC12F629帮我用C语言写个程序,控 ...

  6. 用C语言写个程序推算出是星期几?(用泰勒公式实现)

    在日常生活中,我们常常遇到要知道某一天是星期几的问题.有时候,我们还想知道历史上某一天是星期几.比如: “你出生的那一天是星期几啊?” “明年五一是不是星期天?我去找你玩?” 通常,解决这个问题的最简 ...

  7. 一个用 C 语言写的迷你版 2048 游戏,仅仅有 500个字符

    Jay Chan 用 C 语言写的一个迷你版 2048 游戏,仅仅有 487 个字符. 来围观吧 M[16],X=16,W,k;main(){T(system("stty cbreak&qu ...

  8. 用C语言写解释器(一)——我们的目标

    声明 为提高教学质量,我所在的学院正在筹划编写C语言教材.<用C语言写解释器>系列文章经整理后将收入书中"综合实验"一章.因此该系列的文章主要阅读对象定为刚学完C语言的 ...

  9. 在windows下用C语言写socket通讯实例

    原文:在windows下用C语言写socket通讯实例 From:Microsoft Dev Center #undef UNICODE #define WIN32_LEAN_AND_MEAN #in ...

随机推荐

  1. 学习Hook的必备知识

    1.汇编 2.API 3.内存 4.进程 5.窗口 必须熟悉的汇编指令: PUSH  入栈 MOV   赋值 JMP(JNZ  JE)  跳转 CALL  调用函数 RET  返回 Cmp  比较 T ...

  2. js 同步 异步 宏任务 微任务 文章分享

    分享一篇 写的很好的 宏任务 微任务  同步异步的文章 文章原地址: https://juejin.im/post/59e85eebf265da430d571f89 这一次,彻底弄懂 JavaScri ...

  3. 存储过程:SET Transaction Isolation Level Read语法的四种情况

    这几天一直在弄存储过程,现在在这里跟大伙共享下资料: SET Transaction Isolation Level Read UNCOMMITTED 使用这句东东呢可以分为四种情况,现在就在这里逐一 ...

  4. C语言学习系列(六)存储类

    一.C存储类 存储类定义C程序中变量/函数的范围(可见性)和生命周期.这些说明符放置在他们所修饰的类型之前.for example:auto.register.static.extern. (一).a ...

  5. python自动华 (十四)

    Python自动化 [第十四篇]:HTML介绍 本节内容: Html 概述 HTML文档 常用标签 2. CSS 概述 CSS选择器 CSS常用属性 1.HTML 1.1概述 HTML是英文Hyper ...

  6. 删除git中无用的大文件

    推荐阅读:为什么你的 Git 仓库变得如此臃肿 有时候我们不小心提交了一些大文件上去,后来删除了,但是已经于事无补了,整个git的提及已经蹭蹭上去了. 这个时候怎么办呢? 1. 查看有哪些大文件(to ...

  7. 内存管理2-set方法的内存管理-程序解析

    创建class Book .h 有@ property float price;  //@synthesize 自动 ------------ 创建class Student #import &quo ...

  8. codeforces gym #101987B- Cosmetic Survey(floyd)

    题目链接: https://codeforces.com/gym/101987/my 题意: 顶点数为$n$,边数为$m$ 求出每个点对$(a,b)$,$a$到$b$的最小路径的最大值 数据范围: $ ...

  9. Java主线程在子线程执行完毕后再执行

    一.join() Thread中的join()方法就是同步,它使得线程之间由并行执行变为串行执行. public class MyJoinTest { public static void main( ...

  10. idea 2018注册码(激活码)

    最近做一个项目,用idea 社区版的   但是缺少了好多功能 无奈只能用专业版的,但是需要注册激活  下面是我的注册方法 1.打开了idea  会提示让激活  选择Licensse server 2. ...