Linux 进程
Linux 進(jìn)程
在用戶空間,進(jìn)程是由進(jìn)程標(biāo)識(shí)符(PID)表示的。從用戶的角度來(lái)看,一個(gè) PID 是一個(gè)數(shù)字值,可惟一標(biāo)識(shí)一個(gè)進(jìn)程。一個(gè) PID 在進(jìn)程的整個(gè)生命期間不會(huì)更改,但 PID 可以在進(jìn)程銷毀后被重新使用,所以對(duì)它們進(jìn)行緩存并不見(jiàn)得總是理想的。
進(jìn)程表示
在 Linux 內(nèi)核內(nèi),進(jìn)程是由相當(dāng)大的一個(gè)稱為?task_struct?的結(jié)構(gòu)表示的。此結(jié)構(gòu)包含所有表示此進(jìn)程所必需的數(shù)據(jù),此外,還包含了大量的其他數(shù)據(jù)用來(lái)統(tǒng)計(jì)(accounting)和維護(hù)與其他進(jìn)程的關(guān)系(父和子)。
struct task_struct {volatile long state;void *stack;unsigned int flags;int prio, static_prio;struct list_head tasks;struct mm_struct *mm, *active_mm;pid_t pid;pid_t tgid;struct task_struct *real_parent;char comm[TASK_COMM_LEN];struct thread_struct thread;struct files_struct *files;...}; View Code進(jìn)程管理
在很多情況下,進(jìn)程都是動(dòng)態(tài)創(chuàng)建并由一個(gè)動(dòng)態(tài)分配的?task_struct?表示。一個(gè)例外是init?進(jìn)程本身,它總是存在并由一個(gè)靜態(tài)分配的?task_struct?表示。
在 Linux 內(nèi)雖然進(jìn)程都是動(dòng)態(tài)分配的,但還是需要考慮最大進(jìn)程數(shù)。在內(nèi)核內(nèi)最大進(jìn)程數(shù)是由一個(gè)稱為max_threads?的符號(hào)表示的,它可以在 ./linux/kernel/fork.c 內(nèi)找到。可以通過(guò) /proc/sys/kernel/threads-max 的 proc 文件系統(tǒng)從用戶空間更改此值。
Linux進(jìn)程分為兩種基本類型,分別為內(nèi)核進(jìn)程和用戶進(jìn)程。內(nèi)核進(jìn)程通過(guò)內(nèi)核中kernel_thread()函數(shù)創(chuàng)建的,用戶進(jìn)程通過(guò)fork()和clone()創(chuàng)建。
進(jìn)程的創(chuàng)建&內(nèi)存的復(fù)制
創(chuàng)建一個(gè)子進(jìn)程,就創(chuàng)建了一個(gè)新的子任務(wù),并為它復(fù)制了父進(jìn)程的內(nèi)存。但是這兩個(gè)進(jìn)程使用的內(nèi)存是相互獨(dú)立的。在調(diào)用fork函數(shù)的時(shí)候,父進(jìn)程當(dāng)時(shí)的所有變量對(duì)子進(jìn)程都是可見(jiàn)的,fork函數(shù)執(zhí)行完成之后,父進(jìn)程的變量對(duì)于子進(jìn)程來(lái)說(shuō)就隱藏了。
在創(chuàng)建一個(gè)新進(jìn)程時(shí),父進(jìn)程使用的內(nèi)存并不是真正的全部復(fù)制給子進(jìn)程。它們都指向同一處內(nèi)存空間,但是把內(nèi)存頁(yè)面標(biāo)記為copy-on-write。當(dāng)任何一個(gè)進(jìn)程試圖向這些內(nèi)存中寫(xiě)入內(nèi)容時(shí),就會(huì)產(chǎn)生一組新的內(nèi)存頁(yè)面由這個(gè)進(jìn)程私有。這樣,通過(guò)這種方法提高了創(chuàng)建新進(jìn)程的效率,因?yàn)閮?nèi)存空間的復(fù)制推遲到了發(fā)生寫(xiě)操作的時(shí)候。
進(jìn)程相關(guān)API
Linux下進(jìn)程模型提供了很多API函數(shù),下面的這些函數(shù)可以完成基本的工作。
| API函數(shù) | 用途 |
| fork | 創(chuàng)建子進(jìn)程 |
| wait | 將進(jìn)程掛起,直到子進(jìn)程退出 |
| waitpid ? ? ? | 將進(jìn)程掛起,直到指定子進(jìn)程退出 |
| signal | 注冊(cè)新的信號(hào) |
| pause | 將進(jìn)程掛起,直到捕捉到信號(hào) |
| kill | 向某個(gè)指定的進(jìn)程發(fā)出信號(hào) |
| raise | 向當(dāng)前進(jìn)程發(fā)出信號(hào) |
| exec | 將當(dāng)前進(jìn)程映像用一個(gè)新的進(jìn)程映像來(lái)替換 |
| exit | 正常終止當(dāng)前進(jìn)程 |
函數(shù)詳解&具體應(yīng)用
日常編程中,我們常用到多進(jìn)程的方式,可以讓我們的程序同步執(zhí)行多個(gè)任務(wù)。接下來(lái),從函數(shù)原型依次介紹并用實(shí)例來(lái)分析。
進(jìn)程的創(chuàng)建fork
API函數(shù)fork用于在一個(gè)已經(jīng)存在的父進(jìn)程中創(chuàng)建一個(gè)新的進(jìn)程,子進(jìn)程除了ID與父進(jìn)程不同,其余都相同。
pid_t fork( void ); //pid_t是進(jìn)程描述符 //返回值:>0, 當(dāng)前進(jìn)程就是父進(jìn)程;==0,當(dāng)前進(jìn)程就是子進(jìn)程;<0,失敗//返回值<0時(shí),有兩種錯(cuò)誤,都是內(nèi)存不足問(wèn)題,分別為EAGAIN/ENOMEM 1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 5 int main() 6 { 7 pid_t pid; 8 9 pid = fork(); 10 if(pid > 0) 11 printf("Parent process pid: %d\n",getpid()); 12 else if(pid == 0) 13 printf("Child process pid: %d\n",getpid()); 14 else 15 printf("fork error!\n"); 16 17 return 0; 18 } View Code
進(jìn)程關(guān)閉exit
API函數(shù)exit終止調(diào)用進(jìn)程,傳入exit的參數(shù)會(huì)返回給父進(jìn)程。
子進(jìn)程調(diào)用exit函數(shù)還會(huì)向父進(jìn)程發(fā)出SIGCHLD信號(hào),釋放當(dāng)前進(jìn)程的資源。
//關(guān)閉進(jìn)程 void exit( int status );//status用來(lái)保存狀態(tài)信息
信號(hào)signal
signal_handler函數(shù)允許用戶為進(jìn)程注冊(cè)信號(hào)句柄。在信號(hào)被注冊(cè)后,就可以在需要的時(shí)候調(diào)用信號(hào)API函數(shù)。
PS: 在做一個(gè)WIN32控制臺(tái)下的程序時(shí)候需要?jiǎng)討B(tài)控制,我就利用SIGINT信號(hào),在程序運(yùn)行的過(guò)程中使用CTRL+C,調(diào)用中斷函數(shù)完成操作。
//信號(hào)句柄 void signal_handler( int signal_number );//注冊(cè)信號(hào) sighandler_t signal( int signum, sighandler_t handler ); //signum為信號(hào)類型 //handler為與信號(hào)相關(guān)聯(lián)的動(dòng)作//sighandler_t的類型定義:typedef void (*sighandler_t)(int) *****指向函數(shù)的指針******
進(jìn)程的信號(hào)句柄分為三種類型,分別為忽略類型、默認(rèn)指定類型和用戶自定義句柄類型。
例子:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <signal.h> 5 6 void catch_signal_ctrl_c(int sig_num) 7 { 8 printf("catch signal ctrl+n"); 9 fflush(stdout); 10 } 11 12 int main() 13 { 14 signal(SIGINT, catch_signal_ctrl_c); //注冊(cè)信號(hào)SIGINT軟件中斷,通過(guò)輸入ctrl+c可以發(fā)出 15 16 printf("run a pause"); 17 pause(); //進(jìn)程掛起,等待信號(hào) 18 19 return 0; 20 } View Code進(jìn)程掛起pause
函數(shù)pause會(huì)把進(jìn)程掛起,直到接收到信號(hào)。在信號(hào)接收后,進(jìn)程會(huì)從pause函數(shù)中退出,繼續(xù)運(yùn)行。?
//掛起 int pause( void );向當(dāng)前進(jìn)程發(fā)送信號(hào)raise
raise函數(shù)只可以向當(dāng)前進(jìn)程發(fā)出信號(hào)。
int raise( int sig_num );例子:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <signal.h> 5 6 void catch_signal_ctrl_c(int sig_num) 7 { 8 printf("catch signal ctrl+n"); 9 fflush(stdout); 10 } 11 12 int main() 13 { 14 signal(SIGINT, catch_signal_ctrl_c); //注冊(cè)信號(hào)SIGINT軟件中斷,通過(guò)輸入ctrl+c可以發(fā)出 15 16 raise(SIGINT); 17 18 return 0; 19 } View Code向其他進(jìn)程發(fā)送信號(hào)kill
kill函數(shù)可以向一個(gè)進(jìn)程或一系列進(jìn)程發(fā)送信號(hào)。
int kill( pid_t pid, int sig_num );參數(shù)pid的不同會(huì)有不同的效果:
| pid | 說(shuō)明 |
| >0 ?? | 發(fā)送信號(hào)到pid指定進(jìn)程 |
| 0 | 發(fā)送信號(hào)到與本進(jìn)程同組的所有進(jìn)程 |
| -1 | 發(fā)送信號(hào)到所有進(jìn)程(init進(jìn)程除外) |
| <0 | 發(fā)送信號(hào)到由pid絕對(duì)值指定的進(jìn)程組中的所有進(jìn)程? |
?
進(jìn)程掛起wait&waitpid
wait函數(shù)與waitpid函數(shù)都是將進(jìn)程掛起,直到某個(gè)進(jìn)程退出或信號(hào)發(fā)生,避免僵尸進(jìn)程的產(chǎn)生。
子進(jìn)程退出,父進(jìn)程沒(méi)有等待(調(diào)用wait / waitpid)它, 那么她將變成一個(gè)僵尸進(jìn)程。 但是如果該進(jìn)程的父進(jìn)程已經(jīng)先結(jié)束了,那么該進(jìn)程就不會(huì)變成僵尸進(jìn)程, 因?yàn)槊總€(gè)進(jìn)程結(jié)束的時(shí)候,系統(tǒng)都會(huì)掃描當(dāng)前系統(tǒng)中所運(yùn)行的所有進(jìn)程, 看有沒(méi)有哪個(gè)進(jìn)程是剛剛結(jié)束的這個(gè)進(jìn)程的子進(jìn)程,如果是的話,就由Init 來(lái)接管它,成為它的父進(jìn)程
wait只根據(jù)任何一個(gè)子進(jìn)程退出狀態(tài)。
pid_t wait( int *status ); //返回退出子進(jìn)程的pid值,如果為-1,發(fā)生錯(cuò)誤 //status用來(lái)保存子進(jìn)程退出的狀態(tài)信息通過(guò)評(píng)估函數(shù)可以知道子進(jìn)程退出的狀態(tài)信息。
| 宏 | 說(shuō)明 |
| WIFEXITED | 如果子進(jìn)程正常退出,則不為0 ? |
| WEXITSTATUS ?? | 返回子進(jìn)程的exit狀態(tài) |
| WIFSIGNALED | 如果子進(jìn)程因?yàn)樾盘?hào)退出,則為ture |
| WTERMSIG | 返回引起子進(jìn)程退出的信號(hào)號(hào)(僅在WIFSIGNALED為ture的時(shí)候) |
例子:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <signal.h> 5 6 int main() 7 { 8 pid_t pid; 9 int status; 10 11 pid = fork(); 12 if(pid > 0){ 13 printf("Parent process, pid: %d\n", getpid()); 14 15 printf("父進(jìn)程掛起!\n"); 16 pid = wait(&status); 17 18 printf("父進(jìn)程恢復(fù)\n"); 19 20 }else if(pid == 0){ 21 22 printf("Child process, pid: %d\n", getpid()); 23 printf("子進(jìn)程退出\n"); 24 exit(status); 25 26 }else{ 27 printf("fork error\n"); 28 } 29 30 return 0; 31 } View Codewaitpid是掛起父進(jìn)程直到某個(gè)指定的子進(jìn)程退出。
pid_t waitpid( pid_t pid, int *status, int options); //pid用來(lái)指定進(jìn)程 //status保存退出進(jìn)程狀態(tài) //options有兩種選擇,WNOHANG/WUNTRACED/* WNOHANG:設(shè)定在子進(jìn)程未退出時(shí)也不掛起調(diào)用進(jìn)程,但僅在子進(jìn)程退出時(shí)返回 WUNTRACED:返回已經(jīng)停止而且自停止之后還未報(bào)告狀態(tài)的子進(jìn)程 */pid的取值:
| pid | 說(shuō)明 |
| >0 ? ? ? ? | 掛起直到由pid指定的子進(jìn)程退出? |
| 0 | 掛起直到任何一個(gè)與調(diào)用進(jìn)程的組ID相同的子進(jìn)程退出 ? |
| -1 | 掛起直到任何子進(jìn)程退出,與wait相同 |
| <-1 | 掛起直到任何一個(gè)其組ID與pid參數(shù)的絕對(duì)值相同的子進(jìn)程退出 ? |
waitpid增加兩個(gè)宏:
WIFSTOPPED: 如果子進(jìn)程現(xiàn)在已經(jīng)停止,返回true
WSTOPSIG: 返回使子進(jìn)程停止的信號(hào)(WIFSTOPPED為true)
發(fā)送警告信號(hào)alarm
alarm函數(shù)在其他函數(shù)超時(shí)的時(shí)候會(huì)發(fā)送一個(gè)SIGALRM信號(hào)。
unsigned it alarm( unsigned int secs ); //secs為時(shí)間,單位為秒 //如果在超時(shí),未接收到警告信號(hào),返回0,否則,返回等待警告信號(hào)的時(shí)間? 例子:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <signal.h> 5 #include <string.h> 6 7 void wakeup(int sig_num) 8 { 9 print("timeout quit"); 10 raise(SIGINT);//產(chǎn)生中斷信號(hào) 11 } 12 13 int main() 14 { 15 int ret; 16 char buffer[1024]; 17 18 signal(SIGALRM, wakeup); 19 20 printf("timeout in 3 seconds, please enter your password\n"); 21 alarm(3); 22 23 ret = read(0, buffer, 1023); 24 if(ret >= 0){ 25 buffer[strlen(buffer)-1]='\0'; 26 printf("password : %s", buffer); 27 } 28 29 return 0; 30 } View Code替換當(dāng)前進(jìn)程exec
exec函數(shù)用于完全替換當(dāng)前進(jìn)程映像。實(shí)際上,就是用一個(gè)新的程序來(lái)替換當(dāng)前的進(jìn)程。值得注意的是,如果替換了,就不可能恢復(fù),是完全的替換。
int execl( const char *path, const char *arg, ... ) //path確定要運(yùn)行的程序 //其他都為參數(shù)/* 例子:execl( "/bin/ls", "ls", "-la", NULL );//用ls命令程序來(lái)替換當(dāng)前進(jìn)程 */例子:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <signal.h> 6 #include <string.h> 7 8 9 int main() 10 { 11 int status; 12 char cmd[100]; 13 char* ret; 14 pid_t pid; 15 16 while(1){ 17 printf("mysh>>"); 18 ret = fgets(cmd, sizeof(cmd), stdin); 19 if(ret == NULL) 20 exit(-1); 21 cmd[strlen(cmd)-1]='\0'; 22 23 if(!strncmp(cmd, "quit", 4)) 24 exit(0); 25 26 pid = fork(); 27 if(pid == 0){ 28 execlp(cmd, cmd, NULL); 29 }else if(pid >0){ 30 waitpid(pid, &status, 0); 31 } 32 printf("\n"); 33 } 34 } View Code參考
GNU/LINUX環(huán)境編程
http://www.cnblogs.com/avril/archive/2010/03/22/1691793.html
http://www.ibm.com/developerworks/cn/linux/l-linux-process-management/
本作品由cococo點(diǎn)點(diǎn)創(chuàng)作,采用知識(shí)共享署名-非商業(yè)性使用-相同方式共享 3.0 中國(guó)大陸許可協(xié)議進(jìn)行許可。歡迎轉(zhuǎn)載,請(qǐng)注明出處:
轉(zhuǎn)載自:cococo點(diǎn)點(diǎn) http://www.cnblogs.com/coder2012
轉(zhuǎn)載于:https://www.cnblogs.com/coder2012/p/3176760.html
總結(jié)
- 上一篇: 薄荷健康APP如何绑定华为GT2手表
- 下一篇: Linux下Apache自动监测重启脚本