1. 程式人生 > >2018-2019-1 20189205《Linux核心原理與分析》第七週作業

2018-2019-1 20189205《Linux核心原理與分析》第七週作業

程序的描述與程序的建立

程式設計實現一個一個具有執行命令功能的shell

主要思路是通過利用exec函式族來實現使用者輸入的命令,但是呼叫exec函式族將會覆蓋源程式,因此需要先使用fork()函式生成子程序,在子程序中呼叫exec函式族,而父程序則使用wait()函式等待子程序完成。
本程式使用的exec函式execve(),函式原型為int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

  • 第一個引數filename儲存的是執行命令的可執行檔案,由於shell中命令的執行檔案是儲存在/bin/
    目錄下,因此只需在字串"/bin/"後拼接上輸入的命令;
  • 第二個引數argv[ ]型別為char * const,儲存的是命令的各個引數,生成時把使用者輸入的命令中的各個引數儲存在一個字串陣列中,將argv引數指向各個字串,最後以NULL結尾;
  • 第三個引數envp[ ]儲存的則是傳遞給執行檔案的新環境變數陣列,直接定義為環境變數PATH
    程式程式碼如下:
//myshell.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>

int main ()
{
    pid_t pid;
    int status = -1 ,flag =0,i=0,len=0,n=0,m=0;
    char code[80],s_code[20][20];
    char file[30]="/bin/";
    char* argv[80];         //儲存命令的引數 
    char* envp[ ]={"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",0};//儲存PATH 
    
    while (flag == 0)
    {
        printf("9205-myshell:/$ ");
        fgets(code,80,stdin);   //讀取命令 
        if (code[0]=='e' && code[1]=='x' && code[2]== 'i' && code[3]== 't' ) flag = 1;  //如果命令是exit則退出程式 
        else
        {    
            //shell
            pid = fork();
            if (pid < 0) printf("error1!!");    //分化程序錯誤 
            else if (pid > 0)   //父程序 
            {
                wait(&status);  //父程序等待子程序完成 
            }
            else if(pid==0)     //子程序 
            {
                //將命令中的各個引數儲存進 argv
                while (code[i] !='\n' )
                {
                    if (code[i]!=' ') 
                    {
                        s_code[m][n]=code[i];
                        n++;
                    }
                    else 
                    {
                        s_code[m][n]='\0';
                        argv[m]=s_code[m];
                        m++;
                        n=0;
                    }
                    i++;
                }
                    s_code[m][n]='\0';
                    argv[m]=s_code[m];
                    m++;
                    n=0;
                    argv[m]=(char *)0;

                //構成filename
                strcat(file,s_code[0]);
                
                //執行命令 
                execve(file, argv, envp);
                
                //若執行到以下步驟則呼叫命令時發生錯誤 
                printf ("error2!!");
                exit(0);
            }
        }
        
    }
    return 0;
}

程式執行效果如下圖所示:

學習測試wait()waitpid()函式

wait( )函式
其函式功能為等待子程序中斷或結束,函式原型為pid_t wait (int * status);
其引數 status 是一個整形指標。如果status不是一個空指標,則終止程序的終止狀態將儲存在該指標所指向的記憶體單元中。如果不關心終止狀態,可以將 status引數設定為NULL。以下有幾個巨集可判別status中儲存的結束情況:

  • WIFEXITED(status)如果若為正常結束子程序返回的狀態,則為真;對於這種情況可執行WEXITSTATUS(status),取子程序傳給exit或_eixt的低8位。
  • WEXITSTATUS(status)取得子程序 exit()返回的結束程式碼,一般會先用 WIFEXITED 來判斷是否正常結束才能使用此巨集。
  • WIFSIGNALED(status)若為異常結束子程序返回的狀態,則為真;對於這種情況可執行WTERMSIG(status),取使子程序結束的訊號編號。
  • WTERMSIG(status) 取得子程序因訊號而中止的訊號程式碼,一般會先用 WIFSIGNALED 來判斷後才使用此巨集。
  • WIFSTOPPED(status) 若為當前暫停子程序返回的狀態,則為真;對於這種情況可執行WSTOPSIG(status),取使子程序暫停的訊號編號。
  • WSTOPSIG(status) 取得引發子程序暫停的訊號程式碼,一般會先用 WIFSTOPPED 來判斷後才使用此巨集。

呼叫 wait 函式時,呼叫程序將會出現下面的情況:

  • 如果其所有子程序都還在執行,則阻塞。
  • 如果一個子程序已經終止,正等待父程序獲取其終止狀態,則獲取該子程序的終止狀態然後立即返回。
  • 如果沒有任何子程序,則立即出錯返回。
    wait( )函式如果執行成功則返回子程序識別碼(PID),如果有錯誤發生則返回-1。失敗原因存於errno 中。

waitpid()函式
其函式功能為:暫時停止目前程序的執行,直到有訊號來到或子程序結束。與wait( )函式相似但功能比其更加強大。
其函式原型為pid_t waitpid(pid_t pid,int * status,int options);
第一個引數pid為欲等待的子程序識別碼,其數值意義如下:

  • pid<-1 等待程序組識別碼為 pid 絕對值的任何子程序。
  • pid=-1 等待任何子程序,相當於 wait()。
  • pid=0 等待程序組識別碼與目前程序相同的任何子程序。
  • pid>0 等待任何子程序識別碼為 pid 的子程序。
    第二個引數status將儲存子程序的終止狀態。
    第三個引數options提供了一些額外的選項來控制waitpid,引數 option 可以為 0 或可以用"|"運算子把它們連線起來使用,其選項包括如下:
    WNOHANG 若pid指定的子程序沒有結束,則waitpid()函式返回0,不予以等待。若結束,則返回該子程序的ID。
    WUNTRACED 若子程序進入暫停狀態,則馬上返回,但子程序的結束狀態不予以理會。
    WIFSTOPPED(status)巨集確定返回值是否對應與一個暫停子程序。
    waitpid( )函式如果執行成功則返回子程序識別碼(PID),如果有錯誤發生則返回-1。失敗原因存於errno 中。

測試程式
包括了使用了wait( )函式的wait測試程式和沒事用函式的sleep對比程式,以對照檢視wait( )函式的執行結果。

//wait測試程式
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int num1,num2,status=0;
    
    //在無子程序的狀態下執行wait() 
    num1=wait(&status);
    printf("the wait1:%d\n",num1);

    pid = fork();
    if (pid < 0) printf("error 1!!");
    else if (pid == 0)  //子程序 
    {
        int i;
        printf("i an the son | the pid:%d | the parents pid:%d\n",getpid(),getppid());
        for (i=0; i<5 ;i++) //子程序sleep 
        {
            sleep(1);
            printf ("sleep-----%d\n",i);
        }
    }
    else if (pid > 0)  //父程序
    {
        num2=wait(&status); //父程序wait() 
        printf ("i am the parents | the pid:%d | the wait2:%d\n",getpid(),num2);
    }

    return 0;
}
//對比sleep程式
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t pid;
    int num1,num2,status=0;

    pid = fork();
    if (pid < 0) printf("error 1!!");
    else if (pid == 0)  //子程序
    {
        int i;
        printf("i an the son | the pid:%d | the parents pid:%d\n",getpid(),getppid());
        for (i=0; i<5 ;i++)
        {
            sleep(1);
            printf ("sleep-----%d\n",i);
        }
    }
    else if (pid > 0)  //父程序
    {
        printf ("i am the parents | the pid:%d \n",getpid());
    }

    return 0;
}

執行結果如圖所示: