实现伪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一样执行一些简单命令了