ter*_*don 9 shell ssh terminal stdin
显然,如果同一个 shell 启动到同一个服务器的多个 ssh 连接,它们在执行给定的命令后不会返回,但会永远挂起 ( Stopped (tty input))。为了显示:
#!/bin/bash
ssh localhost sleep 2
echo "$$ DONE!"
Run Code Online (Sandbox Code Playgroud)
如果我在后台多次运行上面的脚本,它永远不会退出:
$ for i in {1..3}; do foo.sh & done
[1] 28695
[2] 28696
[3] 28697
$ ## Hit enter
[1] Stopped foo.sh
[2]- Stopped foo.sh
[3]+ Stopped foo.sh
$ ## Hit enter again
$ jobs -l
[1] 28695 Stopped (tty input) foo.sh
[2]- 28696 Stopped (tty input) foo.sh
[3]+ 28697 Stopped (tty input) foo.sh
Run Code Online (Sandbox Code Playgroud)
system()调用 launch时会发生相同的行为ssh。system(). 我试过了Net::SSH::Perl,Net:SSH2而且Net::OpenSSH。ssh 连接调试信息中没有明显有用的内容:
OpenSSH_7.5p1, OpenSSL 1.1.0f 25 May 2017
debug1: Reading configuration data /home/terdon/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug2: resolving "localhost" port 22
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to localhost [::1] port 22.
debug1: Connection established.
debug1: identity file /home/terdon/.ssh/id_rsa type 1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_7.5
debug1: Remote protocol version 2.0, remote software version OpenSSH_7.5
debug1: match: OpenSSH_7.5 pat OpenSSH* compat 0x04000000
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to localhost:22 as 'terdon'
debug3: hostkeys_foreach: reading file "/home/terdon/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/terdon/.ssh/known_hosts:47
debug3: load_hostkeys: loaded 1 keys from localhost
debug3: order_hostkeyalgs: prefer hostkeyalgs: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,ext-info-c
debug2: host key algorithms: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1
debug2: host key algorithms: ssh-rsa,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,zlib@openssh.com
debug2: compression stoc: none,zlib@openssh.com
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:uxhkh+gGPiCJQPaP024WXHth382h3BTs7QdGMokB9VM
debug3: hostkeys_foreach: reading file "/home/terdon/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/terdon/.ssh/known_hosts:47
debug3: load_hostkeys: loaded 1 keys from localhost
debug1: Host 'localhost' is known and matches the ECDSA host key.
debug1: Found key in /home/terdon/.ssh/known_hosts:47
debug3: send packet: type 21
debug2: set_newkeys: mode 1
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: set_newkeys: mode 0
debug1: rekey after 134217728 blocks
debug2: key: /home/terdon/.ssh/id_rsa (0x555a5e4b5060)
debug2: key: /home/terdon/.ssh/id_dsa ((nil))
debug2: key: /home/terdon/.ssh/id_ecdsa ((nil))
debug2: key: /home/terdon/.ssh/id_ed25519 ((nil))
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password
debug3: start over, passed a different list publickey,password
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/terdon/.ssh/id_rsa
debug3: send_pubkey_test
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: pkalg rsa-sha2-512 blen 279
debug2: input_userauth_pk_ok: fp SHA256:OGvtyUIFJw426w/FK/RvIhsykeP8kIEAtAeZwYBIzok
debug3: sign_and_send_pubkey: RSA SHA256:OGvtyUIFJw426w/FK/RvIhsykeP8kIEAtAeZwYBIzok
debug3: send packet: type 50
debug3: receive packet: type 52
debug1: Authentication succeeded (publickey).
Authenticated to localhost ([::1]:22).
debug2: fd 6 setting O_NONBLOCK
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Requesting no-more-sessions@openssh.com
debug3: send packet: type 80
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 80
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
debug3: receive packet: type 91
debug2: callback start
debug2: fd 3 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IPV6_TCLASS 0x08
debug2: client_session2_setup: id 0
debug1: Sending command: sleep 2
debug2: channel 0: request exec confirm 1
debug3: send packet: type 98
debug2: callback done
debug2: channel 0: open confirm rwindow 0 rmax 32768
debug2: channel 0: rcvd adjust 2097152
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: exec request accepted on channel 0
Run Code Online (Sandbox Code Playgroud)这不取决于我的~/.ssh/config设置。重命名文件不会改变任何东西。
sleep在虚拟示例中,但在现实生活中更复杂)成功退出并执行它应该做的事情。这不取决于您正在运行的命令,这是一个 ssh 问题。~/.bashrc没有区别。另外,我已经在运行 Ubuntu(默认登录 shell dash)和 Arch(默认登录 shell bash,称为 as sh)的机器上运行了它。这是怎么回事?这是 ssh 中的错误吗?我需要设置一个选项吗?如何从同一个 shell 启动通过 ssh 运行命令的脚本的多个实例?
要了解正在发生的事情,您需要对共享终端有所了解。当两个程序试图同时从同一个终端读取时会发生什么?每个输入字节随机进入其中一个程序。(不是在内核中使用 RNG 来决定随机,只是在实践中不可预测的随机。)当两个程序从管道或任何其他文件类型读取时,会发生同样的事情,这些文件类型是从一个地方移动的字节流到另一个(套接字,字符设备,...),而不是可以多次读取任何字节的字节数组(常规文件,块设备)。例如,在终端中运行一个 shell,找出终端的名称并运行cat.
$ tty
/dev/pts/18
$ cat
Run Code Online (Sandbox Code Playgroud)
然后从另一个终端运行cat /dev/pts/18. 现在在终端中输入,然后观察线路有时会转到一个cat进程,有时会转到另一个进程。当终端处于熟模式时,线路作为一个整体进行调度。如果您将终端置于原始模式,那么每个字节将被独立调度。
好乱啊 当然应该有一种机制来决定一个程序获得终端,而其他程序没有。嗯,有!它在典型情况下会触发,但不会在我上面设置的场景中触发。这种情况很不寻常,因为cat /dev/pts/18不是从/dev/pts/18. 从未在该终端内启动的程序访问终端是不寻常的。通常情况下,您在终端中运行 shell,然后从该 shell 运行程序。那么规则就是前台的程序拿到终端,后台的程序不拿到。这称为终端访问控制。它的工作方式是:
tcsetpgrp来让内核知道谁应该在前台。这在典型情况下有效。在 shell 中运行一个程序,该程序将成为前台进程。在后台运行程序(使用&),该程序不会在前台运行。当 shell 显示提示时,shell 会将自己置于前台。当您使用 恢复暂停的作业时fg,该作业将位于前台。随着bg,事实并非如此。
如果后台进程试图从终端读取,内核会向它发送一个 SIGTTIN 信号。信号的默认操作是挂起进程(如 SIGSTOP)。该过程可以通过调用知道这个的父waitpid与WSTOPPED标志; 当子进程接收到一个暂停它的信号时,waitpid父进程中的调用返回并让父进程知道信号是什么。这就是 shell 知道打印“Stopped (tty input)”的方式。它告诉您的是,由于 SIGTTIN,此作业已暂停。
由于进程被挂起,在它被恢复或终止之前不会发生任何事情(带有进程没有捕获的信号,因为如果进程设置了信号处理程序,它不会因为进程被挂起而运行)。您可以通过向它发送一个 SIGCONT 来恢复该进程,但是如果该进程正在从终端读取,则不会有任何效果,它会立即收到另一个 SIGTTIN。如果您使用 恢复进程fg,它会转到前台,因此读取成功。
现在您了解cat在后台运行时会发生什么:
$ cat &
$
[1] + Stopped (tty input) cat
$
Run Code Online (Sandbox Code Playgroud)
现在让我们用 SSH 做同样的事情。
$ ssh localhost sleep 999999 &
$
$
$
[1] + Stopped (tty input) ssh localhost sleep 999999
$
Run Code Online (Sandbox Code Playgroud)
按Enter有时会进入 shell(在前台),有时会进入 SSH 进程(此时它会被 SIGTTIN 停止)。为什么?如果ssh是从终端读取,它应该立即收到 SIGTTIN,如果不是,那么为什么它会收到 SIGTTIN?
发生的事情是 SSH 进程调用select系统调用以了解何时可以在它感兴趣的任何文件上使用输入(或者输出文件是否已准备好接收更多数据)。输入源至少包括终端和网络套接字。与read,select不禁止后台进程,并且ssh在调用 时不会收到 SIGTTIN select。目的select是找出数据是否可用,而不会中断任何事情。理想情况下select根本不会改变系统状态,但实际上这并不完全正确。当select告诉 SSH 进程输入在终端文件描述符上可用时,内核必须承诺在进程调用时发送输入read然后。(如果没有,并且进程调用了read,那么此时可能没有可用的输入,因此 from 的返回值select本来就是谎言。)因此,如果内核决定将某些输入路由到 SSH 进程,它会在select系统调用返回时做出决定。然后 SSH 调用read,此时内核看到后台进程试图从终端读取并使用 SIGTTIN 挂起它。
请注意,您不需要启动到同一服务器的多个连接。一个就够了。多个连接只会增加出现问题的可能性。
如果您需要从终端读取 SSH 会话,请在前台运行它。
如果您不需要从终端读取 SSH 会话,请确保其输入不是来自终端。有两种方法可以做到这一点:
您可以重定向输入:
ssh … </dev/null
Run Code Online (Sandbox Code Playgroud)您可以指示 SSH 不使用-n或转发终端连接-f。(-n相当于</dev/null;-f允许 SSH 本身从终端读取,例如读取密码,但命令本身不会打开终端。)
ssh -n …
Run Code Online (Sandbox Code Playgroud)请注意,终端和 SSH 之间的断开连接必须发生在客户端。sleep服务器上运行的进程永远不会从终端读取,但 SSH 无法知道这一点。如果客户端接收到标准输入的输入,它必须将其转发到服务器,这将使数据在缓冲区中可用,以防应用程序决定读取它(如果应用程序调用select,它会被告知数据是可用的)。
| 归档时间: |
|
| 查看次数: |
2386 次 |
| 最近记录: |