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