linux 多進程編程有如下要點
第一:首當其沖的是進程狀態,我把本地的ubuntu進程截圖了一份,請看:
進程的狀態如下:
R:running,運行狀態。
S:可中斷的睡眠狀態。
D:不可中斷的睡眠狀態。
T:暫停狀態。
Z:僵屍狀態。
<:高優先級別
N:低優先級別
+:前台進程
第二,創建進程
函數定義:pid_t fork();
成功時,返回進程id,失敗時返回-1. 參考例子如下:
#include <stdio.h> #include <unistd.h> int main() { pid_t pid=fork(); if(pid==0) { fputs("Hi i am child proc...",stdout); } else{ fputs("Child proc id is: %d \n ",stdout); sleep(30); } if(pid==0) { puts("End child process"); } else{ puts("End parent process"); } return 0; }
當pid==0時,是子進程代碼運行區域。其他則是父進程運行區域。由於父進程沒有接收到子進程的返回值,這樣子進程會轉換為僵屍進程。
第三 僵屍進程的處理
1)wait 方法:wait方法會一直阻塞當前進程運行,直到有當前進程的子進程結束,wait方法會返回。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { int status; pid_t id=fork(); if(id==0) { return 4; } else{ printf("child proc id is: %d \n",id); wait(&status); if(WIFEXITED(status)) { printf("THe child process return normal:%d \n ",WEXITSTATUS(status)); } sleep(30); } if(id==0) { fputs("The child process ends..\n",stdout); } else{ fputs("The parent process ends..\n",stdout); } return 0; }
2)waitpid 方法:該方法不會阻塞當前進程。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); int status; if(pid==0) { puts("The child process running...."); sleep(9); exit(24); } else{ while(!waitpid(pid,&status,WNOHANG)) { sleep(3); puts("3 seconds..."); } if(WIFEXITED(status)) { printf("Child sent is: %d \n",WEXITSTATUS(status)); } } return 0; }
3)使用信號處理機制,核心在於signal 方法
signal 方法第一個參數為事件名稱。第二個參數為第一個參數事件發生時,需要調用的函數。
事件主要有三個:
SIGCHLD,當前進程的子進程結束時會自動發出這個信號。
SIGINT,當用戶按ctrl c時,系統會發出這個信號。
SIGALRM.系統的alarm函數運行到期后,會發出這個信號。
例子如下:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void timeout(int sig) { printf("time out is: %d \n",sig); if(sig==SIGALRM) { puts("time out...."); } alarm(2); } void keycontrol(int sig) { printf("keycontrol is: %d \n",sig); if(sig==SIGINT) { puts("Ctrl c pressed.."); } } int main() { signal(SIGINT,keycontrol); signal(SIGALRM,timeout); alarm(3); puts("hahahahahah"); int i; for(i=0;i<3;i++) { puts("wait....."); sleep(30); } return 0; }
4)sigaction函數,這個函數是跨平台的。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void timeout(int sig) { if(sig==SIGALRM) { puts("time out...."); } alarm(2); } int main() { int i=0; struct sigaction sig; sig.sa_handler=timeout; sig.sa_flags=0; sigemptyset(&sig.sa_mask); sigaction(SIGALRM,&sig,0); alarm(2); for(i=0;i<100;i++) { sleep(100); puts("wait...."); } return 0; }
這里結構體sigaction的sa_flags=0,sa_mask 都用0即可。sa_handler用來指定處理函數。
第四:進程通信
由於進程是獨立的內存結構,因此進程間不存在共享變量,所以使用管道,管道是內核所有的。見如下例子:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wait.h> void readProc(int sig) { if(sig==SIGCHLD) { int status; waitpid(-1,&status,WNOHANG); if(WIFEXITED(status)) { printf("received from child is: %d \n",WEXITSTATUS(status)); sleep(30); } } } int main() { struct sigaction sig; sig.sa_handler=readProc; sigemptyset(&sig.sa_mask); sig.sa_flags=0; sigaction(SIGCHLD,&sig,0); int fd1[2],fd2[2]; char str1[] = "who are you"; char str2[] = "I am lucy"; char buf[50]; pipe(fd1); pipe(fd2); pid_t id; id = fork(); if(id==0) { write(fd1[1],str1,sizeof(str1)); read(fd2[0],buf,50); printf("child proc recived from parent is: %s \n",buf); return 88; }else{ read(fd1[0],buf,50); write(fd2[1],str2,sizeof(str2)); printf("parent proc recived from child is: %s \n",buf); sleep(40); } return 0; }