Я пытаюсь улучшить скрипт, который сканирует файлы на наличие вредоносного кода.У нас есть список шаблонов регулярных выражений в файле, по одному шаблону в каждой строке.Это регулярное выражение для grep, так как наша текущая реализация - это в основном bash-скрипт find \ grep combo.Скрипт bash занимает 358 секунд в моем каталоге тестов.Я смог написать скрипт на python, который сделал это за 72 секунды, но хочу улучшить еще.Сначала я опубликую базовый код, а затем настройки, которые я пробовал:
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 секунды независимо.Я предполагаю, что это происходит из-за GIL в Python.
В отличие от создания гигантского регулярного выражения, я попытался поместить каждую строку (шаблон) как RE-файл compilex в список и перебирать этот список в своей функции scanfile.Это привело к увеличению времени выполнения.
Чтобы избежать GIL в python, я попытался заставить каждый поток разветвляться на 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
Это привело к увеличению времени выполнения.
Любые предложения по улучшению производительности.
::::::::::::: EDIT ::::::::
Я не могу опубликовать ответы на своиТем не менее, здесь есть ответы на несколько вопросов:
@ David Nehme - просто чтобы люди знали, что я знаю о том, что у меня есть миллион queue.put (-1) '
@ Blender - для отметки нижней части очереди.Мои потоки сканера продолжают блокироваться, пока не достигнут -1, который находится внизу (в то время как nextFile не равен -1 :).Ядра процессора равны 8, однако из-за того, что GIL, использующий 1 поток, 4 потока или 8 потоков, НЕ имеет значения.Возникновение 8 подпроцессов привело к значительно более медленному коду (142 сек против 72)
@ ed - Да, это так же медленно, как и команда find \ grep, на самом деле медленнее, потому что без разбора greps файл не нужен
@ Рон - Не могу обновить, это должно быть универсально.Как вы думаете, это ускорит> 72 секунд?Bash Grepper делает 358 секунд.Мой метод pythoniant RE делает 72 секунды с \ 1-8 потоками.Метод popen w \ 8 thrads (8 подпроцессов) выполнялся за 142 секунды.До сих пор гигантский метод только для Python RE является явным победителем на сегодняшний день
@ 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 более эффективен, я не знаю почему, но я его экспериментально протестировал.Я предпочитаю делать это на питоне.Я уже добился ускорения в 5 раз с python, я хотел бы ускорить его.
::::::::::::: ПОБЕДИТЕЛЬ ПОБЕДИТЕЛЬ ПОБЕДИТЕЛЬ :::::::::::::::::
Похоже, у нас есть победитель.
Сценарий оболочки intued занимает 2-е место с 34 секундами, однако @ steveha пришел первым с 24 секундами.Из-за того, что у многих наших коробок нет python2.6, мне пришлось его cx_freeze.Я могу написать оболочку сценария оболочки для wget tar и распаковать его.Однако мне нравятся интуиции за простоту.
Спасибо за вашу помощь, ребята, теперь у меня есть эффективный инструмент для системного администрирования