Страницы

Поиск по вопросам

Показаны сообщения с ярлыком multiprocessing. Показать все сообщения
Показаны сообщения с ярлыком multiprocessing. Показать все сообщения

среда, 29 января 2020 г.

Можно ли средствами Java убить дочерние процессы процесса, созданного с помощью ProcessBuilder?

#java #jvm #процесс #multiprocessing


Если создать процесс 

ProcessBuilder builder = new ProcessBuilder("command");
final Process process = builder.start();


то его можно будет убить с помощью: process.destroy();

Но что, если этот command наплодит кучу других процессов? process.destroy(); уже
будет не в силах убить их. И они останутся в памяти.

При этом, если из командной строки сделать Ctrl+C, то дочерние процессы тоже завершатся.

В сети много вопросов на эту тему (большинство из них старые) и почти все ответы
говорят о том, что это невозможно сделать средствами JVM - нужно обращаться к ОС. Решил
задать вопрос, в надежде на то, что это всё же стало возможным с появлением Java 8.
Хочется получить решение, не зависимое от ОС.

Возможно ли на сегодняшний день средствами Java убить дочерние процессы процесса,
созданного с помощью ProcessBuilder, не манипулируя напрямую с командами ОС? Если возможно,
то как это реализовать? Если нет, то ожидается ли такая возможность в Java 9?

UPDATE

Хочу ещё раз выделить то, что я не ищу решения, зависящие от ОС. По этой ссылке можно
прочитать о том, как искать PID процесса в Unix и Windows. Потом можно будет обратиться
к терминалу с соответствующей командой для "убийства" процесса. 

Это не является темой данного вопроса.
    


Ответы

Ответ 1



В версиях Java до 8й включительно инструментарий для работы с процессами был довольно скудным. Но если ознакомиться, с JEP 102: Process API Updates (который реализуется в рамках Java 9), то мы увидим, что Brian Goetz говорит нам о следующих вещах: Возможность получить pid процесса JVM и pid-ы процессов, запущенных средствами API. Возможность получить список запущенных в системе процессов, включая их pid, состояние, наименование и, возможно, потребление ресурсов. Возможность взаимодействовать с деревьями процессов, а именно прекращать работу целого дерева. Возможность взаимодействовать с сотнями дочерних процессов, возможно, мультиплексируя потоки вывода и ошибок, чтобы избежать создания отдельной нити (thread) на каждый процесс. Все эти радости уже можно потрогать в 9ке: см. интерфейс ProcessHandle: long getPid() static Stream allProcesses() и ProcessHandle.Info info() Stream descendants()

Ответ 2



Если речь идет о "популярных" ОС для Java (Windows & unix-like), то на первой можно запустить программу taskkill/PID , а на вторых - kill-9 . На юниксе, кроме того, можно отправить сигнал группе процессов (process group id равен - процесса-родоначальника группы. Или воспользоваться библиотекой, дергающей нативные методы, типа Posix for Java (впрочем, для метода kill достаточно простого JNI вызова. Но средствами именно Java, да без сторонних библиотек, насколько я знаю, нет. Правда, говорят, что у Process так просто pid не получишь, но вот можно через Reflection достать приватное поле UnixProcess'а pid.. :) В 9-ой версии OpenJDK Process имеет метод getPid(), так что, теперь вышла нам всем поблажка в плане получения pid. Однако, никаких переносимых методов для управления группой процессов пока не просматривается.

Ответ 3



Кроссплатформенного решения нету. Придётся писать для каждой ОС самому. Не так всё просто, по-этому вряд ли кто-то даст законченное решение. Мы можем только дать какие-то подсказки, пути к решению задачи. Unix Сначала надо получить PID нашего процесса. /** * Получить строку - pid программы - Java VM */ public static String getPid() throws IOException,InterruptedException { Vector commands=new Vector(); commands.add("/bin/bash"); commands.add("-c"); commands.add("echo $PPID"); ProcessBuilder pb=new ProcessBuilder(commands); Process pr=pb.start(); pr.waitFor(); if (pr.exitValue()==0) { BufferedReader outReader=new BufferedReader(new InputStreamReader(pr.getInputStream())); return outReader.readLine().trim(); } else { System.out.println("Error while getting PID"); return ""; } } С другой стороны java.lang.Process - абстрактный класс, конкретная реализация зависит от ОС. На Linux это java.lang.UnixProcess, у которой есть приватное поле pid. Используя рефлексию можно запросто получить поле: public static long getPidOfProcess(Process p) { long pid = -1; try { if (p.getClass().getName().equals("java.lang.UNIXProcess")) { Field f = p.getClass().getDeclaredField("pid"); f.setAccessible(true); pid = f.getLong(p); f.setAccessible(false); } } catch (Exception e) { pid = -1; } return pid; } Дальше необходимо получить список подпроцессов. Есть комманда pstree ${pid}. Можно выполнить команду из Java Process p = Runtime.getRuntime().exec("pstree ${pid}"); Затем спарсить список и вытащить PID'ы всех процессов. Потом используя Runtime.getRuntime().exec() убить все процессы по PID'ам, если надо. Windows Для получения PID'а можно сделать что-то такое. Тут сложнее. Я знаю, что можно получить список запущенных процессов. Process p = Runtime.getRuntime().exec("cmd /c tasklist"); StringWriter writer = new StringWriter(); IOUtils.copy(p.getInputStream(), writer); String theString = writer.toString(); Но вопрос как найти зависимости между процессами не ясен. Как минимум, можно выполнить tasklist и отфильтровать список по имени процесса. Процесс с большим PID вероятно и есть ваш процесс. Так вы получите PID вашего главного процесса. Можно попробовать запустить PowerShell скрипт для получения списка процессов (а потом отфильтровать по parent id). Такая команда в шеле: gwmi win32_process |select ProcessID,ParentProcessID,Name, @{l="Username";e={$_.getowner().user}}|where {$_.Username -ne "SYSTEM"} | where {$_.Username -ne "LOCAL SERVICE"} | where {$_.Username -ne "NETWORK SERVICE"} | where {$_.Username -ne $null} |Sort-Object ProcessID | ft -AutoSize Даст что-то такое: ProcessID ParentProcessID Name Username --------- --------------- ---- -------- 180 4536 chrome.exe Suvitruf 396 5272 slack.exe Suvitruf 1488 1040 taskeng.exe Suvitruf 1504 4008 BatteryLife.exe Suvitruf 1540 180 chrome.exe Suvitruf 1704 180 chrome.exe Suvitruf 2084 180 chrome.exe Suvitruf 2404 5272 slack.exe Suvitruf 2408 5272 slack.exe Suvitruf Вам надо понять как эту команду выполнить с использованием Runtime.getRuntime().exec(). После этого распарсить ответ и получить список PID'ов процессов. Для их убийства вызывать: String cmd = "taskkill /F /PID " + tokill; Runtime.getRuntime().exec(cmd);

суббота, 11 января 2020 г.

Передача статического метода класса в multiprocessing.Process на Питоне 3.4

#python #python_3x #multiprocessing #python_36


Пытаюсь запустить статический метод класса через multiprocessing.Process. 

import multiprocessing as mp

class Test:
    @staticmethod
    def method():
        pass

if __name__=="__main__":       
    proc = mp.Process(target=Test.method)
    proc.start()


Но сталкиваюсь с тем, что оно работает в питоне 3.6, но не работает в 3.4 (а для
проекта требуется поддержка питона с 3.4). Полистав документацию, нашел, что можно
передавать только методы, определенные в top level'е модуля. Но тогда почему это работает
в питоне 3.6? Посмотрев чейнджлоги, я не нашел изменения в этой механике. Соответственно
встали вопросы:


Можно ли как то завернуть произвольную функцию или метод так, чтобы ее можно было
передать в Process.


Для отдельно взятого метода можно сделать функцию-обертку на уровне модуля:

import multiprocessing as mp

class Test:
    @staticmethod
    def method():
        pass

def method_wrapper(): # <======= This
    return Test.method()

if __name__=="__main__":       
    proc = mp.Process(target=method_wrapper)
    proc.start()


но это не подойдет, когда таких функций много. Можно ли сделать какую-то функцию,
которая будет принимать нужный мне метод и возвращать что-то, что можно передать в Process


Соответствует ли поведение питона 3.6 тому, что в документации (или это баг/фича)?
То есть могу ли я рассчитывать, что в будущем это поведение не изменится?


P.S. 

Зачем мне это нужно? Глобально стоит задача ограничить время выполнения функций.
То есть есть скрипт, который выполняет разные действия, и некоторые из них могут быть
очень долгие, и нужно их прибить, если они выполняются дольше определенного времени,
и сделать что-то с этим (или подождать и попробовать еще раз, или начать откат).  То
есть что я пытаюсь сделать: запустить функцию/метод в отдельном процессе, подождать
его в главном некоторое время и убить, если надо.

Потоки не подходят, я не могу их убить. Мне нужно гарантировать, что если время истекло,
действие больше не выполняется, прежде чем работать дальше.

В общем если есть идеи, как это сделать по другому, буду рад идеям.
    


Ответы

Ответ 1



Если я правильно понимаю, у Вас trace при ошибке получается примерно таким (проверял на Python 3.4.4, Windows 7 64-bit): Traceback (most recent call last): File "multiproc.py", line 17, in proc.start() File "c:\Python34\lib\multiprocessing\process.py", line 105, in start self._popen = self._Popen(self) File "c:\Python34\lib\multiprocessing\context.py", line 212, in _Popen return _default_context.get_context().Process._Popen(process_obj) File "c:\Python34\lib\multiprocessing\context.py", line 313, in _Popen return Popen(process_obj) File "c:\Python34\lib\multiprocessing\popen_spawn_win32.py", line 66, in __init__ reduction.dump(process_obj, to_child) File "c:\Python34\lib\multiprocessing\reduction.py", line 59, in dump ForkingPickler(file, protocol).dump(obj) _pickle.PicklingError: Can't pickle : attribute lookup method on __main__ failed ... File "", line 1, in File "c:\Python34\lib\multiprocessing\spawn.py", line 100, in spawn_main new_handle = steal_handle(parent_pid, pipe_handle) File "c:\Python34\lib\multiprocessing\reduction.py", line 81, in steal_handle _winapi.PROCESS_DUP_HANDLE, False, source_pid) OSError: [WinError 87] Параметр задан неверно Обращает на себя внимание строка Can't pickle . И действительно, до версии Python 3.5 pickle не поддерживал упаковку bound-методов. Начиная с версии 3.5 поддерживает. Итого Если есть необходимость использовать именно версию 3.4, то можно воспользоваться одним из советов: Can't pickle when using multiprocessing Pool.map() или Pickling a staticmethod in Python (правда, нужно будет дополнить код обёртками). Начиная с версии 3.5 всё работает и будет работать. Варианты доработок для Python 3.4 Wrapper По совету @jfs в комментарии выше: def wrapper(klass, method): getattr(klass, method)() if __name__ == "__main__": proc = mp.Process(target=wrapper, args=(Test, "method")) proc.start() Использовать метакласс По ответу с основного Stackoverflow: http://stackoverflow.com/a/1914798/711380 class _PickleableStaticMethod(object): def __init__(self, fn, cls=None): self.cls = cls self.fn = fn def __call__(self, *args, **kwargs): return self.fn(*args, **kwargs) def __get__(self, obj, cls): return _PickleableStaticMethod(self.fn, cls) def __getstate__(self): return (self.cls, self.fn.__name__) def __setstate__(self, state): self.cls, name = state self.fn = getattr(self.cls, name).fn class pickleable_staticmethods(type): def __new__(cls, name, bases, dct): new_cls = type.__new__(cls, name, bases, dct) dct = new_cls.__dict__ for name in dct.keys(): value = new_cls.__dict__[name] if isinstance(value, staticmethod): setattr( new_cls, name, _PickleableStaticMethod(value.__get__(None, new_cls), new_cls)) return new_cls class Test(metaclass=pickleable_staticmethods): @staticmethod def method(): while True: print(time.ctime()) time.sleep(2) if __name__ == "__main__": proc = mp.Process(target=Test.method) proc.start()

среда, 17 июля 2019 г.

linux использует только часть процессоров

Имеется восьмипроцессорный сервер. Каждый процессор по 10 ядер, итого 80 физических ядер. ОС Oracle Linux Server release 6.7, ядро 3.8.13-98.2.1.el6uek.x86_64
Проблема в том, что ОС использует только два процессора из 8:

При этом lscpu, говорит что все ядра задействованы
Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 160 On-line CPU(s) list: 0-159 Thread(s) per core: 2 Core(s) per socket: 10 Socket(s): 8 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 47 Stepping: 2 CPU MHz: 2393.990 BogoMIPS: 4787.90 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 30720K NUMA node0 CPU(s): 0-159
Пробовал запустить strees -c 80, количество задействованных ядер не изменилось, только выросла la. Не знаю куда копать. Признателен за любую помощь. Спасибо.


Ответ

Было обновлено ядро ОС. В биосе выставил вручную количество задействованных ядер на CPU (опция Enhanced Processor Core Disable (Intel Core Select)).
После загрузки в ОС все ядра стали использоваться, stress -c 160, соответственно, нагрузил их все. Доподлинно сказать, что именно помогло не могу.
Всем спасибо за помощь.

суббота, 30 марта 2019 г.

Можно ли средствами Java убить дочерние процессы процесса, созданного с помощью ProcessBuilder?

Если создать процесс
ProcessBuilder builder = new ProcessBuilder("command"); final Process process = builder.start();
то его можно будет убить с помощью: process.destroy();
Но что, если этот command наплодит кучу других процессов? process.destroy(); уже будет не в силах убить их. И они останутся в памяти.
При этом, если из командной строки сделать Ctrl+C, то дочерние процессы тоже завершатся.
В сети много вопросов на эту тему (большинство из них старые) и почти все ответы говорят о том, что это невозможно сделать средствами JVM - нужно обращаться к ОС. Решил задать вопрос, в надежде на то, что это всё же стало возможным с появлением Java 8. Хочется получить решение, не зависимое от ОС.
Возможно ли на сегодняшний день средствами Java убить дочерние процессы процесса, созданного с помощью ProcessBuilder, не манипулируя напрямую с командами ОС? Если возможно, то как это реализовать? Если нет, то ожидается ли такая возможность в Java 9?
UPDATE
Хочу ещё раз выделить то, что я не ищу решения, зависящие от ОС. По этой ссылке можно прочитать о том, как искать PID процесса в Unix и Windows. Потом можно будет обратиться к терминалу с соответствующей командой для "убийства" процесса.
Это не является темой данного вопроса.


Ответ

В версиях Java до 8й включительно инструментарий для работы с процессами был довольно скудным.
Но если ознакомиться, с JEP 102: Process API Updates (который реализуется в рамках Java 9), то мы увидим, что Brian Goetz говорит нам о следующих вещах:
Возможность получить pid процесса JVM и pid-ы процессов, запущенных средствами API. Возможность получить список запущенных в системе процессов, включая их pid, состояние, наименование и, возможно, потребление ресурсов. Возможность взаимодействовать с деревьями процессов, а именно прекращать работу целого дерева. Возможность взаимодействовать с сотнями дочерних процессов, возможно, мультиплексируя потоки вывода и ошибок, чтобы избежать создания отдельной нити (thread) на каждый процесс.
Все эти радости уже можно потрогать в 9ке: см. интерфейс ProcessHandle
long getPid() static Stream allProcesses() и ProcessHandle.Info info() Stream descendants()