Python多线程+subprocess模拟一个Shell

实现伪shell的目的

脚本功能:检测压测脚本状态,如果脚本死掉,能自动拉起运行,来达到长时间压测的目的;一个执行线程在永真里面做自动检测和拉起,一个控制线程在永真里面等待用户输入用于结束执行线程。用户可直接操作的就是控制线程,因为只提供了停止执行线程、查看内存状态、查看执行日志的功能,在压测运行起来以后,待在控制线程会很无聊,因此给其增加执行任意Linux命令的功能,执行后返回命令执行结果,增加用户可操作更多命令的乐趣

代码

if __name__=='__main__':
        runThread = threading.Thread(target=A)
        controllerThread = threading.Thread(target=B)
        runThread.start()
        controllerThread.start()
        controllerThread.join()
        runThread.join()
def A ():
    while True:
        #在该永真中实现压测脚本检测逻辑和自动拉起逻辑
def B ():
    while True:
        command = input('$ ')
        if command == 'stop':
            #停止执行线程中启的压测脚本
            sys.exit(0) #结束python进程
        elif command == 'xxx':
            #分支功能自定
        elif command == 'xxx':
             #分支功能自定
        else:
            runLinuxComand(command)

执行Linux命令这个地方,困扰了我两三天,在Python的这个永真控制线程去执行其他Linux命令,如果是ls这种能快速产生结果的命令,communicate能获取结果,执行不会出现问题,有问题的是

1、vi、top 这类的命令,指令没有结束,使用communicate获取指令执行结果会被阻塞住,程序卡在communicate这一行不再往下执行,如果使用CTRL + C强行退出,那控制线程也会被退出

2、tail -f 类命令,有输出,但指令一直在运行,使用communicate获取指令执行结果会被阻塞住,程序卡在communicate这一行不再往下执行,如果使用CTRL + C强行退出,那控制线程也会被退出

CTRL + C必然是不能使用的,因为控制线程已经定义了stop来较好正确的自我终结,使用CTRL + C会强行杀掉控制线程B和执行线程A,A执行的压测就没有暂停;为了防止用户误操作,因此在执行线程运行起来以后,我就执行stty intr undef 将CTRL 禁用,在stop时才执行stty intr ^c恢复

CTRL + C是肯定不能用的,那怎么解决communicate阻塞的问题呢?

尝试过使用定时器,在控制线程runLinuxComand之前设置定时器,如果用户执行了vi tail 等操作,必然被communicate阻塞住,三秒后定时器就运行 stop 函数,这个函数首先把Popen起的 vi tail 杀掉,然后再释放定时器,

timer = threading.Timer(3.0, stop)

timeerlist.append(timer)

timer.start()

runLinuxComand(command)

这个逻辑看起来没啥问题了,刚开始只是单纯的使用terminate来终结Popen起的命令,发现还是卡住,后面找了一堆大佬的资料,发现Popen起命令以后,会产生子子孙孙进程,terminate相当于只终结了子进程,而孙进程还在运行,因此还是卡在了communicate

大佬的资料说在Popen起进程的时候添加参数preexec_fn=os.setsid,就能将孙孙孙进程和子进程绑定,只要kill 了Popen子进程,其子子孙孙都会被干掉,我又来了希望,于是加上:

p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,preexec_fn=os.setsid)

在stop函数中再加上:

if command.pid != '':
    os.killpg(command.pid, signal.SIGKILL)
    command.terminate()

ok,运行 vi ,三秒过后stop跑完了没报什么错,但是我还是不能回到控制线程继续输入其他Linux指令,看样子,这破玩意还是卡在了communicate啊!!!破防了,子子孙孙都干掉了,怎么还会这样

继续疯狂找资料,直到点开了菜鸟教程:

。。。。WTF,这玩意自己就有超时机制,但是不知道它说的是什么超时,vi tail 是确实在运行的吧,且试它一试

output = p.communicate(timeout=3)[0].decode('utf-8')

ok,再来vi ,三秒后,它报了超时异常退出,6,居然有用,只要把这个异常捕获,这该死的逻辑就能继续往下走了,加上try,完结,待在控制线程的无聊,宣布结束:

运行函数:

def runLinuxComand(command):
    p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,preexec_fn=os.setsid)
    try:
        output = p.communicate(timeout=1)[0].decode('utf-8')
    except:
        output = ('非法指令')
        print(output)
        comandList.append(p)
        stop()
    else:
        print(output)

阻塞指令停止函数:

def stop():
    if comandList:
        for command in comandList:
            if command.pid != '':
                os.killpg(command.pid, signal.SIGKILL)
            command.terminate()
    comandList.clear()

 之所以叫伪shell,因为vi tail 这类会导致communicate阻塞的命令都进行超时处理掉了,即便如此,很多的Linux命令也能执行了,在我的控制线程B中,能像Linux一样执行一些简单命令了

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python的FFmpeg是一个功能强大的多媒体处理库,可以用于处理音频和视频文件。如果需要使用多线程进行FFmpeg下载,可以结合Python多线程库进行实现。 首先,我们可以使用Pythonsubprocess模块来调用FFmpeg命令,下载音频或视频文件。通过创建一个子进程来执行FFmpeg命令,我们可以利用多个子进程同时下载多个文件,实现多线程下载。 下面是一个简单的示例代码: ``` import subprocess import threading def download_file(url, output): command = f"ffmpeg -i {url} {output}" subprocess.call(command, shell=True) def multi_thread_download(urls, outputs): threads = [] for url, output in zip(urls, outputs): thread = threading.Thread(target=download_file, args=(url, output)) thread.start() threads.append(thread) for thread in threads: thread.join() # 下载列表中的两个文件 urls = ["http://example.com/file1.mp4", "http://example.com/file2.mp4"] outputs = ["file1.mp4", "file2.mp4"] multi_thread_download(urls, outputs) ``` 在上面的代码中,我们定义了两个函数,`download_file`用于下载单个文件,`multi_thread_download`用于多线程下载。 `download_file`函数接收一个URL和一个输出文件名作为参数,并使用FFmpeg命令将URL指定的文件下载到指定的输出位置。 `multi_thread_download`函数接收一个URL列表和一个输出文件名列表作为参数,然后使用`threading.Thread`类创建多个线程,并分别调用`download_file`函数下载对应的文件。 最后,我们调用`multi_thread_download`函数,传入需要下载的URL列表和输出文件名列表,即可实现多线程下载。 需要注意的是,使用多线程下载可能会增加系统资源的负担,如果下载的文件过多或文件过大,可能会导致系统负载过高或网络带宽受限。所以在实际应用中,需要根据具体需求和系统情况合理调整线程数目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小智机器人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值