何謂進程?進程就是一個正在運行着的程序實例。
在Linux中,每一個進程都有一個進程號(Process ID)來標示身份。
在shell中你可以通過ps命令來查看。
[liyong@localhost temp]$ ps PID TTY TIME CMD 17197 pts/8 00:00:00 bash 31141 pts/8 00:00:00 ps |
關於ps的詳細用法參考man手冊。
PID下面的數字就是進程號,比如正在執行的bash的進程號是17197,ps的進程號是31141。
進程號是一個16位的數字,也就是說,一個系統中頂多只能由2^16-1個進程。
你在程序中可以通過下面函數來獲得進程號。
pid_t getpid()
pid_t pid=getpid(); printf(“%d/n”,(int)pid); |
每一個進程都有其父進程。你在shell下直接運行的進程,其父進程就是bash。系統中只有一個進程沒有父進程,就是init。它的進程號永遠爲1。所有進程都是init進程的子孫進程。
你可以通過下面的函數來獲得其父進程號。
pid_t getppid(); |
進程可以通過自身的停止執行來結束,也可以通過kill命令來殺死。
[liyong@localhost ~]$ ps PID TTY TIME CMD 17197 pts/8 00:00:00 bash 31213 pts/8 00:00:00 find 31220 pts/8 00:00:00 ps [liyong@localhost ~]$ kill 31213 [liyong@localhost ~]$ ps PID TTY TIME CMD 17197 pts/8 00:00:00 bash 31221 pts/8 00:00:00 ps |
kill其實是向進程發送一個SIGTERM信號。
下面談一下如何創建一個進程。
創建一個進程由兩種常用的方法:system 和fork/exec family。
system方法比較低效,通常不用。在這裏重點說明fork/exec family用法。
使用fork來創建一個新的進程,其實是將進程自身複製成一個新的進程。記住,是複製,不是共享。你可以通過fork函數的返回值來區分該進程是父進程還是子進程。對於父進程,fork返回了子進程的pid。對於子進程而言,fork返回0。
下面是一個簡單的fork程序。
#include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h>
int main() { int i=0; pid_t child_pid=fork(); if(child_pid==0) {//child process for(i=0;i<100;i++) { printf("%d:%d/n",getpid(),i); fflush(stdout); } exit(0); } else { for(i=0;i<100;i++) { printf("%d:%d/n",getpid(),i); fflush(stdout); } exit(0); } } |
exec是一系列函數族。它和fork不同的是,他請求運行的程序將代替原先正在運行的程序。該函數執行完後,原先運行的程序將被終止,請求運行的程序開始運行,而進程號不變。
exec有一系列寒暑,他們區分如下:
l 如果名字裏含有p,比如execvp,execlp,那麼表示他們運行的程序地址就在當前目錄下,而如果沒有,則需要寫出程序的絕對地址。
l 如果名字裏有v,則表示傳遞給即將運行的程序的參數是字符串,而如果是l,則表示參數格式採用的是c語言裏的變參數。
如果傳遞的參數是字符串,需要注意的是該字符串內容和在命令行下執行程序時傳遞的參數一樣,也就是說字符串從argv[0]到argv[argc-1]。且argv[0]就是該程序的名字。另外,該字符串必須以NULL來結尾。
參考下面一個程序。
#include <stdio.h> #include <unistd.h> #include <sys/types.h>
int main() { char * argu_list[]={"ls","/","-l",NULL}; int status=0; pid_t child_pid=fork(); if(child_pid==0) { //child process execvp("ls",argu_list); fprintf(stderr,"exec ls error/n"); exit(1); } else { //parent process wait(&status); if(WIFEXITED(status)) printf("the child process exit normally./n"); else printf("the child process exit abnormally./n"); printf("done with the main program./n"); exit(0); } } |
在這裏,我們先調用fork複製了一個子進程,然後再調用execvp新運行了一個程序。
在這個程序離我們還調用了wait函數。該函數有什麼作用了?
在一些情況下,我們的父進程可能比子進程結束的要早。如果父進程提前結束了,子進程就變成了殭屍進程。我們需要父進程來清理子進程結束後的一些環境。在這個時候調用wait,父進程將阻塞在wait地方,等待子進程結束。
wait函數的參數,在這裏我們輸入的是status,將返回子進程結束的返回值,就是return的值。如果不需要,可以設爲NULL。
注意,如果有多個子進程,那麼當只要有一個進程結束的時候,wait函數就會返回。
如果你在父進程中忘記調用了wait,那麼子進程將變成殭屍進程。由於init進程是所有進程的祖宗進程,init的一個職責就是不斷查詢系統,來獲得哪些殭屍進程。它會代替父進程來清理子進程的環境。