Войти

Показать полную графическую версию : [решено] Запуск без видимого окна с получением PID дочернего процесса


Anonymоus
26-06-2019, 18:07
Приветствую уважаемых форумчан.
Есть скрипт — загрузчик, который стартует портабельную сборку nginx + php + mysql + доп. модули, он рассчитан на запуск как от юзера, так и в качестве сервиса (например, через nssm). Хотелось бы организовать пул процессов интерпретатора php-cgi на разных портах, и такая возможность в принципе есть через конфиг бэкенда в nginx, но для отслеживания возможных падений (а у php это не столько редко случается) и перезапуска конкретного экземпляра мне нужен PID запущенного процесса. Я знаю о возможности запуска через wmic process call create с получением PID дочернего процесса, но тут есть проблема: я запускаю через hidcon, соответственно получаю PID hidcon-а, а не php-cgi.
Собственно, вопрос: как запустить без видимого окна и получить PID? Или каким ещё образом можно отслеживать одинаковые процессы, запущенные с разными аргументами?
@Echo Off
:: Portable nginx+php+mysql for Windows
:: Inquisitor, 2013-2019

:: nginx 1.17.1
:: php 7.2.19 + composer
:: mysql 5.7.23

CD /D "%~dp0"

:: Лог работы скрипта
Set LogFile=%~dpn0.log
:: Формат даты в логе
Set TimestampFormat=[%%Date%% %%Time:~,8%%]
:: Периодичность проверки запроса на отключение и перезапуска процессов при падении, секунды
Set WatcherTimeout=10


:: Обрабатываем команду на выключение
If /I "%~1"=="shutdown" (
If Exist "%~dp0.loader.lock" (
Call :Log Received termination signal, shutting down
Del "%~dp0.loader.lock" 2>nul
) Else (
Call :Log [WARNING] Received termination signal, server not started
)
Exit /B
)

:: Обрабатываем запуск сервиса
If /I "%~1"=="service" (
Call Echo.>>"%LogFile%"
Call :Log Loader started as service
GoTo :service
)

:: Перезапускаем себя как скрытый процесс, если запущено без аргументов
If "%~1"=="" (
Start "" "%~dp0tools\hidcon" %~nx0 restart_hidden
Call Echo.>>"%LogFile%"
Call :Log Loader started
Exit
)

:service

:: Сохраняем оригинальный path
Set OSPath=%Path%
:: Добавляем пути к бинарникам в системный path для этого скрипта и дочерних процессов
Set Path=%~dp0tools;%~dp0nginx;%~dp0php;%~dp0mysql\bin
:: Добавляем модули
:: FFMpeg 4.1.3
Set Path=%Path%;%~dp0modules\ffmpeg
:: ImageMagick 6.9.3 (php7)
Set Path=%Path%;%~dp0modules\imagick-php7\ImageMagick
Set LOCALAPPDATA=%~dp0modules\imagick-php5
:: Меняем системный temp на наш
Set Temp=%~dp0\tmp
:: Добавляем системный path с низшим приоритетом
Set Path=%Path%;%OSPath%

:: Обрабатываем запуск консоли в рабочем окружении (Composer \ RIOX)
If /I "%~1"=="console" (
PushD nginx\wwwroot
Start cmd
Exit /B 0
)

:: Запускаем каждый процесс из его рабочей директории

:: MySQL
Call :Log Starting MySQL...
:start-mysql
PushD .\mysql\
Start bin\mysqld --defaults-file="my.ini" --user=root --standalone||Call :Log [ERROR] Can't start MySQL
PopD
If "%1"=="restart" (
Call :Log [WARNING] MySQL crashed, restarting
Exit /B
)

:: PHP
Call :Log Starting PHP-FPM...
:start-php
PushD .\php
:: меняя что-то тут, не забываем изменить в nginx\conf\nginx.conf, секция upstream backend
Start hidcon php-cgi -b 127.0.0.1:9123 -c ".\php.ini"||Call :Log [ERROR] Can't start PHP
::Start hidcon php-cgi -b 127.0.0.1:9124 -c ".\php.ini"||Call :Log [ERROR] Can't start PHP
PopD
If "%1"=="restart" (
Call :Log [WARNING] PHP-FPM crashed, restarting
Exit /B
)

:: nginx
Call :Log Starting NginX...
:start-nginx
PushD .\nginx
Start nginx||Call :Log [ERROR] Can't start NginX
PopD
If "%1"=="restart" (
Call :Log [WARNING] NginX crashed, restarting
Exit /B
)

:: Убиваем уже не нужные процессы hidcon
TaskKill /F /IM "hidcon.exe"

:: Повышаем приоритет для запущенных процессов
For %%A In ("mysqld.exe", "php-cgi.exe", "nginx.exe") Do (
wmic process where name="%%~A" CALL setpriority "high priority"
)

:: Создаём lock-файл и следим за его наличием
Set LockFile=.\.%~n0.lock
Echo.>"%LockFile%"
:Watcher
Ping -n %WatcherTimeout% 127.0.0.1>nul 2>nul
:: Проверяем наличие процессов и перезапускаем в случае падения
TaskList /FI "imagename EQ php-cgi.exe" /FO:CSV|FindStr /I /C:"php-cgi.exe">nul||Call :start-php restart
:: Запускаем процесс выключения, если файл удалён
If Not Exist "%LockFile%" (
Call :Log Lockfile not found, killing child processes

rem Останавливаем nginx
PushD .\nginx
nginx -s stop
PopD

rem Останавливаем MySQL
mysqladmin -u root -p%_DBPASS% shutdown

rem Останавливаем PHP-CGI
rem эта дрянь не поддерживает корректное отключение
rem поэтому просто дергаем TerminateProcess() aka SIGKILL
TaskKill /F /T /IM php-cgi.exe

Call :Log Loader stopped
Exit
)
GoTo :Watcher

:Log
Call Echo %TimestampFormat% %*>>"%LogFile%"
Exit /B

Iska
27-06-2019, 03:40
я запускаю через hidcon, »
А Вы не запускайте через hidecon.exe. Вам для чего hidecon.exe? Для того, чтобы скрыть консоль от пакетного файла. А Вы используйте не пакетный файл, а WSH.

CD /D "%~dp0" »
…может сыграть с Вами очень злую шутку при попытке исполнения данного пакетного файла с разделённого ресурса.

Anonymоus
27-06-2019, 23:24
А Вы используйте не пакетный файл, а WSH »
Да, похоже это единственный вменяемый вариант (кроме написания самостоятельно аналога hidcon, который будет возвращать PID)
Тогда попрошу подсказки, как в VBS вернуть PID процесса в stdout или errorlevel, чтобы получить его основным скриптом.

Anonymоus
28-06-2019, 04:48
WSH оказался не лучшим вариантом, в тестовой сборке у пользователей пошли ложно-положительные срабатывания антивирусов именно на сам vbs, а обьяснять каждому, как в этом зоопарке антивирусов добавить в исключения, мне честно говоря лень.
Пришлось идти по сложному варианту и просить друга помочь с написанием аналога hidcon. Выкладываю здесь, вдруг кому ещё пригодится, исходники в том же архиве, C++, собрано под x86.

Синтаксис: easy_spawner "<имя программы>" <аргумент 1> ... <аргумент N>
Вызываемая программа может как находиться в PATH, так и быть указана полным или относительным путем. Второй и последующий аргументы передаются вызываемой программе.
PID запущенного процесса выводится в stdout и легко получается конструкцией вида For /F "delims=" %%P In ('easy_spawner' "program.exe" -arg1=test --test') Do Set PID=%%~P
Если вызываемая программа не смогла запуститься, easy_spawner устанавливает errorlevel в 1 и выводит сообщение об ошибке от программы.

Patroklos
07-07-2019, 21:29
А на какой ОС вы используете easy_spawner?
А то у меня на 10 х64 не фурычит.
Какие-то окошки мигают но в таскменеджере видно что ничего не запущенно.

Anonymоus
21-07-2019, 16:20
Patroklos, тестировалось на x64 Win10 Enterprise, тестовые сборки проверял на x86 Win10 Home. Имейте в виду, у файла нету цифровой подписи, поэтому при первом запуске его может блокировать SmartScreen.
Попробуйте добавить в path ту директорию, из которой запускаете целевой exe. Вот живой пример того, как используется у меня:
Set PHP_Pool[]="9123","9124","9125"
For %%A In (%PHP_Pool[]%) Do (
For /F "delims=" %%P In ('easy_spawner "php-cgi.exe" "-b 127.0.0.1:%%~A -c .\php.ini"') Do (
Call :Log New PHP worker on port :%%~A with PID %%P
Set _PHP_%%~A=%%P
)
)




© OSzone.net 2001-2012