欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang守護進程用法示例分析

 更新時間:2023年05月16日 11:35:14   作者:dkjhl  
這篇文章主要介紹了Golang守護進程用法示例,創(chuàng)建守護進程首先要了解go語言如何實現(xiàn)創(chuàng)建進程,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧

前言

golang實現(xiàn)守護進程,包含功能:

1. 守護進程只創(chuàng)建一次

2. 平滑創(chuàng)建業(yè)務(wù)進程

3. 業(yè)務(wù)進程掛起,守護進程能監(jiān)聽,并重啟新啟業(yè)務(wù)進程

4. 守護進程退出,也能保證業(yè)務(wù)進程退出

5. 業(yè)務(wù)進程≈子進程

6. 不影響業(yè)務(wù)進程邏輯

7. 以Linux平臺為主,其他平臺暫時沒有實施條件

分析

上一篇博文討論過如何以腳本的形式創(chuàng)建守護進程,這篇討論如何以純golang腳本實現(xiàn)守護進程的功能

  • 在 Unix 中,創(chuàng)建一個進程,通過系統(tǒng)調(diào)用 fork 實現(xiàn)(及其一些變種,如 vfork、clone)。
  • 在 Go 語言中,Linux 下創(chuàng)建進程使用的系統(tǒng)調(diào)用是 clone 。

在 C 語言中,通常會用到 2 種創(chuàng)建進程方法:

fork

pid = fork();
//pid > 0 父進程
//pid = 0 子進程
//pid < 0 出錯

程序會從 fork 處一分為二,父進程返回值大于0,并繼續(xù)運行;子進程獲得父進程的棧、數(shù)據(jù)段、堆和執(zhí)行文本段的拷貝,返回值等于0,并向下繼續(xù)運行。通過 fork 返回值可輕松判斷當前處于父進程還是子進程。

execve

execve(pathname, argv, envp);
//pathname 可執(zhí)行文件路徑
//argv 參數(shù)列表
//envp 環(huán)境變量列表

execve 為加載一個新程序到當前進程的內(nèi)存,這將丟棄現(xiàn)存的程序文本段,并為新程序重新創(chuàng)建棧、數(shù)據(jù)段以及堆。通常將這一動作稱為執(zhí)行一個新程序。

在 Go 語言中,創(chuàng)建進程方法主要有 3 種:

exec.Command

//判 斷當其是否是子進程,當父進程return之后,子進程會被 系統(tǒng)1 號進程接管
if os.Getppid() != 1 {
    // 將命令行參數(shù)中執(zhí)行文件路徑轉(zhuǎn)換成可用路徑
    filePath, _ := filepath.Abs(os.Args[0])
    cmd := exec.Command(filePath, os.Args[1:]...)
    // 將其他命令傳入生成出的進程
    cmd.Stdin = os.Stdin // 給新進程設(shè)置文件描述符,可以重定向到文件中
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Start() // 開始執(zhí)行新進程,不等待新進程退出
    os.Exit(0)
}

os.StartProcess

if os.Getppid()!=1{   
    args:=append([]string{filePath},os.Args[1:]...)
    os.StartProcess(filePath,args,&os.ProcAttr{Files:[]*os.File{os.Stdin,os.Stdout,os.Stderr}})
    os.Exit(0)
}

syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)

pid, _, sysErr := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
if sysErr != 0 {
    Utils.LogErr(sysErr)
    os.Exit(0)
}

方法1和方法2通過 os.Getppid()!=1進行判斷是否子進程,默認父進程退出之后,子進程會被1號進程接管。

但據(jù)Ubuntu Desktop 本地測試,接管孤兒進程的并不是1號進程,因此考慮到程序穩(wěn)定性和兼容性,不能夠以 ppid 作為判斷父子進程的依據(jù)。

方法3直接進行了系統(tǒng)調(diào)用,雖然可以通過 pid 進行判斷父子進程,但該方法過于底層。

綜上,以exec.Command方式,通過控制參數(shù)實現(xiàn)守護進程

實現(xiàn)

func main() {
    // ------------------------ 守護進程 start ------------------------
    basePath, _ := os.Getwd()
    baseDir := filepath.Dir(basePath)
    fmt.Println(fmt.Sprintf("basePath is %s and baseDir is %s", basePath, baseDir))
    // step1
    // 創(chuàng)建監(jiān)聽退出chan
    c := make(chan os.Signal)
    // 監(jiān)聽指定信號 ctrl+c kill
    signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
    go func() {
        for s := range c {
            switch s {
            case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
                utils.StopBusinessProcess(fmt.Sprintf("go_start | grep business"))
                os.Exit(0)
            default:
                fmt.Println("test stop others...")
            }
        }
    }()
    fmt.Println(fmt.Sprintf("os.args is %v", os.Args))
    join := strings.Join(os.Args, "")
    // step2
    if !strings.Contains(join, "-daemon") {
        fmt.Println("enter daemon branch...")
        isE, ierr := utils.CheckProRunning("go_start | grep daemon")
        if ierr != nil {
            fmt.Println("check daemon process failed, " + ierr.Error())
            return
        }
        if isE {
            fmt.Println("daemon process exist!")
        } else {
            fmt.Println("start daemon process...")
            // 啟動守護進程
            cmd := exec.Command(os.Args[0], "-c", os.Args[2], "-d", os.Args[4], "-e", os.Args[6], "-daemon")
            cmd.Stdin = os.Stdin
            cmd.Stdout = os.Stdout
            cmd.Stderr = os.Stderr
            strerr := cmd.Start()
            if strerr != nil {
                fmt.Println("start daemon process fail," + strerr.Error())
                return
            }
            fmt.Println("start daemon process success!")
            time.Sleep(time.Second * 2)
            daePid := cmd.Process.Pid
            isDae, daeErr := utils.CheckProRunning("go_start | grep daemon")
            if daeErr != nil {
                fmt.Println("check daemon process failed, " + daeErr.Error())
                return
            }
            if isDae {
                fmt.Println(fmt.Sprintf("start daemon process success, pid is %d", daePid))
                return
            } else {
                fmt.Println("warning! start business process fail...")
            }
        }
    }
    // step3
    join = strings.Join(os.Args, "")
    if strings.Contains(join, "-daemon") {
        fmt.Println("enter business branch...")
        for {
            exist, checkerr := utils.CheckProRunning("go_start | grep business")
            if checkerr != nil {
                fmt.Println("check business failed, " + checkerr.Error())
                return
            }
            if exist {
                fmt.Println("business process exist!")
                time.Sleep(time.Second * 5)
                continue
            }
            fmt.Println("start business process...")
            command := exec.Command(fmt.Sprintf(fmt.Sprintf("%s/go_start", basePath), "-business", "-c", os.Args[2], "-d", os.Args[4], "-e", os.Args[6]))
            command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
            if comerr := command.Start(); comerr != nil {
                fmt.Println("start business process failed, " + comerr.Error())
                return
            }
            time.Sleep(time.Second * 5)
            businessPid := command.Process.Pid
            exist, checkerr = utils.CheckProRunning("go_start | grep business")
            if checkerr != nil {
                fmt.Println("check business process failed, " + checkerr.Error())
                return
            }
            if exist {
                fmt.Println(fmt.Sprintf("start business process suceess, pid is %d", businessPid))
            } else {
                fmt.Println("warning! start business process fail...")
            }
        }
    }
    // ------------------------ 守護進程 end ------------------------
    // ------------------------ 業(yè)務(wù)進程 start ------------------------
    fmt.Println("hello, welcome to business detail!")
}

相關(guān)工具方法:

package utils
import (
    "os"
    "fmt"
    "go_start/core/global"
    "os/exec"
    "runtime"
    "strconv"
    "strings"
    "syscall"
)
func StopBusinessProcess(serverName string) {
    global.G_LOG.Info("start to stop business...")
    pid, _ := GetPid(serverName)
    if pid > 0 {
        global.G_LOG.Info(fmt.Sprintf("stop %s ...", serverName))
        syscall.Kill(pid, syscall.SIGKILL)
        global.G_LOG.Info(fmt.Sprintf("stop business success, pid is %d", pid))
    }
}
//根據(jù)進程名判斷進程是否運行
func CheckProRunning(serverName string) (bool, error) {
    a := `ps -ef|grep ` + serverName + `|grep -v grep|awk '{print $2}'`
    pid, err := runCommand(a)
    if err != nil {
        return false, err
    }
    return pid != "", nil
}
//根據(jù)進程名稱獲取進程ID
func GetPid(serverName string) (pid int, err error) {
    a := `ps -ef|grep ` + serverName + `|grep -v grep|awk '{print $2}'`
    var pidStr string
    if pidStr, err = runCommand(a); err != nil {
        return
    }
    pid, err = strconv.Atoi(pidStr)
    return
}
func runCommand(cmd string) (string, error) {
    if runtime.GOOS == "windows" {
        return runInWindows(cmd)
    } else {
        return runInLinux(cmd)
    }
}
func runInWindows(cmd string) (string, error) {
    result, err := exec.Command("cmd", "/c", cmd).Output()
    if err != nil {
        return "", err
    }
    return strings.TrimSpace(string(result)), err
}
func runInLinux(cmd string) (string, error) {
    result, err := exec.Command("/bin/sh", "-c", cmd).Output()
    if err != nil {
        return "", err
    }
    return strings.TrimSpace(string(result)), err
}

說明

1、啟動go_start二進制文件,方式:./go_start -c param1 -d param2 -e param3,這里第一次進入main方法

2、main方法中,os.Args = [./go_start -c param1 -d param2 -c param3],此時不包含"-daemon"參數(shù),進入step2,走創(chuàng)建守護進程代碼分支,執(zhí)行創(chuàng)建守護進程,exec.Command(./go_start -c param1 -d param2 -e param3 -daemon),第二次進入main方法

3、main方法中,os.Args = [./go_start -c param1 -d param2 -c param3 -daemon],此時包含"-daemon",進入step3,走創(chuàng)建業(yè)務(wù)進程分支,執(zhí)行創(chuàng)建業(yè)務(wù)進程,exec.Command(./go_start -c param1 -d param2 -e param3);此時守護進程存在,每隔5秒監(jiān)聽一次業(yè)務(wù)進程是否存在,如果存在則不操作;不存在則重新執(zhí)行創(chuàng)建業(yè)務(wù)進程exec.Command(./go_start -c param1 -d param2 -e param3);

4、執(zhí)行具體的業(yè)務(wù)進程邏輯

驗證

ps -ef | grep go_start

]$ 110  1   ./go_start -c param1 -d param2 -c param3             -- ①
]$ 111  1   ./go_start -c param1 -d param2 -c param3 -daemon     -- ②
]$ 112  111 ./go_start -business -c param1 -d param2 -c param3   -- ③

剛開始會出現(xiàn)三個進程,假設(shè)進程id如上,一會之后①會消失,這是正常的,因為剛開始的啟動就是①,然后只剩下進程②和③

]$ 111  1   ./go_start -c param1 -d param2 -c param3 -daemon     -- ②
]$ 112  111 ./go_start -business -c param1 -d param2 -c param3   -- ③

驗證kill業(yè)務(wù)進程:會啟動新的業(yè)務(wù)進程,守護進程不變;所以執(zhí)行:kill 112

]$ 111  1   ./go_start -c param1 -d param2 -c param3 -daemon     -- ②
]$ 112  111 [go_start] <defunct>                                 -- ③'
]$ 113  111 ./go_start -business -c param1 -d param2 -c param3   -- ③

這里kill 112后,會出現(xiàn)一個僵尸進程,不影響實際業(yè)務(wù)進程的創(chuàng)建和運行,不需要理會;假設(shè)新創(chuàng)建的業(yè)務(wù)進程pid為113

驗證kill守護進程:整個程序退出,也就是執(zhí)行ps -ef | grep go_start后,沒有對應(yīng)的守護進程和業(yè)務(wù)進程,同時僵尸進程也會消失;得益于以下代碼,進程組

command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

綜上

純golang語言形式實現(xiàn)了守護進程,針對啟動業(yè)務(wù)進程,優(yōu)化點:可以使用go func(){}()協(xié)程方式啟動更優(yōu)雅,這里先不實施,待后續(xù)有空改進;

缺點:依然要通過參數(shù)控制守護進程和業(yè)務(wù)進程,-daemon -business,期望統(tǒng)一起來,不用參數(shù)控制

放在(3)實現(xiàn)

附錄

以下是關(guān)于信號量的一個記錄,當作參考文檔

信號動作說明
SIGHUP1Term終端控制進程結(jié)束(終端連接斷開)
SIGINT2Term用戶發(fā)送INTR字符(Ctrl+C)觸發(fā)
SIGQUIT3Core用戶發(fā)送QUIT字符(Ctrl+/)觸發(fā)
SIGILL4Core非法指令(程序錯誤、試圖執(zhí)行數(shù)據(jù)段、棧溢出等)
SIGABRT6Core調(diào)用abort函數(shù)觸發(fā)
SIGFPE8Core算術(shù)運行錯誤(浮點運算錯誤、除數(shù)為零等)
SIGKILL9Term無條件結(jié)束程序(不能被捕獲、阻塞或忽略)
SIGSEGV11Core無效內(nèi)存引用(試圖訪問不屬于自己的內(nèi)存空間、對只讀內(nèi)存空間進行寫操作)
SIGPIPE13Term消息管道損壞(FIFO/Socket通信時,管道未打開而進行寫操作)
SIGALRM14Term時鐘定時信號
SIGTERM15Term結(jié)束程序(可以被捕獲、阻塞或忽略)
SIGUSR130,10,16Term用戶保留
SIGUSR231,12,17Term用戶保留
SIGCHLD20,17,18Ign子進程結(jié)束(由父進程接收)
SIGCONT19,18,25Cont繼續(xù)執(zhí)行已經(jīng)停止的進程(不能被阻塞)
SIGSTOP17,19,23Stop停止進程(不能被捕獲、阻塞或忽略) SIGTSTP 18,20,24 Stop 停止進程(可以被捕獲、阻塞或忽略) SIGTTIN 21,21,26 Stop 后臺程序從終端中讀取數(shù)據(jù)時觸發(fā) SIGTTOU 22,22,27 Stop 后臺程序向終端中寫數(shù)據(jù)時觸發(fā)

到此這篇關(guān)于Golang守護進程用法示例分析的文章就介紹到這了,更多相關(guān)Golang守護進程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • 淺談golang 中time.After釋放的問題

    淺談golang 中time.After釋放的問題

    這篇文章主要介紹了淺談golang 中time.After釋放的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Go語言文件讀寫操作案例詳解

    Go語言文件讀寫操作案例詳解

    這篇文章主要為大家介紹了Go語言文件讀寫操作案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Go排序算法通用qsort函數(shù)使用示例

    Go排序算法通用qsort函數(shù)使用示例

    這篇文章主要為大家介紹了Go排序算法通用qsort函數(shù)使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • 詳解golang中?work與?module?的區(qū)別與聯(lián)系

    詳解golang中?work與?module?的區(qū)別與聯(lián)系

    Go?模塊通常由一個項目或庫組成,并包含一組隨后一起發(fā)布的?Go?包,Go?模塊通過允許用戶將項目代碼放在他們選擇的目錄中并為每個模塊指定依賴項的版本,解決了原始系統(tǒng)的許多問題,本文將給大家介紹一下golang中?work與?module?的區(qū)別與聯(lián)系,需要的朋友可以參考下
    2023-09-09
  • Go語言中函數(shù)的使用方法詳解

    Go語言中函數(shù)的使用方法詳解

    這篇文章主要為大家詳細介紹了Go語言中函數(shù)的使用方法的相關(guān)資料,文中的示例代碼講解詳細,對我們學(xué)習(xí)Go語言有一定的幫助,感興趣的可以了解一下
    2023-04-04
  • Golang字符串變位詞示例詳解

    Golang字符串變位詞示例詳解

    這篇文章主要給大家介紹了關(guān)于GoLang字符串變位詞的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • 解決Go中使用seed得到相同隨機數(shù)的問題

    解決Go中使用seed得到相同隨機數(shù)的問題

    這篇文章主要介紹了Go中使用seed得到相同隨機數(shù)的問題,需要的朋友可以參考下
    2019-10-10
  • GO語言數(shù)組和切片實例詳解

    GO語言數(shù)組和切片實例詳解

    這篇文章主要介紹了GO語言數(shù)組和切片的用法,以實例形式較為詳細的分析了GO語言中數(shù)組與切片的創(chuàng)建及使用技巧,是深入學(xué)習(xí)GO語言的基礎(chǔ),需要的朋友可以參考下
    2014-12-12
  • Go語言錯誤處理異常捕獲+異常拋出

    Go語言錯誤處理異常捕獲+異常拋出

    這篇文章主要介紹了Go語言錯誤處理異常捕獲和異常拋出,Go語言的作者認為java等語言的錯誤處理底層實現(xiàn)較為復(fù)雜,就實現(xiàn)了函數(shù)可以返回錯誤類型以及簡單的異常捕獲,雖然簡單但是也非常精妙,大大的提高了運行效率,下文需要的朋友可以參考一下
    2022-02-02
  • golang 中獲取字符串個數(shù)的方法

    golang 中獲取字符串個數(shù)的方法

    這篇文章主要介紹了golang 中獲取字符串個數(shù) ,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08

最新評論