<TCP/IP網(wǎng)絡(luò)編程> Chap10. 多進(jìn)程服務(wù)器端

進(jìn)程概念及應(yīng)用

并發(fā)服務(wù)器端實(shí)現(xiàn)模型和方法:

  • 多進(jìn)程服務(wù)器(Chap10&11)
  • 多路復(fù)用服務(wù)器(Chap12)
  • 多線程服務(wù)器(Chap18)

進(jìn)程:占用內(nèi)存空間的正在運(yùn)行的程序砚殿。

1個(gè)CPU中可能包含多個(gè)運(yùn)算設(shè)備(核)囤锉。核的個(gè)數(shù)與可同時(shí)運(yùn)行的進(jìn)程數(shù)相同。
可以通過(guò)調(diào)用fork函數(shù)創(chuàng)建進(jìn)程悴势。

#include <unistd.h>
pid_t fork(void);    // 成功時(shí)返回進(jìn)程ID钱贯,失敗時(shí)返回-1

fork函數(shù)復(fù)制正在運(yùn)行的父進(jìn)程挫掏,父子進(jìn)程都將執(zhí)行fork函數(shù)之后的語(yǔ)句。但父進(jìn)程的fork返回值是子進(jìn)程ID秩命,子進(jìn)程的fork返回值是0尉共,應(yīng)利用此特點(diǎn)區(qū)分后續(xù)代碼的執(zhí)行流程。

# gcc fork.c -o fork
# ./fork 
Parent Proc: [9, 23] 
Child Proc: [13, 27]


進(jìn)程和僵尸進(jìn)程

下面的示例中弃锐,子進(jìn)程先return(或exit)袄友,但父進(jìn)程不知道,子進(jìn)程就變成了僵尸進(jìn)程(Z+)霹菊。直到父進(jìn)程終止剧蚣,子進(jìn)程才會(huì)與父進(jìn)程一起被銷(xiāo)毀。

# ./zombie &
[1] 19154
Child Process ID: 19156 
Hi, I am a child process
# ps au
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
root     19154  0.0  0.0   4356   732 pts/3    S    06:48   0:00 ./zombie
root     19156  0.0  0.0      0     0 pts/3    Z    06:48   0:00 [zombie] <defunct>
# ps au
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
[1]+  Done                    ./zombie

為了銷(xiāo)毀子進(jìn)程旋廷,父進(jìn)程應(yīng)主動(dòng)請(qǐng)求獲取子進(jìn)程的返回值鸠按。方法之一就是調(diào)用如下函數(shù):

#include <sys/wait.h>
pid_t wait(int *statloc);    // 成功時(shí)返回終止的子進(jìn)程ID,失敗時(shí)返回-1

調(diào)用此函數(shù)時(shí)饶碘,如果子進(jìn)程已終止目尖,那么子進(jìn)程return的返回值(或exit函數(shù)的參數(shù)值)將保存到statloc指向的內(nèi)存空間。statloc還包含其他信息扎运,可以用相應(yīng)的宏來(lái)判斷瑟曲。

# gcc wait.c -o wait
# ./wait &
[1] 5586
Child PID: 5588 
Child PID: 5595 
Child send 1: 3 
Child send 1: 7 
root@nsx:~/tcpip/Chap10 [master] # ps au
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...

注意饮戳,wait并沒(méi)有傳入子進(jìn)程ID,它是按照子進(jìn)程結(jié)束(而非創(chuàng)建)的順序返回結(jié)果的测蹲。如果沒(méi)有已終止的子進(jìn)程莹捡,父進(jìn)程會(huì)在這里阻塞。
第二個(gè)主動(dòng)請(qǐng)求獲取子進(jìn)程返回值的方法:

#include <sys/wait.h>
/* 
 * @params
 *   pid: 子進(jìn)程ID扣甲,若傳遞-1篮赢,則與wait函數(shù)相同
 *   statloc: 同wait函數(shù)中的statloc
 *   options: 傳遞頭文件sys/wait.h中聲明的常量WNOHANG,即使沒(méi)有終止的子進(jìn)程也不會(huì)阻塞琉挖,而是返回0并退出函數(shù)
 */
pid_t waitpid(pid_t pid, int *statloc, int options);    // 成功時(shí)返回終止的子進(jìn)程ID启泣,失敗時(shí)返回-1

waitpid不會(huì)阻塞父進(jìn)程,因此可以循環(huán)檢查示辈,并在期間處理別的工作寥茫。

# gcc waitpid.c -o waitpid
# ./waitpid 
Do sth else.
Do sth else.
Do sth else.
Do sth else.
Do sth else.
Do sth else.
Child send: 24


信號(hào)處理

信號(hào)是在特定事件發(fā)生時(shí)由操作系統(tǒng)向進(jìn)程發(fā)送的消息。

#include <signal.h>
/* 
 * @params
 *   signo: 特殊情況信息(如SIGALRM代表已到通過(guò)alarm函數(shù)注冊(cè)的時(shí)間矾麻,SIGINT代表輸入CTRL+C纱耻,SIGCHILD代表子進(jìn)程終止)
 *   func: 發(fā)生該特殊情況時(shí)要調(diào)用的函數(shù)的指針
 */
void (*signal(int signo, void (*func)(int)))(int);

這個(gè)函數(shù)的返回值為函數(shù)指針,指向signal函數(shù)調(diào)用之前的信號(hào)處理函數(shù)(第二次調(diào)用signal函數(shù)的時(shí)候险耀,它的返回值就是第一次調(diào)用signal時(shí)傳入的信號(hào)處理函數(shù))弄喘。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);    // 返回0或以秒為單位的距SIGALRM信號(hào)發(fā)生所剩的時(shí)間

若alarm函數(shù)的參數(shù)為0代表取消之前對(duì)SIGALRM信號(hào)的預(yù)約。

# gcc signal.c -o signal
# ./signal
Wait...
^CCTRL+C pressed
Wait...
Time out!
Wait...
^CCTRL+C pressed

我們觀察到盡管主進(jìn)程的循環(huán)內(nèi)有100秒的sleep函數(shù)甩牺,但仍很快就結(jié)束了蘑志。因?yàn)槊恳粋€(gè)到達(dá)的信號(hào)都喚醒了主進(jìn)程,使其離開(kāi)阻塞狀態(tài)以處理信號(hào)贬派。
不過(guò)signal函數(shù)在UNIX系列的不同操作系統(tǒng)中可能存在區(qū)別急但,可以用sigaction函數(shù)來(lái)完全替代它。

#include <signal.h>
struct sigaction {
    void (*sa_handler)(int);
    sigset_t sa_mask;
    int sa_flags;
}
/* 
 * @params
 *   signo: 特殊情況信息搞乏,與signal函數(shù)相同
 *   act: 信號(hào)處理函數(shù)信息
 *   oldact: 獲取之前的信號(hào)處理函數(shù)指針波桩,若不需要?jiǎng)t傳遞0
 */
int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);    // 成功時(shí)返回0, 失敗時(shí)返回-1

它的作用可以等同于signal函數(shù):

# gcc sigaction.c -o sigaction
# ./sigaction 
Wait...
Time out!
Wait...
Time out!
Wait...
Time out!

利用sigaction函數(shù)消滅僵尸進(jìn)程:

# gcc remove_zombie.c -o rmzombie
# ./rmzombie 
Child proc id: 9351 
Child proc id: 9352 
Wait...
I am child process
I am child process
Wait...
Enter signal handler...Remove proc id: 9352 
Child send: 24 
Enter signal handler...Remove proc id: 9351 
Child send: 12 
Wait...
Wait...
Wait...

我修改了原書(shū)中的例子查描,在waitpid函數(shù)處加了循環(huán)突委,因?yàn)閮蓚€(gè)子進(jìn)程終止的時(shí)間太過(guò)于接近導(dǎo)致waitpid函數(shù)只處理了一個(gè)信號(hào)(兩個(gè)信號(hào)都在信號(hào)處理函數(shù)執(zhí)行之前產(chǎn)生)。

基于多任務(wù)的并發(fā)服務(wù)器

每當(dāng)有客戶端請(qǐng)求鏈接時(shí)冬三,服務(wù)器端都創(chuàng)建子進(jìn)程以提供服務(wù)≡蹈浚可以結(jié)合第四章的回聲客戶端來(lái)運(yùn)行勾笆。

# gcc echo_mpserv.c -o mpserv
# ./mpserv 9190
Connected client 1 
Begin exchanging data with client 1 
Connected client 2 
Begin exchanging data with client 2 
Client disconnected...
Remove proc id: 30324 
Child send: 0 
Client disconnected...
Remove proc id: 30496 
Child send: 0

第一次寫(xiě)完這段程序時(shí),我杯具地發(fā)現(xiàn)退出其中一個(gè)客戶端時(shí)桥滨,服務(wù)器開(kāi)始刷屏Remove proc id: 0窝爪,因?yàn)槲以谇拔陌褀aitpid函數(shù)放在了while循環(huán)里弛车,而它卻一直收到0返回值因此不能退出循環(huán)。實(shí)際上0是期待的返回值蒲每,因?yàn)檫@個(gè)時(shí)候還有其他客戶端正在連接(意味著還有其他子進(jìn)程未結(jié)束)纷跛,waitpid就會(huì)返回0。-1是出現(xiàn)錯(cuò)誤時(shí)的返回值(如已經(jīng)沒(méi)有子進(jìn)程了)邀杏。前一個(gè)例子之所以沒(méi)有發(fā)現(xiàn)這個(gè)錯(cuò)誤是因?yàn)閮蓚€(gè)子進(jìn)程都在信號(hào)處理函數(shù)前就終止了贫奠,沒(méi)有給waitpid函數(shù)返回0的機(jī)會(huì)。
修改循環(huán)的判定條件為大于0望蜡,運(yùn)行成功唤崭,結(jié)果如上所示。

分割TCP的I/O程序

客戶端的父進(jìn)程負(fù)責(zé)接收數(shù)據(jù)脖律,創(chuàng)建子進(jìn)程發(fā)送數(shù)據(jù)谢肾。

# gcc echo_mpclient.c -o mpclnt
# ./mpclnt 127.0.0.1 9190
Connected
dear
Message from server: dear
Q


習(xí)題

  1. 下列關(guān)于進(jìn)程的說(shuō)法錯(cuò)誤的是?
    a. 從操作系統(tǒng)的角度上說(shuō)小泉,進(jìn)程是程序運(yùn)行的單位芦疏。
    b. 進(jìn)程根據(jù)創(chuàng)建方式建立父子關(guān)系。
    c. 進(jìn)程可以包含其他進(jìn)程微姊,即一個(gè)進(jìn)程的內(nèi)存空間可以包含其他進(jìn)程酸茴。
    d. 子進(jìn)程可以創(chuàng)建其他子進(jìn)程,而創(chuàng)建出來(lái)的子進(jìn)程還可以創(chuàng)建其子進(jìn)程柒桑,但所有這些進(jìn)程只與一個(gè)父進(jìn)程建立父子關(guān)系弊决。
    cd。
  2. 調(diào)用fork函數(shù)將創(chuàng)建子進(jìn)程魁淳,以下關(guān)于子進(jìn)程的描述錯(cuò)誤的是飘诗?
    a. 父進(jìn)程銷(xiāo)毀時(shí)也會(huì)同時(shí)銷(xiāo)毀子進(jìn)程。
    b. 子進(jìn)程是復(fù)制父進(jìn)程所有資源創(chuàng)建出的進(jìn)程界逛。
    c. 父子進(jìn)程共享全局變量昆稿。
    d. 通過(guò)fork函數(shù)創(chuàng)建的子進(jìn)程將執(zhí)行從開(kāi)始到fork函數(shù)調(diào)用為止的代碼。
    cd息拜。不考慮主動(dòng)把子進(jìn)程變成守護(hù)進(jìn)程的情況溉潭,父進(jìn)程銷(xiāo)毀時(shí)也會(huì)同時(shí)銷(xiāo)毀子進(jìn)程。子進(jìn)程完全復(fù)制了父進(jìn)程的資源少欺,包括進(jìn)程上下文喳瓣、代碼區(qū)、數(shù)據(jù)區(qū)赞别、堆區(qū)畏陕、棧區(qū)、內(nèi)存信息仿滔、打開(kāi)文件的文件描述符惠毁、信號(hào)處理函數(shù)犹芹、進(jìn)程優(yōu)先級(jí)、進(jìn)程組號(hào)鞠绰、當(dāng)前工作目錄腰埂、根目錄、資源限制和控制終端等信息蜈膨,而子進(jìn)程與父進(jìn)程的區(qū)別有進(jìn)程號(hào)屿笼、資源使用情況和計(jì)時(shí)器等。全局變量位于數(shù)據(jù)區(qū)丈挟,也會(huì)被一起復(fù)制到子進(jìn)程的內(nèi)存空間刁卜。通過(guò)fork函數(shù)創(chuàng)建的子進(jìn)程將執(zhí)行從開(kāi)始調(diào)用fork函數(shù)到最后的代碼。
  3. 創(chuàng)建子進(jìn)程時(shí)將復(fù)制父進(jìn)程的所有內(nèi)容曙咽,此時(shí)的復(fù)制對(duì)象也包含套接字文件描述符蛔趴。編寫(xiě)程序驗(yàn)證復(fù)制的文件描述符整數(shù)值是否與原文件描述符整數(shù)值相同。
    與echo_client搭配運(yùn)行例朱。
# gcc sockid.c -o sockid
# ./sockid 9190
Connected client 1 
server sock: 3, client sock: 4
server sock: 3, client sock: 4
  1. 請(qǐng)說(shuō)明進(jìn)程變?yōu)榻┦M(jìn)程的過(guò)程及預(yù)防措施孝情。
    如果在父進(jìn)程中沒(méi)有注冊(cè)信號(hào)處理函數(shù)的話,子進(jìn)程的退出信號(hào)會(huì)傳遞給操作系統(tǒng)洒嗤,但操作系統(tǒng)并不會(huì)銷(xiāo)毀子進(jìn)程箫荡,需要傳遞給父進(jìn)程去銷(xiāo)毀。父進(jìn)程可以主動(dòng)發(fā)起請(qǐng)求渔隶,獲得子進(jìn)程的結(jié)束狀態(tài)值羔挡,然后調(diào)用wait或waitpid函數(shù)來(lái)正常終止子進(jìn)程。
  2. 如果在未注冊(cè)SIGINT信號(hào)的情況下輸入Ctrl+C间唉,將由操作系統(tǒng)默認(rèn)的時(shí)間處理器終止程序绞灼。但如果直接注冊(cè)Ctrl+C信號(hào)的處理器,則程序不會(huì)終止呈野,而是調(diào)用程序員指定的事件處理器低矮。編寫(xiě)注冊(cè)處理函數(shù)的程序,完成如下功能:“輸入Ctrl+C時(shí)被冒,詢問(wèn)是否確定退出程序军掂,輸入Y則終止程序”。另外昨悼,編寫(xiě)程序使其每隔1秒輸出簡(jiǎn)單字符串蝗锥,并適用于上述時(shí)間處理器注冊(cè)代碼。
# gcc sigint.c -o sigint
# ./sigint 
Waiting
Waiting
^CPressed CTRL+C. Quit? Y/N
N
Waiting
Waiting
Waiting
^CPressed CTRL+C. Quit? Y/N
Y


我的問(wèn)題

進(jìn)程號(hào)為1的進(jìn)程是什么率触?
init進(jìn)程玛追。

附錄

  1. Github
  2. signal函數(shù)的返回值究竟是什么?
  3. 處理僵死進(jìn)程
  4. linux的 0號(hào)進(jìn)程 和 1 號(hào)進(jìn)程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闲延,一起剝皮案震驚了整個(gè)濱河市痊剖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垒玲,老刑警劉巖陆馁,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異合愈,居然都是意外死亡叮贩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)佛析,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)益老,“玉大人,你說(shuō)我怎么就攤上這事寸莫∞嗝龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵膘茎,是天一觀的道長(zhǎng)桃纯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)披坏,這世上最難降的妖魔是什么态坦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮棒拂,結(jié)果婚禮上伞梯,老公的妹妹穿的比我還像新娘。我一直安慰自己帚屉,他們只是感情好谜诫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著涮阔,像睡著了一般猜绣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敬特,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天掰邢,我揣著相機(jī)與錄音,去河邊找鬼伟阔。 笑死辣之,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的皱炉。 我是一名探鬼主播怀估,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了多搀?” 一聲冷哼從身側(cè)響起歧蕉,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎康铭,沒(méi)想到半個(gè)月后惯退,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡从藤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年催跪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夷野。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懊蒸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悯搔,到底是詐尸還是另有隱情骑丸,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布鳖孤,位于F島的核電站者娱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苏揣。R本人自食惡果不足惜黄鳍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望平匈。 院中可真熱鬧框沟,春花似錦、人聲如沸增炭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隙姿。三九已至梅垄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間输玷,已是汗流浹背队丝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留欲鹏,地道東北人机久。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赔嚎,于是被迫代替她去往敵國(guó)和親膘盖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胧弛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354