Go創建daemon程序

jasonfang 8年前發布 | 14K 次閱讀 Go語言 Google Go/Golang開發

daemon的概念

守護進程(daemon)就是一直在后臺運行的進程,它沒有控制終端,無法和前臺的用戶交互。當我們打開一個終端時會創建一個session會話(shell),從用戶登錄開始到用戶退出為止,這段時間內在該終端執行的進程都屬于這一個會話。一個會話一般包含一個前臺進程組、一個后臺進程組和一個會話首進程(shell程序本身)。 例如用以下命令啟動5個進程:

$ proc1 | proc2 &

$ proc3 | proc4 | proc5

proc1和proc2屬于同一個后臺進程組,proc3、proc4、proc5屬于同一個前臺進程組,Shell進程本身屬于一個單獨的進程組。這些進程組的控制終端相同,它們屬于同一個session。 "后臺任務"與"前臺任務"的本質區別只有一個:是否繼承標準輸入。后臺任務不再繼承當前 session 的標準輸入(stdin),你無法向后臺任務輸入指令了,但是后臺任務繼承了標準輸出(stdout)和標準錯誤(stderr)后臺任務的所有輸出依然會同步地在命令行下顯示

當終端關閉或者檢測到網絡連接斷開時會將掛斷信號(SIGHUP)發送給終端控制進程(會話期首進程,shell進程)。如果會話期首進程接收到SIGHUP信號后會終止,會同時給前臺進程組發送SIGHUP信號(進程接收到SIGHUP信號默認處理是退出),shell的 huponexit 參數

(shopt | grep huponexit)

決定了shell退出時是否發送SIGHUP信號給后臺進程組。

如何實現守護進程

守護進程要與從啟動它的父進程(一般是shell程序)的運行環境隔離開來,需要處理的內容大致包括會話、控制終端、進程組、文件描述符、文件權限掩碼以及工作目錄等。

void init_daemon()
{
    pid_t pid;
    int i = 0;
// 1. 創建子進程,父進程退出,父進程退出子進程變成孤兒進程,孤兒進程由init進程(pid為1)收養
    if ((pid = fork()) == -1) {
        printf("Fork error !\n");
        exit(1);
    }
    if (pid != 0) {
        exit(0);        // 父進程退出
    }

// 2. 子進程調用setsid創建新會話,成為成為會話首進程,并且子進程成為一個新進程組的組長進程,而新的會話也脫離了控制終端的控制。 //(組長進程調用 setsid會出錯,所以第一步fork出子進程,fork出的子進程一定不會是組長進程) setsid();
//3. 子進程變成無終端的會話首進程,但是它仍然可以重新申請打開一個控制終端。可以通過再次創建子進程結束當前進程,使進程不再是會話首進程來禁止進程重新打開控制終端。 if ((pid = fork()) == -1) { printf("Fork error !\n"); exit(-1); } if (pid != 0) { exit(0);
} //4. 改變當前目錄為根目錄,重設文件權限掩碼,關閉文件描述符 //因為這幾樣東西都是繼承自父進程的 chdir("/tmp"); // 改變工作目錄 umask(0); // 重設文件掩碼 for (; i < getdtablesize(); ++i) { close(i); // 關閉打開的文件描述符 }

return;

} </code></pre>

go語言如何實現守護進程

目前Go程序還不能實現daemon,因為go程序在啟動時runtime可能會創建多個線程(用于內存管理,垃圾回收,goroutine管理等),而fork并不能處理好擁有多個線程的進程。

d := flag.Bool("d", false, "Whether or not to launch in the background(like a daemon)")
if *d {
    cmd := exec.Command(os.Args[0],
        "-close-fds",
    )
    serr, err := cmd.StderrPipe()
    if err != nil {
        log.Fatalln(err)
    }
    err = cmd.Start()
    if err != nil {
        log.Fatalln(err)
    }
    s, err := ioutil.ReadAll(serr)
    s = bytes.TrimSpace(s)
    if bytes.HasPrefix(s, []byte("addr: ")) {
        fmt.Println(string(s))
        cmd.Process.Release()
    } else {
        cmd.Process.Kill()
    }
}

`

參考資料

Linux 守護進程的實現

Linux 守護進程的啟動方法

 

來自: http://shanks.leanote.com/post/Go創建daemon程序

 

 本文由用戶 jasonfang 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!