前言: 本文章只是為了給廣大和我一樣的菜鳥一個指引。如果你是高手,或對編程毫沒有興趣。建議請不要在此浪費時間。 多進程是一個非常有用的東西。記得我上次介紹的那個TCP connect掃描器麼,是不是很慢?如果你使用多進程分段掃描連接埠,你會發現速度大大提升。下面我們就來看看怎麼在Linux下進行多進程編程。 首先,簡單介紹一下我們要用的函數:fork()、wait()。 fork()函數是一個很有意思的函數。他可以建立一個新進程,把當前的進程分為父進程和子進程。原來進程的所有頁面在調用fork()函數時被分為相同的兩份,所以父進程和子進程都使用相同的映像。該函數與普通函數的不同之處是函數如果調用成功會返回兩次,在父進程中返回子進程的PID;在子進程中返回0。成功後,父進程和子進程都在fork()函數後繼續執行。如果函數調用不成功,則返回一次,傳回值為 -1。 由於在進程運行時,如果子進程先退出,它不會從進程列表裡清除。而要發一個SIGCHLD(或SIGCLD)訊號給父進程,父進程確認後子進程才會退出。在等待父進程確認期間,子進程處於“zombie”狀態。所以我們就需要使用wait()函數。如果調用wait()函數時已經有一個處於“zombie”狀態的子進程,那麼函數立即返回的同時該子進程從記憶體中清除出去;否則,主進程會被掛起,直到其中一個進程退出。直接調用wait()函數有個很明顯的缺點就是父進程會被掛起而無法進行其他任務。解決辦法就是攔截處理訊號SIGCHLD(或SIGCLD),這我會在以後講訊號處理的文章中給大家簡單的說說。 老規矩,通過原始碼來學習多進程編程。 /*--------------------------fork.c------------------------------*/ /* mikespook */ /* exercise function fork() and wait()*/ /* 2002.5.28 */ #include <stdio.h> #include <sys/types.h> #include <unistd.h> #define FAC_N 65535 /* 子進程調用的函數,這裡我為了類比一個很大的後台操作用了一個迴圈。 */ void big_loop(int n); /* 父進程調用的函數,其實不放到函數裡也可以,不過為了程式的結構更好看還是放到函數裡的好 */ void input_information(); int main() { /* 進程號 */ pid_t pid; /* 程式在這裡“分叉”,新的進程建立了 */ pid = fork(); /* 通過fork()的傳回值來判斷是父進程還是子進程 */ switch(pid){ /* 返回 -1,很不幸,建立進程失敗了。可能是沒有足夠的記憶體空間,也可能已經開起了太多的進程。 */ case -1: perror("fork/n"); break; /* 返回 0,現在是在子進程裡運行,那就調用子進程的操作函數。 */ case 0: /* 一個運行65535次的迴圈,如果你的機子太快,不能看清楚兩個進程同時啟動並執行效果,那就再加大迴圈次數。或用sleep()函數 */ big_loop(FAC_N); /* 取得子進程的PID,你可以看清楚子進程和父進程的PID是不同的(子進程的PID比父進程的要大,因為是在父進程運行後才建立的)。*/ printf("PID:%d/n", getpid()); break; /* 哈哈,返回的即不是錯誤,又不是子進程,那就是父進程嘍。*/ default: /* 這裡讓使用者輸入了4個數 */ input_information(); /* 取得子進程的PID。*/ printf("PID:%d/n", getpid()); break; } /* 等著吧,子進程不退出,你父進程也不能退出的。 */ wait(); exit(0); } /*big_loop: 簡單,一看就明白,不解釋了。*/ void big_loop(int n) { int i; for(i = 0; i < n; i++){ switch(i % 4){ case 0: putchar('-'); break; case 1: putchar('/'); break; case 2: putchar('|'); break; case 3: putchar('//'); break; } putchar('/b'); } } /*input_information: 簡單,一看就明白,也不解釋了。*/ void input_information() { int n_table[4], i; for(i = 0; i < 4; i++){ printf("Number %d:/t", i); scanf("%d", &n_table[i]); } printf("Number1/tNumber2/tNumber3/tNumber4/n"); printf("%d/t%d/t%d/t%d/n", n_table[0], n_table[1], n_table[2], n_table[3]); } /*--------------------------fork.c------------------------------*/ 同樣,我再給大家補充幾點,以供參考。 多進程的好處是同時並行的運行多個任務。由於各自使用獨立的記憶體空間,所以不容易由於衝突而出錯。但是這樣就給進程間的通訊帶來了一定的麻煩。當然有很多辦法,比如管道,訊息等等可以解決這個問題。多進程還有一個問題就是記憶體空間的浪費。一個進程就是一個完整的記憶體映像,有一些資料重複放置,這樣對記憶體空間浪費是很嚴重的(我想這也就是多線程比多進程要優越的原因,可惜我還沒有完全搞明白linux下的多線程,要不然也和大家討論討論。過段時間吧!)。還有,我要提示的是上面這個例子我在最後用了wait()函數,這樣父進程運行完後回等著子進程退出才退出。你可以試試把wait();這個語句去掉,看看什麼效果?父進程運行完退出了,我們回到了[mikespook @ lazycat]$的提示符下,而子進程繼續在運行。有時我們可以利用這個把一個進程放到後台去運行(比如木馬……當然啦,我不是建議你做木馬!)。 好啦,Linux下的多進程編程就怎麼點內容,是不是很簡單呢?其實,讓兩個進程獨立運行很容易,關鍵的痛點是父進程和子進程共用資料,進行通訊。我會在以後的文章中慢慢和大家討論的(其實關鍵是有一些東西還沒有悟透,不敢拿出來丟人^%^)。 給大家個好站:http://www.opengroup.org/onlinepubs/007908799/ 線上的man手冊,有什麼不明白的地方可以到這裡查詢。可惜,是E文的。 由於我是菜鳥,或許有什麼不對的地方。也可能一些細節我沒有考慮到。如果你知道的話希望不惜指教。小弟感激不盡!! |