课后实践之mybash20155314

课后实践之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. 关于进程

    NsTdx.png

    • 进程的状态:
      • 创建(created)
      • 执行(running)
      • 就绪(ready)
      • 阻塞(blocked)
      • 终止(terminated)

实践过程

准备工作

  1. 查询相关命令
    • man -k process命令查看与进程(process)相关的命令,发现有很多很多:
      NwASA.png
    • 再用man -k process | grep 2命令进一步查看与系统调用相关的命令(参数2表示与系统调用有关),找到forkwait命令:
      NwEQI.png
      其描述为:

      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命令:
      Nwmef.png

    • man -k execute | grep 2命令找到execve命令:
      NwMFg.png
      其描述为:

      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命令查看该函数的用法为:NsbFK.pngexec函数会将当前的进程替换为一个新的进程,这个新的进程可以由路径或者文件参数指定。
      • 运行结果:
        NrI58.png
    • 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。
      • 运行结果:
        NyuT0.png
    • 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)出一个一模一样的子进程,如同克隆。
      • 运行结果:NyG6J.png
    • 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。
      • 运行结果:NytmR.png
    • 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则为子进程;如果返回值为负值则出错。
      • 运行结果:Ny3pF.png
    • 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)
      • 运行结果:Nydk6.png
    • 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;
        }
      • 运行结果:NyDpD.png

      • 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);
          }
        • 运行结果:Ny0fO.png
  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;
}

运行结果

Nypwt.png

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

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

NsxOA.png

解决方法

NySeI.png

参考资料

转载于:https://www.cnblogs.com/crazymosquito/p/7712151.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值