---恢復內容開始---shell
fork函數服務器
該函數是unix中派生新進程的惟一方法。網絡
#include <unistd.h>併發
pid_t fork(void);異步
返回: (調用它一次, 它返回 兩次 , 它在調用進程(稱爲父進程)中返回一次, 返回值是新派生進程(稱爲子進程)的進程ID號函數
在子進程又返回一次,返回值爲0。 所以,返回值自己告知當前進程是子進程仍是父進程)spa
在子進程中爲0, 在父進程中爲子進程ID,操作系統
若出錯則爲-1;unix
fork有兩個典型的用法:code
1.一個進程建立一個自身的副本,這樣每一個副本都 能夠在另外一個副本執行其餘任務的同時處理各自的某個操做。 這是網絡服務器的典型用法;
2. 一個進程想要執行另外一個程序。既然建立新進程的惟一辦法是調用fork, 該進程因而首先調用fork建立一個自身的副本,而後另外一個副本(一般爲子進程)調用exec把自身替換成新的程序。 這是shell之類程序的典型用法;
exec把當前進程映像替換成新的程序文件,並且該新程序一般從main函數開始執行,進程ID並不改變。咱們稱調用exec的進程爲調用進程(calling process),稱新執行的程序爲新程序(new program)
#include <unistd.h>
int execl(const char *pathname, const char *arg0, .../);
int execv(const char *pathname, char *const *argv[]);
int execle(const char *pathname, const char *arg(), ....);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, con)
併發服務器在listen到鏈接以後,accept()函數解除阻塞,而後服務器執行fork()函數,在父進程中有listenfd, 和 由 accpet返回的已鏈接套接字即connfd, 而子進程也存在listenfd, 和由accept返回的已經鏈接套接字即connfd。
下一步: 父進程關閉已鏈接的套接字connfd,保留listenfd繼續監聽。 子進程關閉listenfd套接字,保留connfd進行相應操做(read, write等操做)
getsockname 和 getpeername函數
===================================================================================================================
信號(signal) 就是告知某個進程發生了某個事件的通知, 有時也稱爲 「 software interrupt 「 . 信號一般是異步發生的, 也就是說進程預先不知道信號的準確發生的時間;
信號能夠:
1. 由一個信號發給另有一個進程(或自身)
2. 由內核發給某個進程;
每個信號都有一個與之關聯的處置(disposition), 也稱爲行爲(action)。 咱們經過調用sigaction函數來設定一個信號的處置,並有三種選擇。
(1) 咱們提供一個函數, 只要有特定信號發生它就被調用。 這樣的函數稱爲「信號處理函數( signal handler)」, 這種行爲稱爲 捕獲信號(catch signal).
有兩個信號不能被捕獲,它們是SIGKILL 和 SIGSTOP。 信號處理函數由信號值這個單一的整數參數來調用, 且沒有返回值,
其函數原型以下:
void handler(int signo);
對於大多信號來講,調用signcation函數 並指定信號發生所調用的函數 就是捕獲信號所需作的所有工做;此外,SIGIO, SIGPOLL, SIGURG這些個別信號還要求捕獲它們的進程 作其它額外 的工做;
(2)咱們能夠把某個信號的處置設定爲SIG_IGN來忽略(ignore)它。 SIGKILL和SIGSTOP這兩個信號不能被忽略;
(3)咱們能夠把某個信號的處置設定爲 SIG_DFL來啓用它的默認(default)處理。默認處置一般是在收到信號後終止進程,某些信號的默認default處理不一樣;
---恢復內容結束---
fork函數
該函數是unix中派生新進程的惟一方法。
#include <unistd.h>
pid_t fork(void);
返回: (調用它一次, 它返回 兩次 , 它在調用進程(稱爲父進程)中返回一次, 返回值是新派生進程(稱爲子進程)的進程ID號
在子進程又返回一次,返回值爲0。 所以,返回值自己告知當前進程是子進程仍是父進程)
在子進程中爲0, 在父進程中爲子進程ID,
若出錯則爲-1;
fork有兩個典型的用法:
1.一個進程建立一個自身的副本,這樣每一個副本都 能夠在另外一個副本執行其餘任務的同時處理各自的某個操做。 這是網絡服務器的典型用法;
2. 一個進程想要執行另外一個程序。既然建立新進程的惟一辦法是調用fork, 該進程因而首先調用fork建立一個自身的副本,而後另外一個副本(一般爲子進程)調用exec把自身替換成新的程序。 這是shell之類程序的典型用法;
exec把當前進程映像替換成新的程序文件,並且該新程序一般從main函數開始執行,進程ID並不改變。咱們稱調用exec的進程爲調用進程(calling process),稱新執行的程序爲新程序(new program)
#include <unistd.h>
int execl(const char *pathname, const char *arg0, .../);
int execv(const char *pathname, char *const *argv[]);
int execle(const char *pathname, const char *arg(), ....);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, con)
併發服務器在listen到鏈接以後,accept()函數解除阻塞,而後服務器執行fork()函數,在父進程中有listenfd, 和 由 accpet返回的已鏈接套接字即connfd, 而子進程也存在listenfd, 和由accept返回的已經鏈接套接字即connfd。
下一步: 父進程關閉已鏈接的套接字connfd,保留listenfd繼續監聽。 子進程關閉listenfd套接字,保留connfd進行相應操做(read, write等操做)
getsockname 和 getpeername函數
===================================================================================================================
信號(signal) 就是告知某個進程發生了某個事件的通知, 有時也稱爲 「 software interrupt 「 . 信號一般是異步發生的, 也就是說進程預先不知道信號的準確發生的時間;
信號能夠:
1. 由一個信號發給另有一個進程(或自身)
2. 由內核發給某個進程;
每個信號都有一個與之關聯的處置(disposition), 也稱爲行爲(action)。 咱們經過調用sigaction函數來設定一個信號的處置,並有三種選擇。
(1) 咱們提供一個函數, 只要有特定信號發生它就被調用。 這樣的函數稱爲「信號處理函數( signal handler)」, 這種行爲稱爲 捕獲信號(catch signal).
有兩個信號不能被捕獲,它們是SIGKILL 和 SIGSTOP。 信號處理函數由信號值這個單一的整數參數來調用, 且沒有返回值,
其函數原型以下:
void handler(int signo);
對於大多信號來講,調用signcation函數 並指定信號發生所調用的函數 就是捕獲信號所需作的所有工做;此外,SIGIO, SIGPOLL, SIGURG這些個別信號還要求捕獲它們的進程 作其它額外 的工做;
(2)咱們能夠把某個信號的處置設定爲SIG_IGN來忽略(ignore)它。 SIGKILL和SIGSTOP這兩個信號不能被忽略;
(3)咱們能夠把某個信號的處置設定爲 SIG_DFL來啓用它的默認(default)處理。默認處置一般是在收到信號後終止進程,某些信號的默認default處理不一樣;
=======================================================================================================2.1 Linux下進程的結構
Linux下一個進程在內存裏有三部分的數據,就是"代碼段"、"堆棧段"和"數據段"。其實學過彙編語言的人必定知道,通常的CPU都有上述三種段寄存器,以方便操做系統的運行。這三個部分也是構成一個完整的執行序列的必要的部分。
"代碼段",顧名思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程 序,那麼它們就可使用相同的代碼段。"堆棧段"存放的就是子程序的返回地址、子程序的參數以及程序的局部變量。而數據段則存放程序的全局變量,常數以及 動態數據分配的數據空間(好比用malloc之類的函數取得的空間)。這其中有許多細節問題,這裏限於篇幅就很少介紹了。系統若是同時運行數個相同的程 序,它們之間就不能使用同一個堆棧段和數據段。
若是一個大程序在運行中,它的數據段和堆棧都很大,一次fork就要複製一次,那麼fork的系統開銷不是很大嗎?其實UNIX自有其解決的辦法,你們知 道,通常CPU都是以"頁"爲單位來分配內存空間的,每個頁都是實際物理內存的一個映像,象INTEL的CPU,其一頁在一般狀況下是 4086字節大小,而不管是數據段仍是堆棧段都是由許多"頁"構成的,fork函數複製這兩個段,只是"邏輯"上的,並不是"物理"上的,也就是說,實際執 行fork時,物理空間上兩個進程的數據段和堆棧段都仍是共享着的,當有一個進程寫了某個數據時,這時兩個進程之間的數據纔有了區別,系統就將有區別的" 頁"從物理上也分開。系統在空間上的開銷就能夠達到最小。
exec( )函數族
下面咱們來看看一個進程如何來啓動另外一個程序的執行。在Linux中要使用exec函數族。系統 調用execve()對當前進程進行替換,替換者爲一個指定的程序,其參數包括文件名(filename)、參數列表(argv)以及環境變量 (envp)。exec函數族固然不止一個,但它們大體相同,在 Linux中,它們分別是:execl,execlp,execle,execv,execve和execvp,
以execlp爲例,其它函數究 竟與execlp有何區別,請經過manexec命令來了解它們的具體狀況。
一個進程一旦調用exec類函數,它自己就"死亡"了,系統把代碼段替換成新的程序的代碼, 廢棄原有的數據段和堆棧段,併爲新程序分配新的數據段與堆棧段,惟一留下的,就是進程號,也就是說,對系統而言,仍是同一個進程,不過已是另外一個程序 了。(不過exec類函數中有的還容許繼承環境變量之類的信息。)
那麼若是個人程序想啓動另外一程序的執行但本身仍想繼續運行的話,怎麼辦呢?那就是結合fork與exec的使用。下面一段代碼顯示如何啓動運行其它程序:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <errno.h> 4 #include <string.h> 5 6 char command[256]; 7 int main() 8 { 9 int rtn; 10 printf(">>"); 11 12 fgets(command, 256, stdin); 13 command[strlen(command)-1] = 0; 14 if(fork()==0) //子進程 15 { 16 execlp(command, NULL); 17 perror(command); 18 exit(errno); 19 } 20 else //父進程 21 { 22 wait(&rtn); 23 printf("child process return %d\n", rtn); 24 } 25 26 return 0; 27 }