守護進程(
Daemon
)是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務,不是對整個系統就是對某個用戶程序提供服務。Linux系統的大多數服務器就是通過守護進程實現的。常見的守護進程包括系統日志進程syslogd、 web服務器httpd、郵件服務器sendmail和數據庫服務器mysqld等。
守護進程一般在系統啟動時開始運行,除非強行終止,否則直到系統關機都保持運行。守護進程經常以超級用戶(root)權限運行,因為它們要使用特殊的端口(1-1024)或訪問某些特殊的資源。
一個守護進程的父進程是init進程,因為它真正的父進程在fork出子進程后就先于子進程exit退出了,所以它是一個由init繼承的孤兒進程。守護進程是非交互式程序,沒有控制終端,所以任何輸出,無論是向標準輸出設備stdout還是標準出錯設備stderr的輸出都需要特殊處理。
守護進程的名稱通常以d結尾,比如sshd、xinetd、crond等
由對守護進程的介紹我們可以得到創建守護進程的步驟:
- 創建子進程,父進程退出,讓子進程的父進程為
init
進程 - 在子進程中創建會話(使用
setsid()
函數),讓子進程創建新的進程組,并且讓該進程組成為一個新的會話(簡單來講,就是子進程的進程id=進程組id=會話id),并脫離終端控制 - 改變當前目錄(為根目錄,或者為其他目錄),防止占用可卸載的文件系統
- 重設文件權限掩碼,防止繼承的文件創建屏蔽字拒絕某些權限
- 關閉文件描述符,將標準輸入、標準輸出、標準錯誤重定向到
/dev/null
- 開始執行守護進程核心工作
- 退出守護進程
創建守護進程代碼:這里的守護進程每隔兩秒將系統當前時間寫入文件中
std::string getCurrentSystemTime()
{auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());struct tm* ptm = localtime(&tt);char date[60] = { 0 };sprintf(date, "%d-%02d-%02d-%02d.%02d.%02d",(int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,(int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);return std::string(date) + "\n";
}int test_daemon() {pid_t pid = fork();if (pid > 0) {//父進程退出return 0;}//創建一個新會話,并讓子進程成為組長和會長setsid();//改變當前目錄,防止占用可卸載的文件系統chdir("/home/edward");//重設文件權限掩碼,防止繼承的文件創建屏蔽字拒絕某些權限,增加守護進程的靈活性umask(0002);//關閉文件描述符,將標準輸入、標準輸出、標準錯誤重定向到/dev/null中close(STDIN_FILENO);open("/dev/null", O_RDWR);dup2(STDIN_FILENO, STDOUT_FILENO);dup2(STDIN_FILENO, STDERR_FILENO);//開始守護進程的工作int fd = open("test_daemon.txt", O_RDWR | O_CREAT | O_TRUNC, 0666);check_error(fd, "open error");constexpr int BUFFER_SIZE = 1024;char buffer[BUFFER_SIZE];for (int i = 0; i < 10; ++i) {my_sleep(2, 0);strcpy(buffer, getCurrentSystemTime().c_str());write(fd, buffer, strlen(buffer));}//退出守護進程的工作check_error(close(fd), "close error");
}
其中my_sleep
函數是我自己寫的一個sleep
函數,check_error
是檢查返回值是否是-1
,如果是則通過perror
輸出錯誤信息,并退出程序,getCurrentSystemTime
用于獲取系統當前時間