2018-2019-1 20189205《Linux核心原理與分析》第七週作業
阿新 • • 發佈:2018-11-25
程序的描述與程序的建立
程式設計實現一個一個具有執行命令功能的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;
}
執行結果如圖所示: