守護進程的概念和創建步驟
1. 進程組
也稱之爲作業。進程組ID等於第一個進程ID,因此可以通過進程ID是否等於進程組ID來判斷該進程是不是進程組的組長。
只要進程組中有一個進程存在,進程組就存在,哪怕組長已經沒了。
一個進程可以爲自己或子進程設置進程組ID。
2. 會話
由多個進程組組成。
創建會話要注意以下5點:
- 調用進程不能是進程組組長,該進程變成新會話首進程(session header)
- 該進程成爲一個新進程組的組長進程。
- 新會話丟棄原有的控制終端,該會話沒有控制終端。
- 該調用進程是組長進程,則出錯返回。
- 建立新會話時,先調動fork,父進程終止,子進程調用setsid(setsid後子進程不受終端影響,終端退出,不影響子進程)。
man 2 setsid
3. 守護進程
守護進程常常以“d”
結尾
3.1 創建守護進程模型
創建守護進程最關鍵的一步就是調用setsid函數創建一個新的Session,併成爲Session Leader
- 創建子進程,父進程退出
所有工作在子進程中進行,形式上脫離了控制終端 - 在子進程中創建新會話
setsid()函數
使子進程完全獨立出來,脫離控制 - 改變當前目錄爲根目錄
chdir()函數
防止佔用可卸載的文件系統
也可以換成其他路徑 - 重設文件權限掩碼
umask()函數
防止繼承的文件創建屏蔽字拒絕某些權限
增加守護進程的靈活性 - 關閉文件描述符
- 開始執行守護進程核心工作
- 守護進程退出處理程序模型
其中,3、4、5步不必要
3.2 守護進程的步驟
- 創建子進程fork
- 父進程退出
- 子進程當會長setsid
- 切換工作目錄$HOME
- 設置掩碼umask
- 關閉文件描述符0,1,2,爲了避免浪費資源
- 執行核心邏輯
3.3 創建一個守護進程實例:每份中在$HOME/log/ 創建一個文件 ,文件名—— 程序名.時間戳
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/time.h>
#include<time.h>
//通過宏定義文件名
#define _FILE_NAME_FORMAT_ "%s/log/mydaemon.%ld" //定義文件格式化
void touchfile(int num){
//獲取家目錄
char *HomeDir=getenv("HOME");
char strFilename[250]={0};
//獲取時間戳的函數
//time(NULL);
sprintf(strFilename, _FILE_NAME_FORMAT_, HomeDir, time(NULL));
int fd=open(strFilename, O_RDWR|O_CREAT, 0666);
//文件打開失敗
if(fd<0){
perror("open err");
exit(1);
}
close(fd);
}
int main(){
//創建守護進程的步驟如下
//創建子進程,父進程退出
pid_t pid=fork();
if(pid>0){
exit(1);
}
//當會長
setsid();
//設置掩碼
umask(0);
//切換目錄
//getenv獲取環境變量
chdir(getenv("HOME"));//切換到家目錄
//關閉文件描述符(一般通過測試之後,纔會關閉文件描述符)
//close(0),close(1),close(2)
//執行核心邏輯
//設置一個定時器——每60秒來一次
struct itimerval myit={{60,0},{60,0}};
setitimer(ITIMER_REAL, &myit, NULL);
//註冊捕獲函數
struct sigaction act;
act.sa_flags=0;
//清空阻塞信號集
sigemptyset(&act.sa_mask);
//綁定捕獲函數
act.sa_handler=touchfile;
//註冊捕獲函數
sigaction(SIGALRM, &act, NULL);
while(1){
//每隔一分鐘在/home/itheima/log創建文件
sleep(1);
}
return 0;
}
3.4 nohup指令
指令nohup
去執行進程就不會收到第一個信號
通過nohup
指令也可以達到守護進程的創建效果。(可以把進程輸出重定向到特定的地方)
- nohup 指令會讓cmd收不到SIGHUP信號
- & 代表後臺運行