Intereting Posts
Установка CentOS 7: проблемы с разделом и поврежденный пользовательский интерфейс Я хотел бы заменить шаблон другим шаблоном, найденным в той же строке изменение / var в запущенной системе Escape последовательности в выводе скрипта, вызванного из приложения ncurses Сортировка файла на основе средней части Избавьтесь от Meta Key Gnome Невозможно обработать stdout с помощью трубы Запустить команды командной оболочки с правами root без предоставления прав пользователя sudo? Как производитель труб может сообщить потребителю трубки, что он достиг «Конец файла»? »(Un-named-pipe, not named-pipe) Изучение того, какие файлы загрязнены или буферизованы в Linux Удалить файлы на 10 дней старше от hdfs Нужно ли распространять источники полного распространения под GPL? Как определить имя функции callee в скрипте Мышь не работает Найти / заменить на блочном устройстве?

Скрипт умирает, когда родительский процесс завершается

У меня есть служба .NET Core, работающая на Debian 9, назовем ее MyService. В какой-то момент этот сервис запускает скрипт bash update.sh с использованием Process.Start() с ShellExecute=true .

Этот скрипт в основном выполняет apt-get update; apt-get upgrade apt-get update; apt-get upgrade .

Во время обновления пакета процесс MyService завершается: также завершается сценарий обновления, а обновление apt-get upgrade уничтожается, оставляя несовместимые пакеты, которые необходимо исправить вручную.

Что я хочу, так это то, что update.sh НЕ завершается, когда MyService завершается.

Я попытался разделить update.sh на две части, первая из которых запускалась второй по-разному; Я попытался запустить update2.sh с setsid и nohup но я всегда получаю один и тот же результат. Я попытался выполнить скрипт update2.sh в новой оболочке bash с /bin/bash /c "update2.sh" , тот же результат.

Как запустить скрипт, запущенный из двоичного файла, и полностью отсоединиться от двоичного процесса, чтобы я мог уничтожить двоичный файл, пока скрипт продолжает работать?

Вот мое окружение. MyService – это двоичный файл, работающий как сервис. update.sh запускается MyService.

.NET Core код для запуска сценария оболочки внутри двоичного файла MyService:

 var process = new Process(); process.EnableRaisingEvents = true; // to avoid [defunct] sh processes process.StartInfo.FileName = "/opt/myservice/update.sh"; process.StartInfo.Arguments = ""; process.StartInfo.UseShellExecute = true; process.StartInfo.CreateNoWindow = true; process.Start(); process.WaitForExit(10000); if (process.HasExited) { Console.WriteLine("Exit code: " + process.ExitCode); } else { Console.WriteLine("Child process still running after 10 seconds"); } 

update.sh:

 nohup /opt/myservice/update2.sh > /opt/myservice/update.log & systemctl stop MyService 

update2.sh:

 apt-get update >> /opt/myservice/update.log apt-get -y install --only-upgrade myservice-1.0 >> /opt/myservice/update.log 

update2.sh никогда не выполняется, потому что он прекращается, когда MyService завершается update.sh .

update.sh возвращает код 143, похоже, он был убит.

 2018-08-16 14:46:14.5215|Running update script: /opt/myservice/update.sh 2018-08-16 14:46:14.5883|Update script /opt/myservice/update.sh returned: 143 

ОБНОВИТЬ

Я попробовал следующие подходы, спасибо за предложения:

  • setsid
  • отрекаться
  • поЬир
  • экран
  • tmux
  • удалить из списка

Каждый подход имеет один и тот же результат, завершение всех порожденных процессов. Я подозреваю, что это .NET Core “особенность”.

ОБНОВЛЕНИЕ 2

Я обнаружил, что systemctl stop MyService по умолчанию явно убивает все порожденные процессы службой.

https://stackoverflow.com/questions/40898077/systemd-systemctl-stop-aggressively-kills-subprocesses

Если я добавлю KillMode=process к дескриптору службы, сценарий обновления не будет прерван при завершении работы службы.

НЕТ СПОСОБА выхода из пространства PID для службы, запущенной systemctl . Каждая используемая техника, включая принятый ответ, не генерирует отдельный процесс. Каждый порожденный процесс всегда systemctl stop MyService помощью systemctl stop MyService если не KillMode=process .

Я закончил тем, что создал отдельный сервис MyServiceUpdater : этот сервис запускает скрипт простого обновления без какого-либо разветвления. Поскольку PID-пространство отличается, все работает как положено. Это была долгая поездка.

На тестовой системе Centos 7 через

 $ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm $ sudo yum install dotnet-sdk-2.1 

что приводит к установке dotnet-sdk-2.1-2.1.400-1.x86_64 с тестовым кодом

 using System; using System.Diagnostics; using System.ComponentModel; namespace myApp { class Program { static void Main(string[] args) { var process = new Process(); process.EnableRaisingEvents = true; // to avoid [defunct] sh processes process.StartInfo.FileName = "/var/tmp/foo"; process.StartInfo.Arguments = ""; process.StartInfo.UseShellExecute = true; process.StartInfo.CreateNoWindow = true; process.Start(); process.WaitForExit(10000); if (process.HasExited) { Console.WriteLine("Exit code: " + process.ExitCode); } else { Console.WriteLine("Child process still running after 10 seconds"); } } } } 

и сценарий оболочки как /var/tmp/foo strace останавливается и показывает, что /var/tmp/foo запускается через xdg-open что в моей системе делает … Я не уверен, что, это кажется ненужным осложнением ,

 $ strace -o foo -f dotnet run Child process still running after 10 seconds ^C $ grep /var/tmp/foo foo 25907 execve("/usr/bin/xdg-open", ["/usr/bin/xdg-open", "/var/tmp/foo"], [/* 37 vars */]  ... 

более простое решение состоит в том, чтобы просто выполнить программу, которая, в свою очередь, может быть сценарием оболочки, который делает то, что вы хотите, что для .NET не требует использования оболочки:

  process.StartInfo.UseShellExecute = false; 

с этим набором strace показывает, что /var/tmp/foo запускается через (намного более простой execve(2) вызов execve(2) :

 26268 stat("/var/tmp/foo", {st_mode=S_IFREG|0755, st_size=37, ...}) = 0 26268 access("/var/tmp/foo", X_OK) = 0 26275 execve("/var/tmp/foo", ["/var/tmp/foo"], [/* 37 vars */]  

и что .NET отказывается выходить:

 $ strace -o foo -f dotnet run Child process still running after 10 seconds ^C^C^C^C^C^C^C^C 

потому что foo заменяет себя чем-то, что игнорирует большинство сигналов (особенно не USR2 , или всегда есть KILL (но избегайте этого!)):

 $ cat /var/tmp/foo #!/bin/sh exec /var/tmp/stayin-alive $ cat /var/tmp/stayin-alive #!/usr/bin/perl use Sys::Syslog; for my $s (qw(HUP INT QUIT PIPE ALRM TERM CHLD USR1)) { $SIG{$s} = \&shandle; } openlog( 'stayin-alive', 'ndelay,pid', LOG_USER ); while (1) { syslog LOG_NOTICE, "oh oh oh oh oh stayin alive"; sleep 7; } sub shandle { syslog LOG_NOTICE, "nice try - @_"; } 

демон

С процессом, который отделяет себя от родителя, и сценарием оболочки, который запускает несколько команд (надеюсь, эквивалентно предполагаемому apt-get update; apt-get upgrade )

 $ cat /var/tmp/a-few-things #!/bin/sh sleep 17 ; echo a >/var/tmp/output ; echo b >/var/tmp/output 

мы можем изменить программу .NET для запуска /var/tmp/solitary /var/tmp/a-few-things

  process.StartInfo.FileName = "/var/tmp/solitary"; process.StartInfo.Arguments = "/var/tmp/a-few-things"; process.StartInfo.UseShellExecute = false; 

который при запуске приводит к довольно быстрому завершению работы программы .NET

 $ dotnet run Exit code: 0 

и, в конце концов, файл /var/tmp/output содержит две строки, написанные процессом, который не был уничтожен, когда программа .NET была удалена.

Вам, вероятно, следует сохранить выходные данные команд APT где-нибудь, а также может потребоваться что-то, чтобы два (или более!) Обновления не пытались запускаться одновременно и т. Д. Эта версия не останавливается для вопросов и игнорирует любой TERM сигналы ( INT также может быть необходимо игнорировать).

 #!/bin/sh trap '' TERM set -e apt-get --yes update apt-get --yes upgrade 

Используйте crontab (или at) (не mono / .net) для планирования задач.

Нормальные варианты;

  • nohup my.sh &
  • screen -dm -S my my.sh
  • tmux new -d -s my my.sh
  • сервис мой старт / systemctl начать мой
  • Ctrl + Z, BG, отречься