关于golang使用exec.Command的一些踩坑 —
背景: 在golang主进程中调用python脚本,使用exec.CommandContext, 希望在golang中断子进程时,python能通过捕获信号量做一些操作
但是在查看golang exec包源码后发现无法实现,因为中断子进程的方式为SIGKILL这个信号量,即内核层面进行进程中断,并不是告诉子进程你需要中断了,这个跟直接kill -9 pid是一个效果,而在程序中是无法捕获这个信号量的
接下来看一些系统调用的关键代码
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "sleep", "60")
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("err: %v", err.Error())
}
log.Printf("output: %v", output)
select {
case <-ctx.Done():
log.Panicln("timeout")
}
context部分,申请了一个六十秒超时的ctx,内部会有一个计数器,在超时后会关闭 Done
这个chan
这是这个context的内部实现,其中Done就是一个没有内容的chan,纯用作关闭通知
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
而这是cancel函数的内部实现,可以看到有两个关键操作,一是close掉Done,而是将子节点全部关闭,因为context是一个树的结构,上下文之间可以传递。
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
而超时之后内部会自动调用cancel函数,所以我们可以通过监听ctx.Done这个chan来做一些超时处理。
以上是context部分介绍,接下来说下command的内部实现
由于我希望能够手动关闭系统调用,并且希望在系统调用能够感知这个关闭并做一些操作,所以我在调用的python进程中编写了signal.signal(signal.SIGINT, func)的监听回调,可是在我手动关闭系统调用后py部分并未如我预期接收到该信号,于是对CommandContext的实现进行了一番学习
在Command.Start()中有这么一段实现, 这个ctx就是我们传进去的ctx,即前文所说的超时context。
if c.ctx != nil {
c.waitDone = make(chan struct{})
go func() {
select {
case <-c.ctx.Done():
c.Process.Kill()
case <-c.waitDone:
}
}()
}
而这个Kill函数,是linux的信号量9,即内核层面的杀死进程,所以我预想的操作是无法实现的,相应的处理只能交于上层来进行处理
Kill Signal = syscall.SIGKILL
func (p *Process) kill() error {
return p.Signal(Kill)
}
文档信息
- 本文作者:Mikatsuki
- 本文链接:https://akitozz.github.io/2022/03/29/golang-1/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)