用C語言在Linux系統下建立守護程式(Daemon)

大囚長發表於2019-01-15

轉載自:
https://blog.csdn.net/str999_cn/article/details/78686923

      守護程式(daemon)是指在後臺執行的,沒有控制終端與之相連的程式。它獨立於控制終端,週期性地執行某種任務。Linux的大多數伺服器就是用守護程式的方式實現的。如web伺服器程式http等。守護程式在後臺執行,類似於Windows中的系統服務。


      編寫守護程式程式的要點:


(1)讓程式在後臺執行。方法是呼叫fork()產生一個子程式,然後使父程式退出。

(2)呼叫setsid()建立一個新對話期。控制終端、登入會話和程式組通常是從父程式繼承下來的,守護程式要擺脫它們,不受它們的影響,方法是呼叫setsid()使程式成為一個會話組長。setsid()呼叫成功後,程式成為新的會話組長和程式組長,並與原來的登入會話、程式組和控制終端脫離。

(3)禁止程式重新開啟控制終端。經過以上步驟,程式已經成為一個無終端的會話組長,但是它可以重新申請開啟一個終端。為了避免這種情況發生,可以通過使程式不再是會話組長來實現。再一次通過fork()建立新的子程式,使呼叫fork的程式退出。

(4)關閉不再需要的檔案描述符。子程式從父程式繼承開啟的檔案描述符。如不關閉,將會浪費系統資源,造成程式所在的檔案系統無法卸下以及引起無法預料的錯誤。首先獲得最高檔案描述符值,然後用一個迴圈程式,關閉0到最高檔案描述符值的所有檔案描述符。

(5)將當前目錄更改為根目錄。

(6)子程式從父程式繼承的檔案建立遮蔽字可能會拒絕某些許可權。為防止這一點,使用unmask(0)將遮蔽字清零。

(7)處理SIGCHLD訊號。對於伺服器程式,在請求到來時往往生成子程式處理請求。如果父程式不等待子程式結束,子程式將成為殭屍程式(zombie),從而佔用系統資源。如果父程式等待子程式結束,將增加父程式的負擔,影響伺服器程式的併發效能。在Linux下可以簡單地將SIGCHLD訊號的操作設為SIG_IGN。這樣,子程式結束時不會產生殭屍程式。


      守護程式的例項:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/param.h>
  6. #include <sys/stat.h>
  7. #include <time.h>
  8. #include <syslog.h>
  9. int init_daemon(void)
  10. {
  11. int pid;
  12. int i;
  13. //忽略終端I/O訊號,STOP訊號
  14. signal(SIGTTOU,SIG_IGN);
  15. signal(SIGTTIN,SIG_IGN);
  16. signal(SIGTSTP,SIG_IGN);
  17. signal(SIGHUP,SIG_IGN);
  18. pid = fork();
  19. if(pid > 0) {
  20. exit(0); //結束父程式,使得子程式成為後臺程式
  21. }
  22. else if(pid < 0) {
  23. return -1;
  24. }
  25. //建立一個新的程式組,在這個新的程式組中,子程式成為這個程式組的首程式,以使該程式脫離所有終端
  26. setsid();
  27. //再次新建一個子程式,退出父程式,保證該程式不是程式組長,同時讓該程式無法再開啟一個新的終端
  28. pid=fork();
  29. if( pid > 0) {
  30. exit(0);
  31. }
  32. else if( pid< 0) {
  33. return -1;
  34. }
  35. //關閉所有從父程式繼承的不再需要的檔案描述符
  36. for(i=0;i< NOFILE;close(i++));
  37. //改變工作目錄,使得程式不與任何檔案系統聯絡
  38. chdir("/");
  39. //將檔案當時建立遮蔽字設定為0
  40. umask(0);
  41. //忽略SIGCHLD訊號
  42. signal(SIGCHLD,SIG_IGN);
  43. return 0;
  44. }
  45. int main()
  46. {
  47. time_t now;
  48. init_daemon();
  49. syslog(LOG_USER|LOG_INFO,"TestDaemonProcess! \n");
  50. while(1) {
  51. sleep(8);
  52. time(&now);
  53. syslog(LOG_USER|LOG_INFO,"SystemTime: \t%s\t\t\n",ctime(&now));
  54. }
  55. }

      編譯執行上述程式。然後用ps -ef 命令檢視程式狀態,該程式狀態如下:



      

      從結果可以看出該程式具備守護程式的所有特徵。


      檢視/var/log目錄下,先前並不存在的test.log檔案已經有了。




      用vi開啟該日誌檔案,記錄如下:


      


      最後需要關閉此守護程式。關閉的方法是通過ps -ef命令查詢到該程式的程式號,之後再用kill 命令將其殺死。





      注意:使用syslog函式前需要配置。但需要注意的是,在Centos6.x系統中,系統日誌的配置檔案已經發生了變化。不再是原來的/etc/syslog.conf了,而是/etc/rsyslog.conf。開啟上述檔案,在檔案末尾加入下面一行:

user.*    /var/log/test.log

      然後重啟syslog服務。重啟的命令也修改為:/etc/init.d/rsyslog restart




      實際上,Linux提供了完成上述同樣功能的庫函式:

  1. #include <unistd.h>
  2. int daemon(int nochdir,int noclose);

      其中,nochdir引數用於指定是否改變工作目錄,如果給它傳遞0,則工作目錄將被設定為“/”(根目錄),否則繼續使用當前工作目錄。noclose引數為0時,標準輸入、標準輸出和標準錯誤輸出都被重定向到/dev/null檔案,否則依然使用原來的裝置。該函式成功時返回0,失敗返回-1,並設定errno。


相關文章