我正在尝试改进扫描文件以查找恶意代码的脚本。我们在一个文件中有一个正则表达式模式列表,每行一个模式。这些正则表达式用于 grep,因为我们当前的实现基本上是一个 bash 脚本 find\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()
这显然是调试\丑陋的代码,千万不要介意 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 - 只是为了让人们知道我知道我有一百万个 queue.put(-1) 的事实
@Blender - 标记队列的底部。我的扫描仪线程一直在出队,直到它们到达底部的 -1(而 nextFile 不是 -1:)。处理器内核为 8,但由于 GIL 使用 1 个线程、4 个线程或 8 个线程并没有区别。产生 8 个子进程导致代码显着变慢(142 秒对 72 秒)
@ed - 是的,它和 find\grep 组合一样慢,实际上更慢,因为它不加选择地 greps 不需要的文件
@Ron - 无法升级,这必须是通用的。你认为这会加速 > 72 秒吗?bash grepper 执行 358 秒。我的 python 巨型 RE 方法使用 1-8 个线程执行 72 秒。包含 8 个线程(8 个子进程)的 popen 方法运行时间为 142 秒。到目前为止,巨大的 RE python only 方法显然是赢家
@intuted
这是我们当前 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 倍的加速,我想让它加速。
:::::::::::::WINNER WINNER WINNER::::::::::::::::::
看起来我们有一个赢家。
intued 的 shell 脚本以 34 秒排名第二,但 @steveha 以 24 秒排名第一。由于我们的很多盒子没有python2.6,我不得不cx_freeze它。我可以编写一个 shell 脚本包装器来获取 tar 并解压缩它。不过,为了简单起见,我确实喜欢 intued。
谢谢大家的帮助,我现在有了一个高效的系统管理工具