Go语言内置了丰富的标准库用于进程管理和信号处理,开发者可以通过相关API完成子进程创建、状态监控、信号捕获等操作,满足各类系统级开发需求。

Go语言进程管理基础
Go语言的os/exec包提供了进程管理相关的核心功能,通过exec.Command可以创建子进程实例,后续可以对子进程进行启动、等待、终止等操作。
创建并启动子进程
使用exec.Command指定要执行的命令和参数,调用Start方法即可启动子进程,示例代码如下:
package main
import (
"fmt"
"os/exec"
)
func main() {
// 创建执行ls命令的子进程实例,参数为-l
cmd := exec.Command("ls", "-l")
// 启动子进程
err := cmd.Start()
if err != nil {
fmt.Println("启动子进程失败:", err)
return
}
fmt.Println("子进程PID:", cmd.Process.Pid)
}
等待子进程结束并获取结果
如果需要获取子进程的输出或者等待子进程执行完成,可以调用Wait方法或者CombinedOutput方法,CombinedOutput会返回子进程的标准输出和标准错误的合并结果。
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("echo", "Hello Go Process")
// 获取子进程的输出结果
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("执行命令失败:", err)
return
}
fmt.Println("命令输出:", string(output))
}
终止子进程
可以通过Process.Kill方法强制终止子进程,或者通过Process.Signal方法向子进程发送指定信号,示例代码如下:
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
cmd := exec.Command("sleep", "10")
err := cmd.Start()
if err != nil {
fmt.Println("启动子进程失败:", err)
return
}
// 等待2秒后终止子进程
time.Sleep(2 * time.Second)
err = cmd.Process.Kill()
if err != nil {
fmt.Println("终止子进程失败:", err)
return
}
fmt.Println("子进程已被终止")
// 等待子进程资源释放
cmd.Wait()
}
Go语言信号处理实现
Go语言的os/signal包提供了信号捕获相关的功能,结合os.Notify方法可以将指定信号发送到channel中,开发者可以在goroutine中监听channel来处理信号。
常见系统信号说明
不同系统支持的信号略有差异,以下是Linux系统下常见的几个信号:
| 信号名称 | 信号值 | 含义 |
|---|---|---|
| SIGINT | 2 | 用户按下Ctrl+C触发的中断信号 |
| SIGTERM | 15 | 程序终止信号,kill命令默认发送该信号 |
| SIGKILL | 9 | 强制终止信号,无法被捕获或忽略 |
捕获并处理信号
以下示例展示了如何捕获SIGINT和SIGTERM信号,并在接收到信号后执行优雅关闭逻辑:
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
// 创建信号接收channel
sigChan := make(chan os.Signal, 1)
// 注册要监听的信号,SIGINT和SIGTERM
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
fmt.Println("程序启动,等待信号...")
// 启动goroutine监听信号
go func() {
sig := <-sigChan
fmt.Println("n接收到信号:", sig)
fmt.Println("开始执行优雅关闭流程...")
// 模拟资源释放操作
time.Sleep(1 * time.Second)
fmt.Println("资源释放完成,程序退出")
os.Exit(0)
}()
// 主goroutine阻塞,避免程序退出
select {}
}
进程管理与信号处理结合实战
在实际的服务开发中,通常需要同时管理子进程和处理系统信号,比如在接收到终止信号时,先通知子进程退出,再释放自身资源。以下是一个完整的实战示例:
package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
)
func main() {
// 启动子进程,模拟业务服务
cmd := exec.Command("sleep", "30")
err := cmd.Start()
if err != nil {
fmt.Println("启动子进程失败:", err)
return
}
fmt.Println("子进程启动,PID:", cmd.Process.Pid)
// 创建信号监听channel
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 监听信号
go func() {
sig := <-sigChan
fmt.Println("n接收到信号:", sig)
fmt.Println("开始优雅关闭...")
// 向子进程发送终止信号
err := cmd.Process.Signal(syscall.SIGTERM)
if err != nil {
fmt.Println("发送信号给子进程失败:", err)
// 如果发送失败,强制终止子进程
cmd.Process.Kill()
}
// 等待子进程退出
cmd.Wait()
fmt.Println("子进程已退出")
// 模拟自身资源释放
time.Sleep(1 * time.Second)
fmt.Println("主程序资源释放完成,退出")
os.Exit(0)
}()
// 主goroutine阻塞
select {}
}
注意事项
- SIGKILL信号无法被捕获,因此不能通过该信号实现优雅关闭逻辑
- 信号channel建议设置缓冲,避免信号发送时阻塞
- 子进程的资源需要及时通过
Wait方法释放,避免产生僵尸进程 - 不同操作系统的信号支持存在差异,跨平台开发时需要注意兼容性