zoukankan      html  css  js  c++  java
  • 20155219 mybash的实现

    第五周加分题--mybash的实现

    题目要求

    1.使用fork,exec,wait实现mybash

    2.写出伪代码,产品代码和测试代码

    3.发表知识理解,实现过程和问题解决的博客(包含代码托管链接)

    exec函数组

    一个程序如何运行另一个程序呢?

    我们得搞清楚需要调用什么函数来完成这个过程。如果想使用man -k xxx这个命令进行搜索.

    试试man -k program | grep execute 如下图:

    再进一步使用man -k execute搜索,通过观察说明,我们找到了一系列相关的函数:

    它们的命名是有规律的:

    exec[l or v][p][e]

    exec函数里的参数可以分成3个部分:执行文件部分,命令参数部分,和环境变量部分。

    假如要执行:ls -l /etc

    • 执行文件部分就是:"/usr/bin/ls"

    • 命令参数部分就是:"ls","-l","/etc",NULL

    • 环境变量部分:这是1个数组,最后的元素必须是NULL 例如:char * env[] = {"PATH=/etc", "USER=vivian", "STATUS=testing", NULL};
      命名规则如下:

    • e:参数必须带环境变量部分,环境变量部分参数会成为执行exec函数期间的环境变量;

    • l:命令参数部分必须以"," 相隔, 最后1个命令参数必须是NULL;

    • v:命令参数部分必须是1个以NULL结尾的字符串指针数组的头部指针。例如char * pstr就是1个字符串的指针, char * pstr[] 就是数组了, 分别指向各个字符串;

    • p:执行文件部分可以不带路径, exec函数会在$PATH中找。

    我们主要来学习execvp函数

    代码如下

    #include <unistd.h>  
      
    int main()  
    {  
            char *argv[] = {"ls", "-l", ".", (char *)0};  
            printf("···Begin to Show ls -l···
    ");
            execvp("ls", argv);  
            printf("···ls -l is done! ···");
            return 0;  
    }
    

    结果如下图所示:

    遇到的问题:execvp后面的那一条printf打印的消息没有输出?
    • 解决方案:一个程序在一个程序中运行时,内核将新程序载入到当前进程,替代当前进程的代码和数据。如果执行成功,execvp没有返回值。当前程序从进程中清除,新的程序在当前进程中运行。类比execv函数组,系统调用从当前进程中把当前程序的机器指令清除,然后在空的进程中载入调用时指定的程序代码,最后运行这个新的程序。exec调整进程的内存分配使之适应新的程序对内存的要求。相同的进程,不同的内容。

    fork()

    通过man -k fork命令进行搜索,可以看到,fork函数位于manpages的第二节,与系统调用有关。

    如图1

    使用man 2 fork命令查看fork函数,可以看到关于fork函数的所有信息:

    image
    fork函数的结构大致如下:

    #include <sys/types.h>
    #include <unistd.h>
    
    pid_t fork(void);
    
    //返回:子进程返回0,父进程返回子进程的PID,如果出错,则返回-1。
    

    一般来说,运行一个C程序直到该程序全部结束,系统只会分配一个PID给这个程序,也就是说,系统里只有一条关于这个程序的进程。但执行了fork函数就不同了。fork()的作用是复制当前进程(包括进程在内存的堆栈数据),然后这个新的进程和旧的进程一起执行下去。而且这两个进程是互不影响的。如下图的逻辑所示。
    image

    
    #include <sys/types.h>
    #include <unistd.h>
     int main(){  
            printf("step 1
    
    ");  
          
            fork();//创建一个新的进程
          
            printf("after fork()
    
    ");  
          
            int i; scanf("%d",&i);//防止程序退出  
    
            return 0;  
        }  
    

    得到结果如下图:

    wait()

    一个进程可以通过调用wait函数来等待它的子进程终止或者停止。

    使用man -k wait查看与“wait”相关的信息
    如下图:

    再使用man 2 wait命令查看详细信息:

    image

    wait()的使用方法可以用下面的代码表示:

    #include <sys/types.h>
    #include <unistd.h>
    
    pid_t wait(int *status);
    
    //返回:如果成功,则返回子进程的PID,如果出错,则返回-1。
    
    

    父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

    需要注意的几点是:

    • 当父进程忘了用wait()函数等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程。

    • wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID。

    • 如果先终止父进程,子进程将继续正常进行,只是它将由init进程(PID 1)继承,当子进程终止时,init进程捕获这个状态。

    编程实现mybash

    伪代码如下:

    int main(){
    读取要执行的命令
    使用fork()函数产生子进程进行执行
    如果exec函数产生了返回值,表明没有正常执行命令,输出perro()
    父进程等待子进程结束,并输出值wait(&rtn) 
    
    }
    

    产品代码链接

    输出结果如下

  • 相关阅读:
    VS停止调试,IIS Express也跟着关闭了
    会钓鱼的程序员
    彻底搞懂https原理
    Java小知识
    ORA-01000: 超出打开游标的最大数(java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误)
    虚拟机电脑重启后连接不上ORACLE
    电视直播源
    国内开源镜像站
    分享一波泰勒斯威夫特手机高清壁纸
    阿里云网盘内测申请
  • 原文地址:https://www.cnblogs.com/paypay/p/8010941.html
Copyright © 2011-2022 走看看