Зомб ie процессов, здесь мы снова go - PullRequest
0 голосов
/ 16 января 2020

Я много борюсь с многопроцессорностью / многопоточностью / подпроцессингом. В основном я пытаюсь выполнить каждый двоичный файл, доступный на моем компьютере, для этого я написал скрипт python. Но у меня продолжают происходить процессы зомбирования ie («несуществующие»), которые оказываются в тупике, если все 4 моих работника находятся в этом состоянии. Я пробовал много разных вещей, но, похоже, ничего не получается: (

Вот как выглядит архитектура:

|   \_ python -m dataset --generate
|       \_ worker1
|       |   \_ [thread1] firejail bin1
|       \_ worker2
|       |   \_ [thread1] firejail bin1
|       |   \_ [thread2] firejail bin2
|       |   \_ [thread3] firejail bin3
|       \_ worker3
|       |   \_ [thread1] [firejail] <defunct>
|       \_ worker4
|       |   \_ [thread1] [firejail] <defunct>

4 рабочих я создаю так:

# spawn mode prevents deadlocks https://codewithoutrules.com/2018/09/04/python-multiprocessing/
with get_context("spawn").Pool() as pool:

    results = []

    for binary in binaries:
        result = pool.apply_async(legit.analyse, args=(binary,),
                                  callback=_binary_analysis_finished_callback,
                                  error_callback=error_callback)
        results.append(result)

(заметьте, я использую пул "spawn", но теперь мне интересно, пригодится ли он ...)

Каждый работник создаст несколько потоков, например:

threads = []
executions = []

def thread_wrapper(*args):
    flows, output, returncode = _exec_using_firejail(*args)
    executions.append(Execution(*args, flows, is_malware=False))

for command_line in potentially_working_command_lines:
    thread = Thread(target=thread_wrapper, args=(command_line,))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

И каждый поток запускает новый процесс в изолированной программной среде firejail:

process = subprocess.Popen(FIREJAIL_COMMAND +
                           ["strace", "-o", output_filename, "-ff", "-xx", "-qq", "-s", "1000"] + command_line,
                           stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)

try:
    out, errs = process.communicate(timeout=5, input=b"Y\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n")
    # print("stdout:", out)
    # print("stderr:", errs)

except subprocess.TimeoutExpired:
    # print(command_line, "timed out")
    os.killpg(os.getpgid(process.pid), signal.SIGKILL)
    out, errs = process.communicate()

Я использую os.killpg(), а не process.kill(), потому что по некоторым причинам подпроцессы моего процесса Popen не уничтожаются. .. Это возможно благодаря preexec_fn=os.setsid, который устанавливает gid всех потомков, но даже при использовании этого метода некоторые процессы, такие как zsh, будут вызывать процесс zomb ie, потому что, похоже, zsh меняет свой gid и поэтому мой os.killpg не работает должным образом ...

Я ищу способ на 100% быть уверенным, что все процессы будут мертвы.

1 Ответ

1 голос
/ 16 января 2020

Если вы хотите использовать для этого модуль subprocess, вам следует использовать метод .kill объекта process напрямую вместо использования модуля os. Использование communicate является блокирующим действием; поэтому Python будет ждать ответа. Использование параметра timeout помогает, но будет медленным для многих процессов.

import subprocess

cmd_list = (
    FIREJAIL_COMMAND 
    + ["strace", "-o", output_filename, "-ff", "-xx", "-qq", "-s", "1000"] 
    + command_line
) 
proc = subprocess.Popen(
    cmd_list,
    stdout=subprocess.PIPE, 
    stderr=subprocess.PIPE, 
    preexec_fn=os.setsid
)

try:
    out, errs = proc.communicate(timeout=5, input=b"Y\n" * 16)
except subprocess.TimeoutExpired:
    proc.kill()
    out, errs = None, None

ret_code = process.wait()

Если вы хотите запустить его в неблокирующем l oop над набором процессов, то есть когда вы используете poll. Вот пример. Предполагается, что у вас есть список filenames и соответствующий command_lines, который вы хотите передать в процесс создания.

import subprocess
import time

def create_process(output_filename, command_line):
    cmd_list = (
        FIREJAIL_COMMAND 
        + ["strace", "-o", output_filename, "-ff", "-xx", "-qq", "-s", "1000"] 
        + command_line
    ) 
    proc = subprocess.Popen(
        cmd_list,
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE, 
        preexec_fn=os.setsid
    )
    return {proc: (output_filename, command_line)}

processes = [create_process for f, c in zip(filenames, command_lines)]

TIMEOUT = 5
WAIT = 0.25  # how long to wait between checking the processes
finished = []
for _ in range(round(TIMEOUT / WAIT)):
    finished_new = []
    if not processes:
        break
    for proc in processes:
        if proc.poll():
            finished_new.append(proc)
    # cleanup
    for proc in finished_new:
        process.remove(proc)
    finished.extend(finished_new)
    time.sleep(WAIT)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...