前言
本人为了批量反编译,不得不涉及到批量执行,之前没有彻底理解有关于多线程的概念和python方法,现在只能一步一步尝试,并且实践,写本文以记录。
1. 进程与线程
1.1 什么是进程?
1.1.1 概念
进程是一个具有独立功能的程序,它是数据集合运行活动的实体,重点在于运行,只有运行才有进程的概念,这里我截取了我电脑中的任务管理器:
可以看到浏览器、Pycharm和任务管理器是我目前打开的应用,是进程。同样,我右下角的微信及QQ等被称之为后台进程,因为它们也在运行。每个进程都会有一个状态码,称为PID,这个在学习linux的时候,经常报错,会用到kill PID
去杀死进程。
1.1.2 三种状态
Ready(就绪):当进程分配到除CPU以外的必要资源后,只要再获得CPU,便可以立即执行,进程这时的状态为就绪状态。
Blocked(阻塞):正在执行的进程由于发生某事件或接受某消息无法继续执行时,便放弃处理机而处于暂停状态,也即进程的执行收到阻塞,把这种暂停状态称为阻塞状态,有时也称为等待状态和封锁状态。
Running(运行):进程已获得CPU,其程序正在执行。
1.2 什么是线程?
线程是进程中的 执行运算的最小单位,是进程中的一个实体,依赖于线程,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源( 程序计数器,一组寄存器和栈),但它可与同属一个进程的其他线程 共享进程所拥有的全部资源。
1.3 多线程的优点
-
通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一个程序的不同部分。
-
创建线程比创建进程要快,所需开销少,占用的资源也少。
-
通过创建多线程进程,每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分的运行。
参考文献:操作系统:进程与线程之间的区别及联系
2. Python多线程学习
以下为菜鸟教程上的实例:
import thread
import time
# 为线程定义一个函数
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print "%s: %s" % ( threadName, time.ctime(time.time()) )
# 创建两个线程
try:
thread.start_new_thread( print_time, ("Thread-1", 2, ) )
thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print "Error: unable to start thread"
while 1:
pass
输出结果如下:
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
根据输出结果,可以看出线程也是有先后顺序的,是队列性质,先执行第一个print_time(Thread-1),然后第一个持续执行,没有停止,然后看到第一个线程和第二个线程在是在并行执行,在这里为什么不说并发,我查阅了进程、线程、多线程、并发、并行 详解后并与之结果比较发现,这两个线程是在同步运行的。至于多线程实现的是并发还是并行?所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所以多线程是并发还是并行的?都有可能。
3. 实践操作
最开始我的想法是如果一个一个执行cmd命令,导致时间太长,于是网上搜索,有很多种方法,例如os,subprocess等方法,这样都是产生的子进程,而不是多线程。我们知道启动一个线程所花费的空间远远小于启动一个进程所花费的空间,但我还是试了一下,我用的是for循环嵌套,并没有用线程启动,结果发现他是队列执行,顺序执行的,也就是说要等上一个cmd命令运行结束后才会进行下一个。
subprocess.Popen(cmd_str, shell=True, stdout=None, stderr=None).wait()
结果可想而知,反编译速度是非常的慢。
而后,我查阅了多线程执行的方法,【Python】并行运行多个cmd命令
# 是否需要并行运行
if_parallel = True
# 需要执行的命令列表
# model_list = ['yolo', 'centernet']
# cmds = ['python main.py --model ' + i for i in model_list]
cmds = ["F: & cd F:\\benign_apk & " + "apktool.bat d -f " + "benign" + str(i) + ".apk" for i in range(65,70)]
if if_parallel:
# 并行
threads = []
for cmd in cmds:
th = threading.Thread(target=execCmd, args=(cmd,))
th.start()
threads.append(th)
在我将并行数调到了5个开始,内存量一下就升上来了,当我调到更多时,内存几乎占满,而且我不清楚如何停止这个线程,即便我关闭了编译器,这些代码依然在运行中,所以我又在想到底这个是否开启了多线程,这些线程又该如何关闭呢。
带着上述疑问,我将cmd并行数跳到了15个,打开资源监视器:
可以看到这样的方式实际上是打开了多进程,我在任务管理器中,猜想也得到了验证。
这样看来,慢是可以理解的,为什么呢,看以下这张图:
真相清楚了,就是因为cmd命令调用了jar包,因此产生了较多内存去处理,当我要处理900多个文件时,他当然溢出了,汗死,只能选择分批量处理,因为电脑运行内存是有限的。
最后,展示一下完整代码把:
import datetime
import os
import threading
def execCmd(cmd):
try:
print("命令%s开始运行%s" % (cmd, datetime.datetime.now()))
subprocess.Popen(cmd, shell=True, stdout=None, stderr=None).wait()
print("命令%s结束运行%s" % (cmd, datetime.datetime.now()))
except:
print('%s\t 运行失败' % (cmd))
if __name__ == '__main__':
# 是否需要并行运行
if_parallel = True
# 需要执行的命令列表
# model_list = ['yolo', 'centernet']
# cmds = ['python main.py --model ' + i for i in model_list]
cmds = ["F: & cd F:\\benign_apk & " + "apktool.bat d -f " + "benign" + str(i) + ".apk" for i in range(70,85)]
if if_parallel:
# 并行
threads = []
for cmd in cmds:
th = threading.Thread(target=execCmd, args=(cmd,))
th.start()
threads.append(th)
# 等待线程运行完毕
for th in threads:
# .join的作用:现在有 A, B, C 三件事情,只有做完 A 和 B 才能去做 C,而 A 和 B 可以并行完成。
th.join()
# 确保 A 完成
print("OK!!!!!!!!!!!")
else:
# 串行
for cmd in cmds:
try:
print("命令%s开始运行%s" % (cmd, datetime.datetime.now()))
os.system(cmd)
print("命令%s结束运行%s" % (cmd, datetime.datetime.now()))
except:
print('%s\t 运行失败' % (cmd))
结论与思考
批量执行cmd实际上就是说多进程执行,而不是多线程,虽然每个cmd占用的内存不高,但是需要结合执行命令是否牵扯到其他进程,若其他进程占用内存较高,也是无法并行或并发的。
此外,我在想是否可以在.bat中修改代码,使得一个cmd窗口就可以批量并行执行反编译命令呢,这个有待考量,短期内以我的代码水平,我大概是想不出来了。感觉很多还是不理解,希望有大佬能够批评指正。