golang 設(shè)置進(jìn)程退出時(shí)kill所有子進(jìn)程的三種方法
在 Go 語(yǔ)言中使用 os/exec.Command 啟動(dòng)子進(jìn)程時(shí),默認(rèn)情況下子進(jìn)程不會(huì)隨父進(jìn)程(當(dāng)前進(jìn)程)的退出而自動(dòng)終止,因?yàn)樽舆M(jìn)程會(huì)被系統(tǒng)(如 init 或系統(tǒng)進(jìn)程管理器)收養(yǎng)并繼續(xù)運(yùn)行。要實(shí)現(xiàn)“父進(jìn)程退出時(shí)自動(dòng)終止子進(jìn)程”的功能,需要根據(jù)操作系統(tǒng)采用不同的策略。沒有完美的跨平臺(tái)標(biāo)準(zhǔn)解決方案,但以下是常見實(shí)現(xiàn)方式,包括手動(dòng)處理信號(hào)和平臺(tái)特定機(jī)制。以下解釋逐步如何實(shí)現(xiàn),并提供代碼示例。
1.通用方式:捕獲信號(hào)并手動(dòng)終止子進(jìn)程(跨平臺(tái),適用于正常退出和可捕獲信號(hào))
這是一種簡(jiǎn)單、跨平臺(tái)的做法:父進(jìn)程監(jiān)聽退出信號(hào)(如 SIGINT、SIGTERM),在收到信號(hào)時(shí)主動(dòng)殺死子進(jìn)程。這種方式適用于父進(jìn)程正常退出或被可捕獲信號(hào)終止的情況,但如果父進(jìn)程被 kill -9 (SIGKILL) 強(qiáng)制殺死,則無(wú)效(因?yàn)?SIGKILL 無(wú)法捕獲)。
步驟:
- 使用
signal.Notify監(jiān)聽信號(hào)。 - 啟動(dòng)子進(jìn)程后,記錄
PID。 - 在信號(hào)處理中逐個(gè)終止子進(jìn)程(如果需要?dú)⑺雷舆M(jìn)程的子進(jìn)程,可使用進(jìn)程組,詳見下文)。
代碼示例:
package main
import (
"context"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
)
func main() {
// 啟動(dòng)子進(jìn)程,例如運(yùn)行 "sleep 60"
cmd := exec.Command("sleep", "60")
if err := cmd.Start(); err != nil {
fmt.Println("Start error:", err)
return
}
fmt.Printf("Child process started with PID: %d\n", cmd.Process.Pid)
// 監(jiān)聽信號(hào)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// goroutine 處理信號(hào)
go func() {
sig := <-sigs
fmt.Printf("Received signal: %v\n", sig)
if cmd.Process != nil {
if err := cmd.Process.Kill(); err != nil {
fmt.Println("Kill error:", err)
}
}
os.Exit(0)
}()
// 等待子進(jìn)程正常結(jié)束(可選,如果子進(jìn)程有自己的退出邏輯)
if err := cmd.Wait(); err != nil {
fmt.Println("Wait error:", err)
}
}
擴(kuò)展:使用進(jìn)程組殺死子進(jìn)程及其后代(Unix-like 系統(tǒng)):
要?dú)⑺勒麄€(gè)進(jìn)程樹:
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
// 在殺死時(shí):
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
2.Linux 特定:使用 Pdeathsig 自動(dòng)發(fā)送信號(hào)
在 Linux 上,可以使用 syscall.SysProcAttr.Pdeathsig 設(shè)置當(dāng)父進(jìn)程(或創(chuàng)建線程)死亡時(shí),內(nèi)核自動(dòng)向子進(jìn)程發(fā)送指定信號(hào)(如 SIGTERM 或 SIGKILL)。這實(shí)現(xiàn)了“自動(dòng)”終止,即使父進(jìn)程被 SIGKILL 殺死。
注意:
- 只適用于 Linux(基于 prctl(PR_SET_PDEATHSIG))。
- 如果子進(jìn)程是多線程的,可能有邊緣問題(見 Go issue #27505)。
- 子進(jìn)程需要能響應(yīng)信號(hào)(例如,如果它忽略 SIGTERM,則用 SIGKILL)。
代碼示例:
package main
import (
"fmt"
"os/exec"
"syscall"
)
func main() {
cmd := exec.Command("sleep", "60")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGKILL, // 或 syscall.SIGTERM
}
if err := cmd.Start(); err != nil {
fmt.Println("Start error:", err)
return
}
fmt.Printf("Child process started with PID: %d\n", cmd.Process.Pid)
// 父進(jìn)程可以繼續(xù)其他工作,或直接退出測(cè)試
// cmd.Wait() // 可選
}
3.Windows 特定:使用 Job Object 自動(dòng)終止
在 Windows 上,沒有直接等價(jià)于 Pdeathsig 的機(jī)制,但可以使用 Windows Job Object 將進(jìn)程分組,并設(shè)置 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 標(biāo)志:當(dāng) Job Object 的最后一個(gè)句柄關(guān)閉(父進(jìn)程退出)時(shí),自動(dòng)殺死所有關(guān)聯(lián)進(jìn)程。
注意:
- Go 標(biāo)準(zhǔn)庫(kù)不直接支持,需要使用
golang.org/x/sys/windows包調(diào)用 Windows API。 - 需要安裝依賴:
go get golang.org/x/sys/windows。 - 這會(huì)終止子進(jìn)程及其后代。
代碼示例(簡(jiǎn)化版,需要處理錯(cuò)誤和清理):
package main
import (
"fmt"
"os/exec"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func main() {
// 創(chuàng)建 Job Object
job, err := windows.CreateJobObject(nil, nil)
if err != nil {
fmt.Println("CreateJobObject error:", err)
return
}
defer windows.CloseHandle(job)
// 設(shè)置 Kill on Job Close
var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
info.BasicLimitInformation.LimitFlags = windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
err = windows.SetInformationJobObject(
job,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)),
)
if err != nil {
fmt.Println("SetInformationJobObject error:", err)
return
}
// 將當(dāng)前進(jìn)程分配到 Job Object(子進(jìn)程會(huì)繼承)
err = windows.AssignProcessToJobObject(job, windows.CurrentProcess())
if err != nil {
fmt.Println("AssignProcessToJobObject error:", err)
return
}
// 啟動(dòng)子進(jìn)程
cmd := exec.Command("timeout", "/t", "60") // Windows 等價(jià)于 sleep
if err := cmd.Start(); err != nil {
fmt.Println("Start error:", err)
return
}
fmt.Printf("Child process started with PID: %d\n", cmd.Process.Pid)
// cmd.Wait() // 可選
}
說明:子進(jìn)程啟動(dòng)后會(huì)自動(dòng)關(guān)聯(lián) Job Object(因?yàn)楦高M(jìn)程已關(guān)聯(lián))。父進(jìn)程退出時(shí),Job Object 關(guān)閉,子進(jìn)程被終止。
其他注意
- 跨平臺(tái)實(shí)現(xiàn):可以使用條件編譯(
//go:build linux等)來(lái)區(qū)分平臺(tái)特定代碼。對(duì)于通用場(chǎng)景,優(yōu)先使用信號(hào)捕獲方式。 - 測(cè)試:在 Unix 上用
kill <pid>測(cè)試;在 Windows 上用 Task Manager 殺死父進(jìn)程。 - 局限性:沒有方式能 100% 保證在所有情況下(如父進(jìn)程崩潰)自動(dòng)終止,尤其是跨平臺(tái)。如果子進(jìn)程是 daemon 或忽略信號(hào),可能需要額外處理。
到此這篇關(guān)于golang 設(shè)置進(jìn)程退出時(shí)kill所有子進(jìn)程的三種方法的文章就介紹到這了,更多相關(guān)Golang殺死子進(jìn)程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言中同一個(gè)package中函數(shù)互相調(diào)用為undefined的解決
這篇文章主要介紹了Go語(yǔ)言中同一個(gè)package中函數(shù)互相調(diào)用為undefined的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
一文掌握Go語(yǔ)言并發(fā)編程必備的Mutex互斥鎖
Go 語(yǔ)言提供了 sync 包,其中包括 Mutex 互斥鎖、RWMutex 讀寫鎖等同步機(jī)制,本篇博客將著重介紹 Mutex 互斥鎖的基本原理,需要的可以參考一下2023-04-04
Go語(yǔ)言利用heap實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中heap的使用以及如何利用heap實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列的相關(guān)資料,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05
Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用教程
輸入輸出在任何一門語(yǔ)言中都必須提供的一個(gè)功能,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言標(biāo)準(zhǔn)輸入輸出庫(kù)的基本使用,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的全過程
Go語(yǔ)言是Google的開源編程語(yǔ)言,廣泛應(yīng)用于云計(jì)算、分布式系統(tǒng)開發(fā)等領(lǐng)域,在Linux上也有大量的應(yīng)用場(chǎng)景,這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下編譯并運(yùn)行g(shù)o項(xiàng)目的相關(guān)資料,需要的朋友可以參考下2023-11-11
golang中拿slice當(dāng)queue和拿list當(dāng)queue使用分析
這篇文章主要為大家介紹了golang?中拿slice當(dāng)queue和拿list當(dāng)queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
詳解Go語(yǔ)言微服務(wù)開發(fā)框架之Go chassis
分布式系統(tǒng)中每個(gè)進(jìn)程的動(dòng)態(tài)配置管理及運(yùn)行時(shí)熱加載就成為了一個(gè)亟待解決的問題。go chassis汲取了netflix的archaius框架經(jīng)驗(yàn),并做出來(lái)自己的創(chuàng)新特性。2021-05-05
搭建Go語(yǔ)言的ORM框架Gorm的具體步驟(從Java到go)
很多朋友不知道如何使用Goland軟件,搭建一個(gè)ORM框架GORM,今天小編給大家分享一篇教程關(guān)于搭建Go語(yǔ)言的ORM框架Gorm的具體步驟(從Java到go),感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2022-09-09
Golang中使用Date進(jìn)行日期格式化(沿用Java風(fēng)格)
這篇文章主要介紹了Golang中使用Date進(jìn)行日期格式化,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Golang測(cè)試框架goconvey進(jìn)行單元測(cè)試流程介紹
goconvey是一款針對(duì)Golang的測(cè)試框架,可以管理和運(yùn)行測(cè)試用例,同時(shí)提供了豐富的斷言函數(shù),并支持很多Web界面特性,這篇文章主要介紹了使用goconvey進(jìn)行單元測(cè)試流程,感興趣的同學(xué)可以參考下文2023-05-05

