课后实践之mybash

实践要求

加分题-mybash的实现

  • 使用fork,exec,wait实现mybash
  • 写出伪代码,产品代码和测试代码
  • 发表知识理解,实现过程和问题解决的博客(包含代码托管链接)

预备知识

  1. 关于bash

    • Bash (GNU Bourne-Again Shell) 是大多数Linux系统以及Mac OS X默认的shell,它能运行于大多数类Unix风格的操作系统之上,甚至被移植到了Microsoft Windows上的Cygwin系统中,以实现Windows的POSIX虚拟接口。此外,它也被DJGPP项目移植到了MS-DOS上。
    • bash的命令语法是Bourne shell命令语法的超集。数量庞大的Bourne shell脚本大多不经修改即可以在bash中执行,只有使用了Bourne的特殊变量或内置命令的脚本才需要修改。 bash的命令语法很多来自Korn shell (ksh) 和 C shell (csh), 例如命令行编辑,命令历史,目录栈,$RANDOM 和 $PPID 变量,以及POSIX的命令置换语法: $(...)。作为一个交互式的shell,按下TAB键即可自动补全已部分输入的程序名、文件名、变量名等等。
  2. 操作系统的核心

    • 文件:字节序列
    • 虚拟内存:字节数组抽象出外设和主存
    • 进程:操作系统对一个正在运行的程序的一种抽象
  3. 操作系统的功能

    • “管家婆”:管理各种硬件资源

    • “服务生” :提供接口

      -->为用户提供shell(写代码)

      -->为程序猿提供系统调用

  4. 关于进程

    • 进程的状态:

      • 创建(created)
      • 执行(running)
      • 就绪(ready)
      • 阻塞(blocked)
      • 终止(terminated)

实践过程

准备工作

  1. 查询相关命令

    • man -k process命令查看与进程(process)相关的命令,发现有很多很多:

    • 再用man -k process | grep 2命令进一步查看与系统调用相关的命令(参数2表示与系统调用有关),找到forkwait命令:



      其描述为:

      fork(2) - create a child process

      即fork命令用来创建一个子进程

      wait(2),wait3(2),wait4(2),waitpid(2) - wait for process termination

      即wait命令用来等待进程结束

      (后来发现只需查找与创建(create)进程相关的命令man -k process | grep 2 | grep create就可一步到位,快速找到fork命令:

    • man -k execute | grep 2命令找到execve命令:



      其描述为:

      execve(2) - execute a file

      即execve命令用来执行文件

  2. 模块分析:

    • exec1.c:

        #include <stdio.h>
      #include <unistd.h> int main() {
      char *arglist[3]; arglist[0] = "ls";
      arglist[1] = "-l";
      arglist[2] = 0;//NULL
      printf("* * * About to exec ls -l\n");
      execvp("ls", arglist);
      printf("* * * ls is done. bye"); return 0;
      }
      • 功能:

        调用execvp()函数,用man命令查看该函数的用法为:exec函数会将当前的进程替换为一个新的进程,这个新的进程可以由路径或者文件参数指定。
      • 运行结果:

    • psh1.c:

        #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <unistd.h> #define MAXARGS 20
      #define ARGLEN 100 int execute(char *arglist[]) {
      execvp(arglist[0], arglist);
      perror("execvp failed");
      exit(1);
      } char *makestring(char *buf) {
      char *cp; buf[strlen(buf) - 1] = '\0';
      cp = malloc(strlen(buf) + 1);
      if (cp == NULL) {
      fprintf(stderr, "no memory\n");
      exit(1);
      }
      strcpy(cp, buf);
      return cp;
      } int main() {
      char *arglist[MAXARGS + 1];
      int numargs;
      char argbuf[ARGLEN]; numargs = 0;
      while (numargs < MAXARGS) {
      printf("Arg[%d]? ", numargs);
      if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')
      arglist[numargs++] = makestring(argbuf);
      else {
      if (numargs > 0) {
      arglist[numargs] = NULL;
      execute(arglist);
      numargs = 0;
      }
      }
      }
      return 0;
      }
      • 功能:一次性bash。
      • 运行结果:

    • forkdemo1.c:

        #include <stdio.h>
      #include <sys/types.h>
      #include <unistd.h> int main() {
      int ret_from_fork, mypid;
      mypid = getpid();
      printf("Before: my pid is %d\n", mypid);
      ret_from_fork = fork();
      sleep(1);
      printf("After: my pid is %d, fork() said %d\n", getpid(), ret_from_fork); return 0;
      }
      • 功能:即fork的功能,除pid之外复制(duplicate)出一个一模一样的子进程,如同克隆。
      • 运行结果:
    • forkdemo2.c:

        #include <stdio.h>
      #include <unistd.h> int main() {
      printf("before:my pid is %d\n", getpid());
      fork();
      fork();
      printf("after:my pid is %d\n", getpid()); return 0;
      }
      • 功能:调用2次fork会出现4个after,调用n次fork会出现2^n个after。
      • 运行结果:
    • forkdemo3.c:

        #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h> int main() {
      int fork_rv; printf("Before: my pid is %d\n", getpid()); fork_rv = fork(); /* create new process */ if (fork_rv == -1) /* check for error */
      perror("fork");
      else if (fork_rv == 0) {
      printf("I am the child. my pid=%d\n", getpid()); exit(0);
      } else {
      printf("I am the parent. my child is %d\n", fork_rv);
      exit(0);
      } return 0;
      }
      • 功能:通过fork()的返回值来区分是父进程还是子进程:如果返回一个大于0的数(子进程的Pid)则为父进程;如果返回值为0则为子进程;如果返回值为负值则出错。
      • 运行结果:
    • forkgdb.c:

        #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h> int gi = 0; int main() {
      int li = 0;
      static int si = 0;
      int i = 0; pid_t pid = fork();
      if (pid == -1) {
      exit(-1);
      } else if (pid == 0) {
      for (i = 0; i < 5; i++) {
      printf("child li:%d\n", li++);
      sleep(1);
      printf("child gi:%d\n", gi++);
      printf("child si:%d\n", si++);
      }
      exit(0); } else {
      for (i = 0; i < 5; i++) {
      printf("parent li:%d\n", li++);
      printf("parent gi:%d\n", gi++);
      sleep(1);
      printf("parent si:%d\n", si++);
      }
      exit(0); }
      return 0;
      }
      • 相关函数:

        • getpid():获得自己的pid
        • getppid():获得父进程的pid
        • sleep():延迟指定数量的时间(作为函数参数,单位为s)
      • 运行结果:

    • waitdemo1.c:

        #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/wait.h>
      #include <unistd.h> #define DELAY 4 void child_code(int delay) {
      printf("child %d here. will sleep for %d seconds\n", getpid(), delay);
      sleep(delay);
      printf("child done. about to exit\n");
      exit(17);
      } void parent_code(int childpid) {
      int wait_rv = 0; /* return value from wait() */
      wait_rv = wait(NULL);
      printf("done waiting for %d. Wait returned: %d\n",
      childpid, wait_rv);
      } int main() {
      int newpid;
      printf("before: mypid is %d\n", getpid());
      if ((newpid = fork()) == -1)
      perror("fork");
      else if (newpid == 0)
      child_code(DELAY);
      else
      parent_code(newpid); return 0;
      }
      • 运行结果:

      • waitdemo2.c:

          #include <stdio.h>
        #include <stdlib.h>
        #include <sys/types.h>
        #include <sys/wait.h>
        #include <unistd.h> #define DELAY 10 void child_code(int delay) {
        printf("child %d here. will sleep for %d seconds\n", getpid(), delay);
        sleep(delay);
        printf("child done. about to exit\n");
        exit(27);
        } void parent_code(int childpid) {
        int wait_rv;
        int child_status;
        int high_8, low_7, bit_7; wait_rv = wait(&child_status);
        printf("done waiting for %d. Wait returned: %d\n", childpid, wait_rv); high_8 = child_status >> 8; /* 1111 1111 0000 0000 */
        low_7 = child_status & 0x7F; /* 0000 0000 0111 1111 */
        bit_7 = child_status & 0x80; /* 0000 0000 1000 0000 */
        printf("status: exit=%d, sig=%d, core=%d\n", high_8, low_7, bit_7);
        } int main() {
        int newpid; printf("before: mypid is %d\n", getpid()); if ((newpid = fork()) == -1)
        perror("fork");
        else if (newpid == 0)
        child_code(DELAY);
        else
        parent_code(newpid);
        }
        • 运行结果:
  3. 伪代码

    mybash:

     for
    {
    用户输入命令;
    fork;
    spid:父进程
    子进程:exec
    }

我的代码

mybash20155314.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> #define MAXARGS 20
#define ARGLEN 100 int mybash20155314(char *arglist[])
{
int pc,pr;
pc=fork();
pr=wait(NULL);
if(pc==0) execute(arglist);
else return 0;
} int execute(char *arglist[])
{
execvp(arglist[0],arglist);
perror("execvp failed");
exit(1);
} char *makestring(char *buf)
{
char *cp;
buf[strlen(buf)-1] = '\0';
cp = malloc( strlen(buf)+1 );
if ( cp == NULL ){
fprintf(stderr,"no memory\n");
exit(1);
}
strcpy(cp, buf);
return cp;
} int main() {
char *arglist[MAXARGS + 1];
int numargs;
char argbuf[ARGLEN]; numargs = 0;
while (numargs < MAXARGS) {
printf("Arg[%d]? ", numargs);
if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')
arglist[numargs++] = makestring(argbuf);
else {
if (numargs > 0) {
arglist[numargs] = NULL;
mybash20155314(arglist);
numargs = 0;
}
}
}
return 0;
}

运行结果

代码调试过程中遇到的问题

macOS High Sierra下终端man命令中文显示问题

解决方法

参考资料

课后实践之mybash20155314的更多相关文章

  1. 第3周课后实践&#183;程序阅读(4)-利用引用訪问私有数据成员

    /* * Copyright (c) 2015, 烟台大学计算机学院 * All rights reserved. * 文件名:test.cpp * 作 者:刘畅 * 完毕日期:2015年 3 月 2 ...

  2. 小垃圾myl的课后实践

    #include<iostream> #include<cstdio> using namespace std; int main(){ ,flag=; printf(&quo ...

  3. 00java语法基础和课后实践

    一:运行代码,并分析结果 代码1: package reserve; public class Main { public static void main(String[] args) { Size ...

  4. 零基础python入门(1)

    1.前景及准备 (1).python是一门简单易学且功能强大的编程语言.它拥有高效的高级数据结构,并且能用简单而又高效的方式进行面向对象的编程.python优雅的语法和动态的类型,再结合它的解释性,使 ...

  5. 20155216 2016-2017-2《Java程序设计》课程总结

    20155216 2016-2017-2<Java程序设计>课程总结 (按顺序)每周作业链接汇总 预备作业1:简要内容:我对师生关系的见解 预备作业2:简要内容:有关C语言学习调查以及学习 ...

  6. 20155322 2017-2018-1 《信息安全系统设计》第五周 MyBash实现

    #20155322 2017-2018-1<信息安全系统设计>第五周 MyBash实现 [博客目录] 实现要求 相关知识 bash fork exec wait 相关问题 fork返回两次 ...

  7. HTMl+CSS 模仿京东网登录页面

    课后实践项目,仅页面效果,写博客纯属记录! 码云开源仓库地址:https://gitee.com/ynavc/jd 演示地址:https://ynavc.gitee.io/jd 效果图: 实现代码: ...

  8. 《Java 程序设计》课堂实践项目 课后学习总结

    <Java 程序设计>课堂实践项目 课后学习总结 String类的使用(sort) 目录 Linux命令(sort) 课堂实践 课后思考 学习老师的代码之后的思考:int与Integer ...

  9. 《python编程:从入门到实践》课后习题及答案

    转载: <Python编程:从入门到实践>课后习题及答案-码农之家 (xz577.com) <Python编程:从入门到实践>课后习题及答案 - 信德维拉 - 博客园 (cnb ...

随机推荐

  1. hdu 1430

    魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  2. Highchar.js插件提示框千分位显示为空格而不是逗号 --(2018 08/06-08/12周总结)

    1.Oracle在已经存在主键的表中插入复合主键的SQL语句 如已有一个表test_key,其中a1列为主键. CREATE TABLE TEST_KEY( A1 VARCHAR2(3) NOT NU ...

  3. ASP.NET Core 2 学习笔记(十四)Filters

    Filter是延续ASP.NET MVC的产物,同样保留了五种的Filter,分别是Authorization Filter.Resource Filter.Action Filter.Excepti ...

  4. 关于JS中闭包的问题

    一直以来,我都以为我已经懂了JavaScript中闭包的概念,直到有一次小伙伴突然问我这个概念的时候,我才发现我根本不知道该怎来么跟他来讲述这个概念. 那时候我就知道我是自我欺骗,打肿脸充胖子了. 所 ...

  5. HTML之列表

    列表有三种类型: 有序列表:列表项使用数字来标记 无序列表:列表项使用粗体圆点(典型的小黑圆圈)进行标记. 自定义列表:自定义列表以 <dl> 标签开始.每个自定义列表项以 <dt& ...

  6. 关于android R.layout 中找不到已存在的布局文件问题的解决

    今天遇到一个很奇怪的问题,打R.layout.,居然不会提示已经写好的布局文件,自己把xml文件名打下去后,居然提示错误. 开始以为是R文件中没有自动生成关于布局文件对应的整型,看了R文件,其实是有生 ...

  7. ios移动端禁止双指缩放功能

    在实际开发中,我们禁止缩放的实现方式: 1.meta设置: <meta name="viewport"  content="width=device-width,h ...

  8. 如何从本地添加项目到Github?(Windows)

    有两种方法可以上传项目到Github 一.github在线上传文件夹 在线上传也可以上传完整的文件夹结构,直接拖拽到上传文件页面的框中即可. 点击上传文件 直接拖拽即可上传文件夹及文件夹里面的文件.如 ...

  9. Pig安装

    环境: hadoop-2.4.1.jdk1.6.0_45.pig-0.12.1   1.下载pig并解压 tar -xzvf pig-0.12.1.tar.gz 2.设置环境变量 export PIG ...

  10. 7 Recursive AutoEncoder结构递归自编码器(tensorflow)不能调用GPU进行计算的问题(非机器配置,而是网络结构的问题)

    一.源代码下载 代码最初来源于Github:https://github.com/vijayvee/Recursive-neural-networks-TensorFlow,代码介绍如下:“This ...