Pty 没有被关闭,althoguh close() 系统调用被调用



我正在实现ssh服务器的特性,因此给定shell请求,我打开一个ty-tty对。
代码片段:

import (
"github.com/creack/pty"
...
)
func attachPty(channel ssh.Channel, shell *exec.Cmd) {
mypty, err := pty.Start(shell)
go func() {
io.Copy(channel, mypty) // (1) ; could also be substituted with read() syscall, same problem
}
go func() {
io.Copy(mypty, channel) // (2) - this returns on channel exit with eof, so let's close mypty
if err := syscall.Close(int(mypty.Fd())); err != nil {
fmt.Printf("error closing fd") // no error is printed out, /proc/fd shows it's successfuly closed
}
}
}

一旦ssh通道关闭,我就关闭该通道。我期望的行为是它应该发送SIGHUP到shell。

如果我注释掉(1)副本(src: myty, dst: channel),它就可以工作了!
然而-当它没有被注释掉时:

  • (1)副本不返回,意味着myptyread系统调用仍然阻塞,并且不返回eof =>主设备没有关闭?
  • shell没有得到SIGHUP

我不知道为什么如果我注释出(1)拷贝它的工作,也许内核参考计数读取?

我的领导:

  • 企业。Read实际上被分派给tty,如下所示:主机缺少读功能
  • SIGHUP流程演练
  • drivers/tty/pty.c中的pty_close,它调用tty_vhangup(tty->link);,见这里
  • Linux设备驱动程序,第三版,PTY章节

指出:

  • 我直接关闭fd,因为否则使用通常的os.File.close()不会因为某种原因关闭fd,它在/proc/<pid>/fd

    中保持打开状态
  • 用直接的read系统调用替换(1)副本将导致相同的结果

谢谢!

我自己也遇到了这个问题,并花了一些时间进行调查。

证明这是Go运行时的故意行为。具体来说,它可以防止文件在被另一个程序读取时被关闭。这是为了缓解一种竞争条件,即如果文件描述符被关闭并被另一个运行例程重用,则运行例程可能从错误的文件中读取。

对于SSHD用例,我没有一个好的解决方案。稍后我可能会更新这个答案。你有自己的解决办法吗?

最新更新