golang 設置進程退出時kill所有子進程的三種方法
在 Go 語言中使用 os/exec.Command 啟動子進程時,默認情況下子進程不會隨父進程(當前進程)的退出而自動終止,因為子進程會被系統(tǒng)(如 init 或系統(tǒng)進程管理器)收養(yǎng)并繼續(xù)運行。要實現(xiàn)“父進程退出時自動終止子進程”的功能,需要根據(jù)操作系統(tǒng)采用不同的策略。沒有完美的跨平臺標準解決方案,但以下是常見實現(xiàn)方式,包括手動處理信號和平臺特定機制。以下解釋逐步如何實現(xiàn),并提供代碼示例。
1.通用方式:捕獲信號并手動終止子進程(跨平臺,適用于正常退出和可捕獲信號)
這是一種簡單、跨平臺的做法:父進程監(jiān)聽退出信號(如 SIGINT、SIGTERM),在收到信號時主動殺死子進程。這種方式適用于父進程正常退出或被可捕獲信號終止的情況,但如果父進程被 kill -9 (SIGKILL) 強制殺死,則無效(因為 SIGKILL 無法捕獲)。
步驟:
- 使用
signal.Notify監(jiān)聽信號。 - 啟動子進程后,記錄
PID。 - 在信號處理中逐個終止子進程(如果需要殺死子進程的子進程,可使用進程組,詳見下文)。
代碼示例:
package main
import (
"context"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
)
func main() {
// 啟動子進程,例如運行 "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)聽信號
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// goroutine 處理信號
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)
}()
// 等待子進程正常結束(可選,如果子進程有自己的退出邏輯)
if err := cmd.Wait(); err != nil {
fmt.Println("Wait error:", err)
}
}
擴展:使用進程組殺死子進程及其后代(Unix-like 系統(tǒng)):
要殺死整個進程樹:
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
// 在殺死時:
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
2.Linux 特定:使用 Pdeathsig 自動發(fā)送信號
在 Linux 上,可以使用 syscall.SysProcAttr.Pdeathsig 設置當父進程(或創(chuàng)建線程)死亡時,內(nèi)核自動向子進程發(fā)送指定信號(如 SIGTERM 或 SIGKILL)。這實現(xiàn)了“自動”終止,即使父進程被 SIGKILL 殺死。
注意:
- 只適用于 Linux(基于 prctl(PR_SET_PDEATHSIG))。
- 如果子進程是多線程的,可能有邊緣問題(見 Go issue #27505)。
- 子進程需要能響應信號(例如,如果它忽略 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)
// 父進程可以繼續(xù)其他工作,或直接退出測試
// cmd.Wait() // 可選
}
3.Windows 特定:使用 Job Object 自動終止
在 Windows 上,沒有直接等價于 Pdeathsig 的機制,但可以使用 Windows Job Object 將進程分組,并設置 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 標志:當 Job Object 的最后一個句柄關閉(父進程退出)時,自動殺死所有關聯(lián)進程。
注意:
- Go 標準庫不直接支持,需要使用
golang.org/x/sys/windows包調(diào)用 Windows API。 - 需要安裝依賴:
go get golang.org/x/sys/windows。 - 這會終止子進程及其后代。
代碼示例(簡化版,需要處理錯誤和清理):
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)
// 設置 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
}
// 將當前進程分配到 Job Object(子進程會繼承)
err = windows.AssignProcessToJobObject(job, windows.CurrentProcess())
if err != nil {
fmt.Println("AssignProcessToJobObject error:", err)
return
}
// 啟動子進程
cmd := exec.Command("timeout", "/t", "60") // Windows 等價于 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() // 可選
}
說明:子進程啟動后會自動關聯(lián) Job Object(因為父進程已關聯(lián))。父進程退出時,Job Object 關閉,子進程被終止。
其他注意
- 跨平臺實現(xiàn):可以使用條件編譯(
//go:build linux等)來區(qū)分平臺特定代碼。對于通用場景,優(yōu)先使用信號捕獲方式。 - 測試:在 Unix 上用
kill <pid>測試;在 Windows 上用 Task Manager 殺死父進程。 - 局限性:沒有方式能 100% 保證在所有情況下(如父進程崩潰)自動終止,尤其是跨平臺。如果子進程是 daemon 或忽略信號,可能需要額外處理。
到此這篇關于golang 設置進程退出時kill所有子進程的三種方法的文章就介紹到這了,更多相關Golang殺死子進程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言中同一個package中函數(shù)互相調(diào)用為undefined的解決
這篇文章主要介紹了Go語言中同一個package中函數(shù)互相調(diào)用為undefined的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
golang中拿slice當queue和拿list當queue使用分析
這篇文章主要為大家介紹了golang?中拿slice當queue和拿list當queue使用分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
搭建Go語言的ORM框架Gorm的具體步驟(從Java到go)
很多朋友不知道如何使用Goland軟件,搭建一個ORM框架GORM,今天小編給大家分享一篇教程關于搭建Go語言的ORM框架Gorm的具體步驟(從Java到go),感興趣的朋友跟隨小編一起學習下吧2022-09-09
Golang中使用Date進行日期格式化(沿用Java風格)
這篇文章主要介紹了Golang中使用Date進行日期格式化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04

