用fock實現簡易shell(程序替換)

fork之後就會創建則進程,數據、堆、棧有兩份,代碼仍然爲一份但是這個代碼段成爲兩個進程的共享代碼段都從fork函數中返回,當父子進程有一個想要修改數據或者堆棧時,兩個進程真正分裂子進程在創建的時候會複製父進程的代碼。

不管是共用代碼還是複製代碼,這就相當於父進程創建了一個和自己功能完全相同的進程,這樣一來,子進程就只能進行和父進程一樣的操作動作,這樣做實際意義並不大,那怎麼讓子進程乾和父進程不一樣的事情?

程序替換:子程序通常調用exec函數,替換掉該進程的用戶空間代碼和數據,從新程序的啓動歷程開始執行,exec函數並不是創建進程,所以exec之後,進程的id並未改變

exec系列函數

需要頭文件  #include <unistd.h>

   int execl(const char *path, const char *arg, ...);
   int execlp(const char *file, const char *arg, ...);
   int execle(const char *path, const char *arg, ..., char * const envp[]);
   int execv(const char *path, char *const argv[]);
   int execvp(const char *file, char *const argv[]);
   int execvpe(const char *file, char *const argv[], char *const envp[]);

l(list):表示採用列表

v(vector):參數用數組

p(path):自動搜索環境變量

e(env):自己維護的環境變量

集體操作看下面代碼

#include<stdlib.h>
#include<unistd.h>

int main()
{
    char* const argv[] = {"ps", "-ef", NULL};
    char* const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

    execl("/bin/ps", "ps", "-ef", NULL);

    //帶p可以使用環境變量PATH,無需路徑
    execlp("ps", "ps", "-ef", NULL);

    //帶e需要自己組裝環境變量
    execle("ps", "ps", "-ef", NULL, envp);

    execv("/bin/ps", argv);

    execvp("ps", argv);

    execve("/bin/ps", argv, envp);

    exit(0);
}

這個程序只需要執行一個上述 exec 函數,就可將這段代碼生成的可執行程序編程ps -ef 這條命令,上述代碼只有一個進程,換成兩個進程,基本操作也是一樣的,如下面代碼,實現最簡shell:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>

char *strs[128] = {};
int argc = 0;

void stok(char *buf, const char *ch)
{
    if(buf == NULL)
    {
        return;
    }
    char *token = strtok(buf, ch);
    strs[argc] = token;
    while((token = strtok(NULL, ch)) != NULL)
    { 
        argc++;
        strs[argc] =token;
    }
    strs[argc+1] = NULL;
}

int main()
{
    char buf[256] = {};
    while(1)
    {
        pid_t pid = fork();
    
        if(pid == 0)
        {
            printf("myshell#");
            scanf("%[^\n]%*c", buf);    
            stok(buf, " "); //打斷buf字符串
            execvp(strs[0], strs);
        }
        else
        {
            int status = 0;
            pid_t id = waitpid(pid,&status, 0);   
            if(id < 0)
            {
                printf("wait runing error!\n");
            }
        }
    }
    return 0;
}

在這段代碼中,子程序的代碼被替換,父進程調用waitpid阻塞式等待子進程返回,子進程返回之後回收子進程的資源,while循環中使用fork,創建子進程,然後在子進程中讀取鍵盤上的輸入,並將讀進的字符在串在空格處打斷,並在每個子串的最後加上\0,讓strs數組中的每個指針都依次指向這些子串,完成之後在strs最後一個元素的後面加上一個空指針,這樣做的是爲了滿足execvp的格式要求,然後調用execvp進行程序替換,替換完完畢之後子程序退出,父進程會受到子進程的資源,然後進入下一次循環。


 scanf("%[^\n]%*c", buf); 

使用snanf是最後的回車也會被輸入,上面這句代碼的意思是:當敲下回車時,這個回車會被屏蔽掉,也可寫爲:

fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;






發佈了67 篇原創文章 · 獲贊 29 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章