網(wǎng)站推廣服務(wù)合同模板2023年4月疫情恢復(fù)
前言
本人為了批量反編譯,不得不涉及到批量執(zhí)行,之前沒有徹底理解有關(guān)于多線程的概念和python方法,現(xiàn)在只能一步一步嘗試,并且實(shí)踐,寫本文以記錄。
1. 進(jìn)程與線程
1.1 什么是進(jìn)程?
1.1.1 概念
進(jìn)程是一個(gè)具有獨(dú)立功能的程序,它是數(shù)據(jù)集合運(yùn)行活動(dòng)的實(shí)體,重點(diǎn)在于運(yùn)行,只有運(yùn)行才有進(jìn)程的概念,這里我截取了我電腦中的任務(wù)管理器:
可以看到瀏覽器、Pycharm和任務(wù)管理器是我目前打開的應(yīng)用,是進(jìn)程。同樣,我右下角的微信及QQ等被稱之為后臺(tái)進(jìn)程,因?yàn)樗鼈円苍谶\(yùn)行。每個(gè)進(jìn)程都會(huì)有一個(gè)狀態(tài)碼,稱為PID,這個(gè)在學(xué)習(xí)linux的時(shí)候,經(jīng)常報(bào)錯(cuò),會(huì)用到kill PID
去殺死進(jìn)程。
1.1.2 三種狀態(tài)
Ready(就緒):當(dāng)進(jìn)程分配到除CPU以外的必要資源后,只要再獲得CPU,便可以立即執(zhí)行,進(jìn)程這時(shí)的狀態(tài)為就緒狀態(tài)。
Blocked(阻塞):正在執(zhí)行的進(jìn)程由于發(fā)生某事件或接受某消息無法繼續(xù)執(zhí)行時(shí),便放棄處理機(jī)而處于暫停狀態(tài),也即進(jìn)程的執(zhí)行收到阻塞,把這種暫停狀態(tài)稱為阻塞狀態(tài),有時(shí)也稱為等待狀態(tài)和封鎖狀態(tài)。
Running(運(yùn)行):進(jìn)程已獲得CPU,其程序正在執(zhí)行。
1.2 什么是線程?
線程是進(jìn)程中的 執(zhí)行運(yùn)算的最小單位,是進(jìn)程中的一個(gè)實(shí)體,依賴于線程,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源( 程序計(jì)數(shù)器,一組寄存器和棧),但它可與同屬一個(gè)進(jìn)程的其他線程 共享進(jìn)程所擁有的全部資源。
1.3 多線程的優(yōu)點(diǎn)
-
通過線程可方便有效地實(shí)現(xiàn)并發(fā)性。進(jìn)程可創(chuàng)建多個(gè)線程來執(zhí)行同一個(gè)程序的不同部分。
-
創(chuàng)建線程比創(chuàng)建進(jìn)程要快,所需開銷少,占用的資源也少。
-
通過創(chuàng)建多線程進(jìn)程,每個(gè)線程在一個(gè)處理器上運(yùn)行,從而實(shí)現(xiàn)應(yīng)用程序的并發(fā)性,使每個(gè)處理器都得到充分的運(yùn)行。
參考文獻(xiàn):操作系統(tǒng):進(jìn)程與線程之間的區(qū)別及聯(lián)系
2. Python多線程學(xué)習(xí)
以下為菜鳥教程上的實(shí)例:
import thread
import time# 為線程定義一個(gè)函數(shù)
def print_time( threadName, delay):count = 0while count < 5:time.sleep(delay)count += 1print "%s: %s" % ( threadName, time.ctime(time.time()) )# 創(chuàng)建兩個(gè)線程
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
輸出結(jié)果如下:
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
根據(jù)輸出結(jié)果,可以看出線程也是有先后順序的,是隊(duì)列性質(zhì),先執(zhí)行第一個(gè)print_time(Thread-1),然后第一個(gè)持續(xù)執(zhí)行,沒有停止,然后看到第一個(gè)線程和第二個(gè)線程在是在并行執(zhí)行,在這里為什么不說并發(fā),我查閱了進(jìn)程、線程、多線程、并發(fā)、并行 詳解后并與之結(jié)果比較發(fā)現(xiàn),這兩個(gè)線程是在同步運(yùn)行的。至于多線程實(shí)現(xiàn)的是并發(fā)還是并行?所寫多線程可能被分配到一個(gè)CPU內(nèi)核中執(zhí)行,也可能被分配到不同CPU執(zhí)行,分配過程是操作系統(tǒng)所為,不可人為控制。所以多線程是并發(fā)還是并行的?都有可能。
3. 實(shí)踐操作
最開始我的想法是如果一個(gè)一個(gè)執(zhí)行cmd命令,導(dǎo)致時(shí)間太長(zhǎng),于是網(wǎng)上搜索,有很多種方法,例如os,subprocess等方法,這樣都是產(chǎn)生的子進(jìn)程,而不是多線程。我們知道啟動(dòng)一個(gè)線程所花費(fèi)的空間遠(yuǎn)遠(yuǎn)小于啟動(dòng)一個(gè)進(jìn)程所花費(fèi)的空間,但我還是試了一下,我用的是for循環(huán)嵌套,并沒有用線程啟動(dòng),結(jié)果發(fā)現(xiàn)他是隊(duì)列執(zhí)行,順序執(zhí)行的,也就是說要等上一個(gè)cmd命令運(yùn)行結(jié)束后才會(huì)進(jìn)行下一個(gè)。
subprocess.Popen(cmd_str, shell=True, stdout=None, stderr=None).wait()
結(jié)果可想而知,反編譯速度是非常的慢。
而后,我查閱了多線程執(zhí)行的方法,【Python】并行運(yùn)行多個(gè)cmd命令
# 是否需要并行運(yùn)行if_parallel = True# 需要執(zhí)行的命令列表# 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)
在我將并行數(shù)調(diào)到了5個(gè)開始,內(nèi)存量一下就升上來了,當(dāng)我調(diào)到更多時(shí),內(nèi)存幾乎占滿,而且我不清楚如何停止這個(gè)線程,即便我關(guān)閉了編譯器,這些代碼依然在運(yùn)行中,所以我又在想到底這個(gè)是否開啟了多線程,這些線程又該如何關(guān)閉呢。
帶著上述疑問,我將cmd并行數(shù)跳到了15個(gè),打開資源監(jiān)視器:
可以看到這樣的方式實(shí)際上是打開了多進(jìn)程,我在任務(wù)管理器中,猜想也得到了驗(yàn)證。
這樣看來,慢是可以理解的,為什么呢,看以下這張圖:
真相清楚了,就是因?yàn)閏md命令調(diào)用了jar包,因此產(chǎn)生了較多內(nèi)存去處理,當(dāng)我要處理900多個(gè)文件時(shí),他當(dāng)然溢出了,汗死,只能選擇分批量處理,因?yàn)殡娔X運(yùn)行內(nèi)存是有限的。
最后,展示一下完整代碼把:
import datetime
import os
import threadingdef execCmd(cmd):try:print("命令%s開始運(yùn)行%s" % (cmd, datetime.datetime.now()))subprocess.Popen(cmd, shell=True, stdout=None, stderr=None).wait()print("命令%s結(jié)束運(yùn)行%s" % (cmd, datetime.datetime.now()))except:print('%s\t 運(yùn)行失敗' % (cmd))if __name__ == '__main__':# 是否需要并行運(yùn)行if_parallel = True# 需要執(zhí)行的命令列表# 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)# 等待線程運(yùn)行完畢for th in threads:# .join的作用:現(xiàn)在有 A, B, C 三件事情,只有做完 A 和 B 才能去做 C,而 A 和 B 可以并行完成。th.join()# 確保 A 完成print("OK!!!!!!!!!!!")else:# 串行for cmd in cmds:try:print("命令%s開始運(yùn)行%s" % (cmd, datetime.datetime.now()))os.system(cmd)print("命令%s結(jié)束運(yùn)行%s" % (cmd, datetime.datetime.now()))except:print('%s\t 運(yùn)行失敗' % (cmd))
結(jié)論與思考
批量執(zhí)行cmd實(shí)際上就是說多進(jìn)程執(zhí)行,而不是多線程,雖然每個(gè)cmd占用的內(nèi)存不高,但是需要結(jié)合執(zhí)行命令是否牽扯到其他進(jìn)程,若其他進(jìn)程占用內(nèi)存較高,也是無法并行或并發(fā)的。
此外,我在想是否可以在.bat中修改代碼,使得一個(gè)cmd窗口就可以批量并行執(zhí)行反編譯命令呢,這個(gè)有待考量,短期內(nèi)以我的代碼水平,我大概是想不出來了。感覺很多還是不理解,希望有大佬能夠批評(píng)指正。