如何可靠终止通过asyncssh启动的进程?
解决方案:避免SSH远程进程在父进程崩溃后残留
问题背景
你使用以下Python代码通过asyncssh启动了一个远程长期运行的进程,但当父进程因不可捕获信号(如SIGSEGV)或IDE调试崩溃时,远程进程会残留:
from asyncio import gather, run import asyncssh async def listen(stream): async for line in stream: print(line) async def main(): async with asyncssh.connect("localhost", username="root") as conn: process = await conn.create_process("while true; do date; sleep 1; done") await gather( listen(process.stdout), listen(process.stderr), process.wait(), ) run(main())
可行方案
1. 利用SSH连接存活检测机制
父进程崩溃后,SSH连接会陷入无响应状态,通过配置客户端心跳检测,让服务器在连接超时后主动关闭会话,进而终止远程进程。
修改asyncssh.connect的参数,添加心跳配置:
async def main(): # 每5秒发送一次心跳,连续3次未收到响应则断开连接 async with asyncssh.connect( "localhost", username="root", server_alive_interval=5, server_alive_count_max=3 ) as conn: process = await conn.create_process("while true; do date; sleep 1; done") await gather( listen(process.stdout), listen(process.stderr), process.wait(), )
原理:父进程崩溃后无法发送心跳包,服务器累计3次心跳失败后会主动断开SSH连接,远程前台进程会收到SIGHUP信号并终止。
2. 让远程进程直接绑定SSH会话
如果远程进程可能通过shell忽略SIGHUP,可以在命令前添加exec,让目标进程直接替换shell进程,确保会话关闭时进程被终止:
process = await conn.create_process("exec while true; do date; sleep 1; done")
添加exec后,不再有中间shell进程,SSH会话关闭时,目标进程会直接收到SIGHUP信号,无法通过shell的忽略机制逃避。
3. 绑定远程进程到TTY环境
若远程命令未关联TTY,可能不会收到SIGHUP,可以在创建进程时分配TTY:
process = await conn.create_process( "while true; do date; sleep 1; done", term_type='xterm', width=80, height=24 )
远程进程运行在TTY环境下,会话关闭时TTY断开,进程会收到SIGHUP信号终止。
4. 用shell陷阱终止进程组
如果远程系统存在进程忽略SIGHUP的情况,可以通过shell的EXIT陷阱,在会话退出时终止整个进程组:
process = await conn.create_process("trap 'kill -- -$$' EXIT; while true; do date; sleep 1; done")
当SSH会话关闭时,shell的EXIT陷阱触发,kill -- -$$会终止当前进程组下的所有进程。
内容的提问来源于stack exchange,提问作者frans
相关产品推荐
相关产品推荐

