pexpect多线程程序在生成shell时会挂起

时间:2016-08-16 20:46:02

标签: python multithreading fork pexpect

我有一个多线程程序。程序运行时,有些线程在调用pexpect.spawn()时有时会挂起。

此程序在每个线程中创建一个Session对象,Session对象在创建时会产生一个pexpect会话。在程序中,每个Session对象都绑定到一个特定的线程。

class CustomizedThread(threading.Thread):
    __init__(self, thread_name):
        super().__init__(name=thread_name)

    def run(self):
        session = Session()
        ...

class Session:
    __init__(self, session_name):
        self.name = session_name
        print('Thread {} is spawning a shell in session {}.'.format(
            threading.currentThread(), session.name))
        self.pexpect_session = pexpect.spawn('/bin/sh')
        print('Thread {} finished spawning a shell in session {}.'.format(
            threading.currentThread(), session.name))
        self.pexpect_session.sendline('ssh MACHINE_NAME')
        ...

    __del__(self):
        print('Thread {} is cleaning up session {}.'.format(
            threading.currentThread(), session.name))
        self.pexepect_session.close(force=True)

以下是线程2挂起时的示例输出,在线程2上调用pexpect.spawn()期间触发了Session对象1的析构函数。

...
Thread 1 is spawning a shell in session 1.
Thread 1 finished spawning a shell in session 1.
Thread 2 is spawning a shell in session 2.
Thread 2 is cleaning up session 1.

将挂起进程附加到gdb,我得到了以下堆栈跟踪。它显示在尝试将异常消息写入文件描述符时线程挂起:

(gdb) where
#0  0x00007fff9628391a in write () from /usr/lib/system/libsystem_kernel.dylib
#1  0x000000010ed7aa22 in _Py_write_impl (fd=2, buf=0x10f3f1010, count=76, gil_held=1) at ../Python/fileutils.c:1269
#2  0x000000010ed7a9a1 in _Py_write (fd=2, buf=0x10f3f1010, count=76) at ../Python/fileutils.c:1327
#3  0x000000010ede8795 in _io_FileIO_write_impl (self=0x10f3875f8, b=0x7000013dd168) at ../Modules/_io/fileio.c:840
#4  0x000000010ede7957 in _io_FileIO_write (self=0x10f3875f8, arg=0x11312c148)
at ../Modules/_io/clinic/fileio.c.h:245
#5  0x000000010ebbfd72 in PyCFunction_Call (func=0x112ed7b98, args=0x112fc6330, kwds=0x0)
at ../Objects/methodobject.c:134
#6  0x000000010eb2803d in PyObject_Call (func=0x112ed7b98, arg=0x112fc6330, kw=0x0) at ../Objects/abstract.c:2165
#7  0x000000010eb290de in PyObject_CallMethodObjArgs (callable=0x112ed7b98, name=0x10f234d40)
at ../Objects/abstract.c:2394
#8  0x000000010edf0456 in _bufferedwriter_raw_write (self=0x10f25de58,
start=0x10f3f1010 "\nThread 2 is cleaning up session 1. \"terminated\" is 0, but there was no child process. Did someone else call waitpid() on our process?\n"...,
len=76) at ../Modules/_io/bufferedio.c:1847

异常消息“已终止”为0,但没有子进程。有人在我们的流程上调用了waitpid()吗?来自pexpect会话关闭的行

self.pexepect_session.close(force=True)

此外,在pexpect的spawn()方法中,进程被分叉(我在gdb中附加到的进程)以执行'/ bin / sh'并创建管道以将任何异常消息写入父进程

看起来分叉进程垃圾收集了另一个线程的Session对象,但在尝试关闭另一个线程上的会话时遇到异常。该过程正在挂起将异常消息写入管道,因为异常消息应该从另一端读取。

2 个答案:

答案 0 :(得分:0)

使用https://jsfiddle.net/5nx6u18n/而不是使用线程来生成shell和pexpect来运行ssh。这是一个非常好的,成熟的库,它实现了ssh而不必外壳。你可以在一个线程中运行多个会话,无论如何都可能在Python中更有效 - 见这里:Paramiko

答案 1 :(得分:0)

可以通过切换到多处理来解决此问题。混合多线程和fork是有问题的。