ファイルの悪意のあるコードをスキャンするスクリプトを改善しようとしています。ファイルには正規表現パターンのリストがあり、各行に 1 つのパターンがあります。現在の実装は基本的に bash スクリプトの find\grep コンボであるため、これらの正規表現は grep 用です。私のベンチマーク ディレクトリでは、bash スクリプトに 358 秒かかります。これを 72 秒で実行する Python スクリプトを作成できましたが、さらに改善したいと考えています。最初にベースコードを投稿し、次に試した微調整を投稿します。
import os, sys, Queue, threading, re
fileList = []
rootDir = sys.argv[1]
class Recurser(threading.Thread):
def __init__(self, queue, dir):
self.queue = queue
self.dir = dir
threading.Thread.__init__(self)
def run(self):
self.addToQueue(self.dir)
## HELPER FUNCTION FOR INTERNAL USE ONLY
def addToQueue(self, rootDir):
for root, subFolders, files in os.walk(rootDir):
for file in files:
self.queue.put(os.path.join(root,file))
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
self.queue.put(-1)
class Scanner(threading.Thread):
def __init__(self, queue, patterns):
self.queue = queue
self.patterns = patterns
threading.Thread.__init__(self)
def run(self):
nextFile = self.queue.get()
while nextFile is not -1:
#print "Trying " + nextFile
self.scanFile(nextFile)
nextFile = self.queue.get()
#HELPER FUNCTION FOR INTERNAL UES ONLY
def scanFile(self, file):
fp = open(file)
contents = fp.read()
i=0
#for patt in self.patterns:
if self.patterns.search(contents):
print "Match " + str(i) + " found in " + file
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
############MAIN MAIN MAIN MAIN##################
fileQueue = Queue.Queue()
#Get the shell scanner patterns
patterns = []
fPatt = open('/root/patterns')
giantRE = '('
for line in fPatt:
#patterns.append(re.compile(line.rstrip(), re.IGNORECASE))
giantRE = giantRE + line.rstrip() + '|'
giantRE = giantRE[:-1] + ')'
giantRE = re.compile(giantRE, re.IGNORECASE)
#start recursing the directories
recurser = Recurser(fileQueue,rootDir)
recurser.start()
print "starting scanner"
#start checking the files
for scanner in xrange(0,8):
scanner = Scanner(fileQueue, giantRE)
scanner.start()
これは明らかにデバッグ\醜いコードです。100 万の queue.put(-1) は気にしないでください。後でクリーンアップします。特にscanFileで、いくつかのインデントが正しく表示されません。
とにかく気づいたことをいくつか。1、4、さらには 8 スレッド (xrange(0,???) のスキャナー用:) を使用しても違いはありません。それでも72秒以内です。これはpythonのGILによるものだと思います。
巨大な正規表現を作成するのではなく、各行 (パターン) を compilex RE としてリストに配置し、scanfile 関数でこのリストを反復処理してみました。これにより、実行時間が長くなりました。
PythonのGILを回避するために、次のように各スレッドをgrepにフォークさせてみました:
#HELPER FUNCTION FOR INTERNAL UES ONLY
def scanFile(self, file):
s = subprocess.Popen(("grep", "-El", "--file=/root/patterns", file), stdout = subprocess.PIPE)
output = s.communicate()[0]
if output != '':
print 'Matchfound in ' + file
これにより、実行時間が長くなりました。
パフォーマンスを改善するための提案。
:::::::::::::編集::::::::
私自身の質問への回答はまだ投稿できませんが、提起されたいくつかの点に対する回答は次のとおりです。
@David Nehme - 人々に知らせるために、私は100万個のqueue.put(-1)を持っているという事実を認識しています
@Blender - キューの一番下をマークします。私のスキャナースレッドは、一番下にある-1に達するまでデキューを続けます(nextFileは-1ではありません:)。プロセッサ コアは 8 ですが、GIL が 1 スレッド、4 スレッド、または 8 スレッドを使用しているため、違いはありません。8 つのサブプロセスを生成すると、コードが大幅に遅くなりました (142 秒対 72 秒)
@ed-はい、それはfind\grepコンボと同じくらい遅く、実際には必要のないファイルを無差別にgrepするため遅くなります
@Ron - アップグレードできません。これはユニバーサルでなければなりません。これで 72 秒以上速くなると思いますか? bash grepper は 358 秒かかります。私の Python の巨大な RE メソッドは、1 ~ 8 スレッドで 72 秒かかります。8 スレッド (8 サブプロセス) の popen メソッドは 142 秒で実行されました。これまでのところ、巨大な RE python のみの方法が明らかに勝者です。
@intted
これが現在の find\grep コンボの要点です (私のスクリプトではありません)。とてもシンプルです。そこにはlsのような追加のものがありますが、5倍の速度低下を引き起こすものは何もありません. grep -r がわずかに効率的であっても、5x は非常に遅くなります。
find "${TARGET}" -type f -size "${SZLIMIT}" -exec grep -Eaq --file="${HOME}/patterns" "{}" \; -and -ls | tee -a "${HOME}/found.txt"
Python コードの方が効率的です。理由はわかりませんが、実験的にテストしました。私はPythonでこれを行うことを好みます。私はすでにpythonで5倍のスピードアップを達成しました.もっとスピードアップしたいと思います.
:::::::::::::勝者 勝者 勝者:::::::::::::::::
勝者がいるようです。
intued のシェル スクリプトは 34 秒で 2 位になりましたが、@steveha は 24 秒で 1 位になりました。多くのボックスに python2.6 がないため、cx_freeze を使用する必要がありました。tar を wget して解凍するためのシェル スクリプト ラッパーを作成できます。ただし、単純にするために intued が好きです。
助けてくれてありがとう、システム管理のための効率的なツールを手に入れました