作業系統實習課(一) -處理程序相關指令介紹- 助教:鄧執中 Email:steven1991@ares.ee.nchu.edu.tw
處理成序 每個執行中的程式會構成一個處理程序 處理程序包含程式碼、資料、變數、檔案描述子與環境
在終端機內鍵入ps指令可以得到系統處理程序的資料 命令參數: a 顯示所有進程 -a 顯示同一終端下的所有程式 -A 顯示所有進程 c 顯示進程的真實名稱 -N 反向選擇 e 顯示環境變數 f 顯示程式間的關係 -H 顯示樹狀結構 PID(process identifier): 處理程序辨識子,介於2-32768之間。每當一個處理程序啟動便會依序指定 下一個未用的數字。 第1號處理程序為init,他是Linux系統啟動後執行的第一個程式,可以稱他為 處理程序管理者,所有的程序都是由init啟動,因此他也是所有處理程序的 父程序。
處理程序相關指令(exec、fork、waitpid) #include <unistd.h> extern char **environ; 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[]); /*對於類 Unix 系統,unistd.h 中所定義的介面通常都是大量針對系統調用的封裝,如 fork、pipe 以及各 種 I/O 原語。*/
Exec使用範例: Char *const ps_argv[]={"ps","-ag",0}; Char *const ps_envp[]={"PATH=/bin:/usr/bin","TERM=console",0}; execel("/bin/ps","ps","-ax",0); execelp("ps","ps","-ax",0); execelv("/bin/ps","ps","-ax",0,ps_envp); execv("/bin/ps",ps_argv); execvp("ps",ps_argv); execve("/bin/ps",ps_argv,ps_envp);
範例(一):execlp #include <unisted.h> #include <stdio.h> #include <stdlib.h> Int main() { printf(“Running ps with execlp\n”); execlp(“ps”,”ps”,”-af”,0); printf(“Done!!\n”); exit(0); } /*exit函數用於结束正在運行的程序,exit函數將參數返回给OS。一般0代表程序正常結束,1 代表不正常。Exit會刪除程序使用的内存空間,同時把錯誤訊息(1)返回給父程序。此函數包含 在<stdlib.h>中。*/
fork:會把原本的程序複製一份,原本的程序成為父程序而複製出的就 是子程序,兩個程序大致相同,但有屬於各自的資料空間、環境變數 與檔案描述子。複製完後都從fork下一行開始執行。 父處理程序呼叫fork會回傳一程序的PID,而子處理程序會回傳0。如果 fork失敗則會回傳-1。(可以看成fork會回傳子處理程序的PID,但此處產生的子處理程序 底下已經沒有其他子處理程序了所以回傳0) 要用到fork我們需要設一個變數來接fork的回傳值。因為fork會回傳PID 所以系統有個專門的int資料型態pid_t,像這種基本系統數據類型包含 在sys/types.h裡。 fork出錯可能有兩種原因: 1.當前的程序數已經達到了系統規定的上限,這時errno的值被設置 為EAGAIN。 2.系統內存不足,這時errno的值被設置為ENOMEM。
殭屍程序<zombie>、<defunct> 當一個子程序已運作完成,還必須等待父程序結束或是父函式使 用wait函式才會真正的結束,在此發生之前子程序會變成僵屍程 序。(父程序不正常結束也會造成殭屍程序的產生) 殭屍程序仍然占用系統資源與處理程序,所以必須阻止他的產生。 當父程序不正常終止,子程序會交給init管理。init會定期重新整 理並把殭屍程序清理乾淨。
範例二(fork): getpid:系統會讀取自身的PID getppid:系統會讀取父程序的PID #include <unistd.h> #include < sys/types.h> #include <stdio.h> int main () { pid_t fpid; int count=0; printf("I am the original process, my process id is %d\n",getpid()); fpid=fork(); if (fpid < 0) printf("error in fork!"); else if (fpid == 0) { printf(“I am the child process, my process id is %d\n",getpid()); printf("我是爹的兒子\n"); count++; } else { printf(“I am the parent process, my process id is %d\n",getpid()); printf("我是孩子他爹\n"); printf("統計結果是: %d\n",count); return 0; } getpid:系統會讀取自身的PID getppid:系統會讀取父程序的PID
範例三(fork): 1316 1316 5844 1316 第一次fork 5964 5964 我也成為爹了 6008 #include <unistd.h> #include <stdio.h> #include < sys/types.h> int main(void) { int i=0; printf("i son/pa ppid pid fpid\n"); //ppid指當前程序的父程序pid //pid指當前程序的pid, //fpid指fork回傳給當前程序的值 for(i=0;i<2;i++){ pid_t fpid=fork(); if(fpid==0) printf("%d child %4d %4d %4d\n",i,getppid(),getpid(),fpid); else printf("%d parent %4d %4d %4d\n",i,getppid(),getpid(),fpid); } return 0; 1316 第二次fork 5844 1316 第一次fork 5964 5964 我也成為爹了 第二次fork 6008
以上的範例也有可能出現這個結果WHY? i son/pa ppid pid fpid 0 parent 2043 3224 3225 0 child 3224 3225 0 1 parent 2043 3224 3226 1 parent 3224 3225 3227 1 child 1 3227 0 1 child 1 3226 0
Wait:此函式會讓父程式暫停直到他的子處理程序結束,並回傳子程序 的狀態資訊如PID,結束碼等..) 函式原型: #include <> pid_t wait(int *status); 以下為sys/wait.h定義的巨集: WIFEXITED(status) 如果子程序正常结束,回傳一個非零值 WEXITSTATUS(status) 如果WIFEXITED非零,回傳子程序退出碼 WIFSIGNALED(status) 子程序因为捕獲信號而终止,回傳非零值 WTERMSIG(status) 如果WIFSIGNALED非零,回傳信號代碼 WIFSTOPPED(status) 如果子程序被暂停,回傳一個非零值 WSTOPSIG(status) 如果WIFSTOPPED非零,回傳一個信號代碼
範例四(wait): default: message = "This is the parent"; n = 3; exit_code = 0; break; } for(; n > 0; n--) { puts(message); sleep(1); if(pid !=0) { int status; pid_t child_pid; child_pid = wait(&status); printf("Child has finished: PID = %d\n", child_pid); if(WIFEXITED(status)) printf("Child exited with code %d\n", WEXITSTATUS(status)); else printf("Child terminated abnormally\n"); exit (exit_code); #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> int main() { pid_t pid; char *message; int n; int exit_code; printf("fork program starting\n"); pid = fork(); switch(pid) case -1: perror("fork failed"); exit(1); case 0: message = "This is the child"; n = 5; exit_code = 37; break;
pid_t waitpid(pid_t pid,int *status,int options); pid: pid>0:等待程序ID等於 pid的子程序,不管其它已經有多少子程序運行結束退出,只要指定的子 程序還没有结束,waitpid就會一直等下去。 pid=-1:等待任何一個子程序退出,没有任何限制,此時若options設為0則waitpid和wait的作用一樣。 pid=0:等待同一個程序組中的任何子程序,如果子程序已經加入了别的程序組,waitpid不會對 它做任何理會。 pid<-1:等待一個指定程序組中的任何子程序,這個程序組的ID等於pid的絕對值。 options(設為0為不使用): WNOHANG:如果没有任何已終止的程序,它也會立即返回,不會像wait那樣永遠等下去。 WUNTRACED:如果子程序進入暫停執行則馬上返回,但終止狀態不予理會。
習題: 將其中執行echoll取代命令改為第一次執行ls -l、第二次執行ps -a #include <sys/types.h> #include <sys/wait.h> #include "ourhdr.h" char *env_init[]={"USER=unknown","PATH=/tmp",NULL}; int main() { pid_t pid; if((pid=fork())<0) err_sys("fork error"); else if (pid= =0) { if (execle("/home/stevens/bin/echoall","echoll","myarge1","MY ARG2",(char*) 0,env_init)<0) err_sys("execle error"); } if (waitpid(pid, NULL,0)<0) err_sys("wait error"); if ((pid=fork())<0) else if (pid= =0){ if (execlp("echoall","only 1 arg", (char *) 0)<0) err_sys("execlp error"); exit(0);