![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Я люблю работать в консоли. Люблю автоматизацию скриптами. И если надо что-то массово параллельно обработать, то запустить все это в разных окнах tmux'а или screen'а, и любоваться процессом. И вот чего мне не хватало, так это возможности в каком-нибудь сложном скрипте наделать дочерних процессов и разложить их по разным окнам tmux'а. Кажется что естественная хотелка, а вот как сделать -- не понятно. И вот дошли руки разобраться как же такой фокус делается
Переносим процесс в другую консоль
Для начала рассмотрим более простую задачу: как вообще в принципе перенести процесс из одной консоли в другую. В древние времена эта задача решалась ручной перепривязкой потоков ввода-вывода процесса к другому tty или каким-то подобным образом. В современном линуксе для этого есть утилита reptyr которая автоматически стырит для вас процесс из соседнего терминала и привяжет его к текущему.
Предположим у вас есть программа:
#!/usr/bin/perl
print "My PID is $$\n"; # print current process PID;
sleep 10;
while(1){
print ++$i, "\n";
sleep 1;
}
Эта программа выводит в консоль свой PID, и через некоторое время начинает в той же консоли считать секунды.
Если запустить эту программу в одной консоли, а в соседней консоли выполнить команду
reptyr [PID-number]
где PID-number
номер процесса который напечатала наша тестовая программа в самом начале, то выполнение программы в исходной консоли прекратиться, а в новой продолжиться как ни в чем ни бывало.
Достаточно просто.
Перенос дочернего процесса в другую консоль
Однако если вы попробуете проделать тот же самый фокус не с отдельно работающей программой, а с дочерним процессом reptyr
на вас наругается:
[-] Process 739989 (test2.pl) shares 739990's process group. Unable to attach.
(This most commonly means that 739990 has sub-processes).
Unable to attach to pid 739990: Invalid argument
Дело в том, что когда вы делаете форк, порожденный процесс оказывается в одной группе с родительским процессом (и родительский процесс оказывается лидером группы), и по какой-то причине reptyr
с такой ситуацией не справляется. Поэтому прежде чем тырить дочерний процесс, надо сначала вывести его из группы. Делается это вызовом setpgid
, в котором дочерний процесс назначается сам себе лидером группы, и таким образом выходит из группы родительского процесса. После этого reptyr
вполне справляется с переносом дочернего процесса в новую консоль.
#!/usr/bin/perl
use POSIX;
$pid = fork();
if ($pid)
{
# parent process
print "Child PID is $pid\n";
sleep 10;
while (1) {print ".\n"; sleep 1};
} else
{
# child process
(setpgid($$,$$) != -1) || die "Can't set own group: $!";
sleep 10;
while (1) {print ++$i,"\n"; sleep 1};
}
Эта программа выводит на экран PID
порожденного дочернего процесса, после чего спустя десять секунд, родительский процесс начинает выводить на экран точки с частотой один раз в секунду, а дочерний процесс начинает эти секунды вслух считать. Если не предпринимать никаких действий выводы обоих процессов будут смешаны в одной консоли, в которой их запустили.
Однако если в соседнем терминале запустить
reptyr [child-PID-number]
где child-PID-number
-- PID
дочернего процесса напечатанный нашей программой, то в результате вывод дочернего процесса (цифры) переедет в новую консоль, а вывод родительского процесса (точки) останется в той консоли в которой его изначально запустили.
Чего собственно говоря и хотелось добиться.
Материалы для раздумий
- Статья в которой подробно объясняется материал групп и сессий: https://blog.nelhage.com/2011/02/changing-ctty/ Понимание этого материала помогло таки найти финальное решение
- Мои попытки выспросить нужное решение на linux.org.ru: https://www.linux.org.ru/forum/development/17555795
- Заметка на stackoverflow написанная по результатм: https://stackoverflow.com/questions/78428371/how-to-fork-process-into-another-virtual-terminal/78428372
no subject
Date: 2024-05-09 12:08 pm (UTC)Роскошная идея. Спасибо.
no subject
Date: 2024-05-09 12:42 pm (UTC)вроде же в screen это тривиально делается. Если из-под screen запустить
screen команда
, то в существующем screen будет для этой команды создано новое окно.Можно еще
screen -t заголовок команда
no subject
Date: 2024-05-09 02:15 pm (UTC)Тут речь не про запустить отдельную команду... Тут про то, чтобы нафоркать воркеров, показать каждый из воркеров в своем виртуальном терминале, дождаться пока доработают и потом продолжить...
no subject
Date: 2024-05-09 03:35 pm (UTC)Ну вот нафоркать воркеров можно путем запуска их отдельными командами, каждый в своем скрине. Вот с дождаться - сложнее, похоже синхронизатор придется свой придумывать.
no subject
Date: 2024-05-09 03:43 pm (UTC)Я думал над этим, но мне уж больно этого не хотелось... Воркеры там элементы некоторой объектно-ориентированной иерархии... При последовательном выполнении -- вообще норм. С форком тоже ничего. А вот запуск в новом процессе это нужно в каком-то урезанном виде всю иерархию надо было бы выстраивать... Можно, но не хотелось бы, слишком многое может пойти не так...
no subject
Date: 2024-05-09 03:58 pm (UTC)А как в том языке, на котором ты пишешь, с сериализацией? Вот ты питон не любишь, а в нем только в стандартной библиотеке есть по-моему три способа сериализации/восстановления объектов.
no subject
Date: 2024-05-09 04:06 pm (UTC)Пишу я, вполне ожидаемо, на перле. Можно наверное сдампить все Data::Dumper'ом, а потом загрузить обратно... Но как-то это не изящно...
no subject
Date: 2024-05-09 04:34 pm (UTC)Ну для этой цели скорее пойдет Storable или FreezeThaw.