標籤:
1. 進程間開啟檔案的繼承 1.1. 用fork繼承開啟的檔案
fork以後的子進程自動繼承了父進程的開啟的檔案,繼承以後,父進程關閉開啟的檔案不會對子進程造成影響。
樣本:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
char szBuf[32] = {‘\0‘};
int iFile = open("./a.txt", O_RDONLY);
if(fork() > 0){//parent process
close(iFile);
return 0;
}
//child process
sleep(3); //wait for parent process closing fd
if(read(iFile, szBuf, sizeof(szBuf)-1) < 1){
perror("read fail");
}else{
printf("string:%s\n",szBuf);
}
close(iFile);
return 0;
}
1.2. 守護進程
Daemon運行在後台也稱作“後台服務進程”。 它是沒有控制終端與之相連的進程。它獨立與控制終端、通常周期的執行某種任務。那麼為什麼守護進程要脫離終端後台運行呢?守護進程脫離終端是為了避免進程在執行過程中的資訊在任何終端上顯示並且進程也不會被任何終端所產生的任何終端資訊所打斷。那麼為什麼要引入守護進程呢?由於在linux中,每一個系統與使用者進行交流的介面稱為終端,每一個從此終端開始啟動並執行進程都會依賴這個終端,這個終端就稱為這些進程的控制終端。當控制終端被關閉時,相應的進程都會自動關閉。但是守護進程卻能突破這種限制,它被執行開始運轉,直到整個系統關閉時才退出。幾乎所有的伺服器程式如Apache和wu-FTP,都用daemon進程的形式實現。很多Linux下常見的命令如inetd和ftpd,末尾的字母d通常就是指daemon。
守護進程的特性:
1> 守護進程最重要的特性是後台運行。
2> 其次,守護進程必須與其運行前的環境隔離開來。這些環境包括未關閉的檔案描述符、控制終端、會話和進程組、工作目錄已經檔案建立掩碼等。這些環境通常是守護進程從父進程那裡繼承下來的。
3> 守護進程的啟動方式
daemon進程的編程規則
l 建立子進程,父進程退出:
調用fork產生一個子進程,同時父進程退出。我們所有後續工作都在子進程中完成。這樣做我們可以交出控制台的控制權,並為子進程作為進程組長作準備;由於父進程已經先於子進程退出,會造成子進程沒有父進程,變成一個孤兒進程(orphan)。每當系統發現一個孤兒進程,就會自動由1號進程收養它,這樣,原先的子進程就會變成1號進程的子進程。代碼如下:
pid = fork();
if(pid>0)
exit(0);
l 在子進程中建立新會話:
使用系統函數setsid()。由於建立守護進程的第一步調用了fork函數來建立子進程,再將父進程退出。由於在調用fork函數的時候,子進程全盤拷貝了父進程的會話期、進程組、控制終端等,雖然父進程退出了,但會話期、進程組、控制終端並沒有改變,因此,還不是真正意義上的獨立開來。而調用setsid函數會建立一個新的會話並自任該會話的組長,調用setsid函數有下面3個作用:讓進程擺脫原會話的控制,讓進程擺脫原進程組的控制,讓進程擺脫原控制終端的控制;
進程組:是一個或多個進程的集合。進程組有進程組ID來唯一標識。除了進程號(PID)之外,進程組ID(GID)也是一個進程的必備屬性。每個進程都有一個組長進程,其組長進程的進程號等於進程組ID。且該進程組ID不會因為組長進程的退出而受影響。
會話周期:會話期是一個或多個進程組的集合。通常,一個會話開始於使用者登入,終止於使用者退出,在此期間該使用者啟動並執行所有進程都屬於這個會話期。
控制終端:由於在linux中,每一個系統與使用者進行交流的介面稱為終端,每一個從此終端開始啟動並執行進程都會依賴這個控制終端。
l 改變目前的目錄為根目錄:
使用fork函數建立的子進程繼承了父進程的當前工作目錄。由於在進程運行中,目前的目錄所在的檔案是不能卸載的,這對以後的使用會造成很多的不便。利用chdir("/");把當前工作目錄切換到根目錄。
l 重設檔案許可權掩碼:
umask(0);將檔案許可權掩碼設為0,Deamon建立檔案不會有太大麻煩;
l 關閉所有不需要的檔案描述符:
新進程會從父進程那裡繼承一些已經開啟了的檔案。這些被開啟的檔案可能永遠不會被守護進程讀寫,而它們一直消耗系統資源。另外守護進程已經與所屬的終端失去聯絡,那麼從終端輸入的字元不可能到達守護進程,守護進程中常規方法(如printf)輸出的字元也不可能在終端上顯示。所以通常關閉從0到MAXFILE的所有檔案描述符。
for(i=0;i<MAXFILE;i++)
close(i);
(註:有時還要處理SIGCHLD訊號signal(SIGCHLD, SIG_IGN);防止殭屍進程(zombie))
下面就可以添加任何你要daemon做的事情
樣本:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
void Daemon()
{
const int MAXFD=64;
int i=0;
if(fork()!=0) //父進程退出
exit(0);
setsid(); //成為新進程組組長和新會話領導,脫離控制終端
chdir("/"); //設定工作目錄為根目錄
umask(0); //重設檔案存取權限掩碼
for(;i<MAXFD;i++) //儘可能關閉所有從父進程繼承來的檔案
close(i);
}
int main()
{
Daemon(); //成為守護進程
while(1){
sleep(1);
}
return 0;
}
Example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <time.h>
main()
{
int i = 0;
if(fork() > 0)
exit(0);
setsid();
chdir("/");
umask(0);
for(; i < 64; i++)
{
close(i);
}
i = 0;
while(i < 10)
{
printf("%d\n",i);
time_t ttime;
time(&ttime);
struct tm *pTm = gmtime(&ttime);
syslog(LOG_INFO,"%d %04d:%02d:%02d", i, (1900 + pTm->tm_year), (1 + pTm->tm_mon), (pTm->tm_mday));
i++;
sleep(2);
}
}
通過查看vi /var/log/messages
Linux進程式控制制(三)