linux系統編程綜合練習-實現一個小型的shell程式(四)

來源:互聯網
上載者:User

標籤:style   blog   http   color   使用   os   strong   io   

上節中已經對後台作業進行了簡單處理,基本上要實現的功能已經完了,下面回過頭來,對代碼進行一個調整,把寫得不好的地方梳理一下, 給代碼加入適當的注釋,這種習慣其實是比較好了,由於在開發的時候時間都比較緊,都只是想辦法去儘快實現,而肯定會有一些代碼是寫得不太好的,所以有時間的話最好是從頭至尾將整個代碼進行梳理,也許在梳理的過程中會發現許多不足的地方,好了,下面開始:而這個訊號安裝函數是在init.c中實現的:接下來進行shell迴圈:它的實現是在parse.c中:如注釋所示,可以挪至init.c中:接下來,擷取命令:然後解析命令:接下來的這句,是為了測試,在發布時可以注釋掉了:最後執行命令:這個方法裡面的代碼有點亂,下面將其實現抽取到另外一個檔案中,使得該函數要看起來清爽一些:其實現execute.c:
#include "execute.h"#include "def.h"#include "externs.h"#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <linux/limits.h>#include <fcntl.h>void forkexec(int i){    pid_t pid;    pid = fork();    if(pid == -1) {        /* 建立進程失敗了 */        ERR_EXIT("fork");    }    if(pid > 0) {        /* 父進程 */        if (backgnd == 1)            printf("%d\n", pid);        lastpid = pid;    } else if(pid == 0) {        /* 子進程 */        /* 表示將第一條簡單命令的infd重新導向至/dev/null,其中cmd[i].infd == 0隻有可能是第一條簡單命令 */        /* 當第一條命令試圖從標準輸入擷取資料的時候立既返回EOF */        if(cmd[i].infd == 0 && backgnd == 1){            //屏蔽後台作業,因為沒有實現作業控制            cmd[i].infd = open("/dev/null", O_RDONLY);        }        /* 將第一個簡單命令進程作為進程組組長 */        if(i == 0){            setpgid(0, 0);        }        if(cmd[i].infd != 0){            //說明該命令的輸入是指向管道的讀端            close(0);            dup(cmd[i].infd);        }        if(cmd[i].outfd != 1){            //說明該命令的輸出指向的是管道的寫端            close(1);            dup(cmd[i].outfd);        }        /* 關閉3以上的所有檔案描述符 */        /*int i;        for(i=3; i<OPEN_MAX; ++i){            close(i);        }*/        /*前台作業能夠接收SIGINT,SIGQUIT訊號,這兩個訊號就要恢複成預設操作*/        if(backgnd == 0){//非後台作業            signal(SIGINT, SIG_DFL);            signal(SIGQUIT, SIG_DFL);        }        /* 開始替換進程 */        execvp(cmd[i].args[0], cmd[i].args);        /* 如果執行到這句,則證明替換失敗了 */        exit(EXIT_FAILURE);    }}int execute_disk_command(void){    /* ls | grep init | wc -w */    if(cmd_count == 0) {        return 0;    }    if(infile[0] != ‘\0‘){        cmd[0].infd = open(infile, O_RDONLY);    }    if(outfile[0] != ‘\0‘){        if(append)//說明是以追加的方式            cmd[cmd_count-1].outfd = open(outfile, O_WRONLY | O_CREAT | O_APPEND, 0666);        else             cmd[cmd_count-1].outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);    }    /* 因為後台作業不會調用wait等待子進程退出,為避免殭屍進程,可以忽略SIGCHLD訊號 */    if(backgnd == 1){        signal(SIGCHLD, SIG_IGN);    }else{        signal(SIGCHLD, SIG_DFL);    }    int i;    /* 管道描述符 */    int fds[2];    int fd;    for(i=0; i<cmd_count; ++i){        /* 如果不是最後一條命令,則需要建立管道 */        if(i < cmd_count-1){            pipe(fds);            /* 第一條命令的輸出不再是標準輸出,而是管道的寫端 */            cmd[i].outfd = fds[1];            /* 第二條命令的輸入不再是標準輸入,而是管道的讀端 */            cmd[i+1].infd = fds[0];        }                /* 建立一個進程,並且替換成系統命令 */        forkexec(i);                if((fd = cmd[i].infd) != 0)            close(fd);        if((fd = cmd[i].outfd) != 1)            close(fd);    }    if(backgnd == 0){//如果是非後台作業        while(wait(NULL) != lastpid)            ;        }}

將其forkexec函數也抽取到execute.c檔案中,下面來進行編譯一下:

另外在編譯成,需要修改一下Makefile:

修改這麼多後下面編譯一下:好了,對於上面的實現都解釋的外部命令,那對於系統的內部命令還需要相容,下面主要是來實現內部命令的解析:首先要判斷是否是內部命令,如果是,則執行內部命令:另外需要將builtin.h包含在parse.c檔案中:下面來編譯一下:說明忘了將builtin.o檔案加入到Makefile中了,修改並再編譯:一大堆錯,不用著急,一個個解決,首先builtin.c檔案中也需要用到check函數,而之前是定義在parse.c中,這時應該將其定義在parse.h中,讓builtin.c來包含它既可,另外還需包含一些系統標頭檔:在builtin.c中去包含parse.h檔案:下面再來make並執行:思考一個問題:系統有大量的內部命令,那是不是每解析一個內部命令,我們都要在builtin.c中加一個判斷語句,這樣會造成builtin函數會越來越龐大,所以這種實現方式還是不太靈活,下面改用數組來避免這種情況的發生:最後builtin.c的代碼如下:
#include "builtin.h"#include "parse.h"#include "externs.h"#include <stdlib.h>#include <stdio.h>typedef void (*CMD_HANDLER)(void);typedef struct builtin_cmd{    char *name;    CMD_HANDLER handler;} BUILTIN_CMD;void do_exit(void);void do_cd(void);void do_type(void);BUILTIN_CMD builtins[] = {    {"exit", do_exit},    {"cd", do_cd},    {"type", do_type},    {NULL, NULL}};/* * 內部命令解析 * 返回1表示為內部命令,0表示不是內部命令 */int builtin(void){    /*    if (check("exit"))        do_exit();    else if (check("cd"))        do_cd();    else        return 0;    return 1;    */    int i = 0;    int found = 0;    while (builtins[i].name != NULL)    {        if (check(builtins[i].name))        {            builtins[i].handler();            found = 1;            break;        }        i++;    }    return found;}void do_exit(void){    printf("exit\n");    exit(EXIT_SUCCESS);}void do_cd(void){    printf("do_cd ... \n");}void do_type(void){    printf("do_type ... \n");}

編譯運行:

好了,關於內部命令的具體實現這裡就不多說了,主要是還是實現其原理,這樣自己的一個小型的shell程式就已經完成了,跟系統的shell程式還相差很多,但是通過這個程式足以可以將之前學的知識給串接起來, 達到一個很好的練習的目的,關於小型shell程式最終就實現到這,東西還是比較多,需好好消化。【說明】:由於linux系統命令太多太多,下面只是簡單實現幾個,做一個範例,重點是知道其實現原理。  下面列一下該程式中使用到的各個檔案的作用:main.c----主調程式def:h----定義常量,結構體externs.h----定義extern的變數init.h/init.c----做一些初始化操作parse.h/parse.c----做命令的解析execute.h/execute.c----做外部命令的執行builtin.h/builtin.c----做內部命令的執行【只實現其原理】 
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.