Linux中創建守護進程setsid()



簡 述: 前面幾篇,剛寫過了父子進程的實例。這裏寫一個守護進程 的例子,從瞭解到運用 setsid() ;守護進程也就是脫離於終端,不需要和用戶交流的,不受註銷影響的後臺程序(可理解爲 win 中的服務 )。


編程環境:

💻: uos20 📎 gcc/g++ 8.3 📎 gdb8.0

💻: MacOS 10.14 📎 gcc/g++ 9.2 📎 gdb8.3


守護進程的特點:

  • 後臺服務程序
  • 獨立(是脫離於)控制終端的,用戶不需要和終端交互
  • 週期性的執行某任務
  • 不受用戶登錄註銷的影響
  • 一般採用 d 結尾的名字(服務)

進程組 - 多個進程:

  • 進程組的組長?
    • 組長是是組裏的第一個進程
    • 進程組的 ID == 進程組的組長的 ID

會話 - 多個進程組:

進程組是由多個進程組成,而會話是由多個進程組組成成。

  • 創建一個會話注意的事項:
    • 不能是組長進程
    • 創建會話的進程成爲新進程組的組長
    • 有些 Linux 版本需要 root 權限執行此操作(Ubuntu 不需要)
    • 創建出的新會話會丟棄原有的控制終端
    • 一般步驟:先 fork(),父親死,兒子創建繪畫操作(setsid())
  • 獲取進程所屬的會話 ID:
    • pid_t getsid(pid_t pid);
  • 創建一個會話:
    • pid_t setsid(void);

創建守護進程模型:

下面列出創建一個標準的進程守護模型操作流程:

  1. fork() 進程,父進程退出 (必須)
  2. 子進程創建新的會話 (必須)
    • 使用 setsid()
  3. 改變當前工作目錄 chdir (可選)
    • 比如,U 盤插在筆記本,運行 U 盤文件夾裏面的可執行程序,然後拔掉,會有一些影響
  4. 重設文件掩碼 (可選)
    • 子進程會繼承父進程的掩碼
    • 增加子進程程序操作的靈活性
    • umask(0)
  5. 關閉文件描述符 (可選)
    • 節約資源,關閉此進程的 PCB 的文件描述符表中 0、1、2 三個,因爲預警不需要和終端交互,故可關閉
  6. 執行核心工作 (必須)
    • 你想讓該守護進程乾的事情

寫一個例子:

對上面的函數使用,寫一個例子:寫一個守護進程,每隔 2s 獲取一次系統時間,寫入到文本文件中。

  • 代碼示例

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <sys/stat.h>
    #include <time.h>
    
    void func(int no); //用作回調函數,給 signal() 調用
    
    int main(int argc, char *argv[])
    {
        pid_t pid = fork();
    
        if (pid > 0) {
            _exit(1); //父進程退出
        } else if (pid == 0) {
            setsid();  //子進程創建爲會話
            chdir("/Users/muli/Desktop/");  //改變進程的工作目錄
            umask(0);    //重設置文件掩碼
    
            close(STDIN_FILENO);   //關閉和終端的聯繫,文件描述符
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
    
            __sigaction_u sigactu;  //設置信號捕捉
            sigactu.__sa_handler = func;
    
            struct sigaction act;
            act.sa_flags = 0;
            act.__sigaction_u = sigactu;
            sigaddset(&act.sa_mask, SIGQUIT);
    
            itimerval time;  //設置週期性的定時器
            time.it_value.tv_sec = 2;
            time.it_value.tv_usec = 0;
            time.it_interval.tv_sec = 1;
            time.it_interval.tv_usec = 0;
    
            sigaction(SIGVTALRM, &act, NULL);          //捕捉(下面一行發射的)信號,在 fun() 裏面實現
            setitimer(ITIMER_VIRTUAL, &time, NULL);    //使用定時器,發射信號
    
            while (true); //保持該守護進程不死亡
        }
    
        return 0;
    }
    
    void func(int no) 
    {
        time_t currtime;  //獲取系統當前時間,傳出參數
        time(&currtime);
        char* ptr = ctime(&currtime);
    
        int fd = open("/Users/muli/Desktop/setsid.txt", O_CREAT | O_WRONLY | O_APPEND, 0777);  //寫入磁盤文件
        write(fd, ptr, sizeof(ptr) + 1);
        close(fd);
    }
    
  • 代碼分析:

    這裏 sigaction(SIGVTALRM, &act, NULL); 和代碼 setitimer(ITIMER_VIRTUAL, &time, NULL); 這一行,成爲互相對應的關係。

    ,使用了週期定時器 setitimer,第一個參數填了三個枚舉之一,但是對應的 sigaction 信號捕捉中,就要填寫對應的參數(見 man 手冊);注意,要先有捕捉函數,後實現信號發射函數,避免出現信號已經發射了,但是捕捉函數還沒有實現或執行。

  • 運行效果:


文件掩碼是什麼?

  • linux中的 umask 函數主要用於:在創建新文件或目錄時 屏蔽掉新文件或目錄不應有的訪問允許權限。
  • 文件的訪問允許權限共有9種,分別是:rwxrwxrwx
  • 它們分別代表:用戶讀 用戶寫 用戶執行 組讀 組寫 組執行 其它讀 其它寫 其它執行
  • 更多是使用 umask 命令或者 umask() 函數老表示,比如: 777;

下載地址:

16_daemon

歡迎 star 和 fork 這個系列的 linux 學習,附學習由淺入深的目錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章