首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >强制关闭SSH客户端会话

强制关闭SSH客户端会话
EN

Stack Overflow用户
提问于 2016-05-05 07:59:27
回答 1查看 2.1K关注 0票数 10

我已经编写了一个SSH客户端来连接到网络设备,一旦运行命令超过25秒,我就通过"select“设置超时。我注意到有一些设备有另一个IOS,一旦超时被触发,它就不能通过Close()方法丢弃SSH会话,这会导致goroutinge泄漏。我需要保持客户端状态并断开会话,以便为下一条命令做好准备。看起来goroutine在那个时候不会永远终止!你们有什么想法吗?

代码语言:javascript
复制
    go func() {
       r <- s.Run(cmd)
    }()

    select {
       case err := <-r:
         return err
       case <-time.After(time.Duration(timeout) * time.Second):
         s.Close()
         return fmt.Errorf("timeout after %d seconds", timeout)
    }

通过堆分析,我看到了以下内容:2.77 saw 99.44% 99.44% 2.77 saw 99.44% bytes.makeSlice

代码语言:javascript
复制
     0     0% 99.44%     2.77GB 99.44%  bytes.(*Buffer).ReadFrom

     0     0% 99.44%     2.77GB 99.44%  golang.org/x/crypto/ssh.(*Session).start.func1

     0     0% 99.44%     2.77GB 99.44%  golang.org/x/crypto/ssh.(*Session).stdout.func1

     0     0% 99.44%     2.77GB 99.44%  io.Copy

     0     0% 99.44%     2.77GB 99.44%  io.copyBuffer

     0     0% 99.44%     2.78GB 99.93%  runtime.goexit

在/usr/ ======================== /go/src/ runtime.goexit /asm_amd64.s中的例程运行时

代码语言:javascript
复制
     0     2.78GB (flat, cum) 99.93% of Total

     .          .   1993:   RET

     .          .   1994:

     .          .   1995:// The top-most function running on a goroutine

     .          .   1996:// returns to goexit+PCQuantum.

     .          .   1997:TEXT runtime·goexit(SB),NOSPLIT,$0-0

     .     2.78GB   1998:   BYTE    $0x90   // NOP

     .          .   1999:   CALL    runtime·goexit1(SB) // does not return

     .          .   2000:   // traceback from goexit1 must hit code range of goexit

     .          .   2001:   BYTE    $0x90   // NOP

     .          .   2002:


     .          .   2003:TEXT runtime·prefetcht0(SB),NOSPLIT,$0-8
EN

回答 1

Stack Overflow用户

发布于 2020-02-15 22:37:29

通道r阻止Go例程返回,因为它没有被清空。我已经编写了您的代码的改编版本,并插入了一个等待组来演示这个问题:

代码语言:javascript
复制
func main() {
    var wg sync.WaitGroup // This is only added for demonstration purposes
    s := new(clientSession)

    r := make(chan error)

    go func(s *clientSession) {
        wg.Add(1)
        r <- s.Run()
        wg.Done() // Will only be called after s.Run() is able to return
    }(s)

    fmt.Println("Client has been opened")

    select {
    case err := <-r:
        fmt.Println(err)
    case <-time.After(1 * time.Second):
        s.Close()
        fmt.Println("Timed out, closing")
    }

    wg.Wait() // Waits until wg.Done() is called.

    fmt.Println("Main finished successfully")
}

Go playground似乎会终止程序,所以我用完整的可运行代码创建了一个gist。当我们运行incorrect.go

代码语言:javascript
复制
$ go run incorrect.go
Client has been opened
Timed out, closing
fatal error: all goroutines are asleep - deadlock!
....

这是因为我们的代码在wg.Wait()行上死锁了。这表明Go例程中的wg.Done()从未达到。

正如评论指出的那样,缓冲通道在这方面可以有所帮助。但前提是您在调用s.Close()之后不再关心错误

代码语言:javascript
复制
r := make(chan error, 1)

buffered.go运行正常,但错误丢失:

代码语言:javascript
复制
$ go run buffered.go
Client has been opened
Timed out, closing
Main finished successfully

另一种选择是将通道正好排出一次:

代码语言:javascript
复制
select {
    case err := <-r:
        fmt.Println(err)
    case <-time.After(1 * time.Second):
        s.Close()
        fmt.Println("Timed out, closing")
        fmt.Println(<-r)
    }

或者通过将select包装在for循环中(无缓冲通道):

代码语言:javascript
复制
X:
    for {
        select {
        case err := <-r:
            fmt.Println(err)
            break X // because we are in main(). Normally `return err`
        case <-time.After(1 * time.Second):
            s.Close()
            fmt.Println("Timed out, closing")
        }
    }

当我们运行drain.go时,我们看到错误也被打印出来:

代码语言:javascript
复制
$ go run incorrect.go
Client has been opened
Timed out, closing
Run() closed
Main finished successfully

在现实世界中,人们可能会运行多个围棋例程。因此,您将希望在for循环上使用一些计数器,或者进一步利用等待组功能。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37040099

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档