fork()理解及簡單併發服務器應用

首先,我覺得有兩個容易混淆的地方:

1,fork()函數並不是創建新進程,而是複製 當前進程。

2,fork()函數被共享執行,有2種返回值。

========================分割線===================================

對於第一點:

父進程fork()後,子進程完全與父進程相等,包括數據段與代碼段。可以理解成,數據段有2份相同 的,而代碼段只有一份供他們共享。關於如何區分子進程與父進程在第二點講述。

特別強調“複製”這個概念還有一個原因是因爲fork()並不是用於創建新進程的,一般Linux下有兩種創建新進程的方法:system()與exec()。

system()的底層是exec(),它會重新啓動一個shell來執行新進程;exec()是替換 當前進程爲新進程。

如果用exec()創建新進程,那麼必須配合fork()先複製一份進程,再調用exec()將複製品替換掉。、

補充:Linux下的進程是很廉價的,多進程常常比多線程更加方便,關於他們的異同,最主要的就是在多線程中,數據段是共享的,而多進程中,正如上面所述,獨立性很高。

對於第二點:

由於子進程與父進程代碼段是共享的,所以這點與多線程相同,創建子進程後,馬上執行相同的函數,自然子進程也會執行fork()函數,但是並不創建新進程且返回值與父進程不同,這就是區分子進程與父進程的辦法。

========================分割線===================================

一個例子:簡單多進程併發服務器框架:

pid_t pid;
int listenfd, connfd;

listenfd = Socket( ... );

/* fill in sockaddr_in{} with server's well-known port */
Bind(listenfd, ... );
Listen(listenfd, LISTENQ);

for ( ; ; ) {
connfd = Accept (listenfd, ... ); /* probably blocks */

if ( (pid = fork()) == 0) {
#2 Close(listenfd); /* child closes listening socket */
doit(connfd); /* process the request */
Close(connfd); /* done with this client */
exit(0); /* child terminates */
}

#1 Close(connfd); /* parent closes connected socket */
}

以上只是一個框架,很多代碼都簡化了,可以看出fork()的常規使用方法:if ( (pid = fork()) == 0) 的代碼塊爲子進程才能執行的區域。另外特別需要注意的是,listenfd與connfd被close兩次,這是必須的,因爲系統對套接字的使用是計數的,當計數爲0時才真正關閉。又因爲父進程只關心listenfd,所以在父進程區域內,必須關閉connfd(#1處);子進程只關心connfd,所以在子進程區域內,必須關閉listenfd(#2處)。

另外補充一點,socket的close()採用計數,當計數爲0才發送FIN,但是shutdown()函數可以不管計數,直接發送FIN。顯然這裏不能用shutdown()。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章