Дочерние процессы, созданные в ASP.NET Core Process, уничтожаются при выходе
Я порождаю дочерний процесс в ASP.NET Core (.NET Framework) с классом Process
:
var process = new Process
{
StartInfo = new ProcessStartInfo(executableDir)
{
Arguments = commandDefinition.CommandDef.ArgumentsAsString,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = _contentPath,
},
};
process.Start()
Насколько я понимаю, когда родительский (ASP.Net Core) процесс будет убит, дочерний процесс должен остаться в живых. Я проверил это поведение с помощью двух консольных приложений, и дочерний процесс никогда не уничтожается после уничтожения родительского процесса. Однако, когда я запускаю новый процесс в ASP.NET Core, дочерний процесс убивается, когда:
- IIS перерабатывает приложение.
- MSDeploy публикует новую версию приложения ASP.NET Core.
- При использовании dotnet watch и приложение перезапускается во время изменения кода.
Он не убивается ТОЛЬКО, если родитель убит через диспетчер задач. (После некоторых тестов это не всегда так)
Из вышесказанного я подозреваю, что в ASP.NET Core есть механизм, который убивает все дочерние процессы при успешном завершении. Это где-то задокументировано? Есть ли способ избежать этого? Я не мог найти никакой информации о таком поведении.
Редактировать: на самом деле довольно легко.
- Создать проект ASP.NET Core (.NET Framework или .NET Core, не имеет значения)
- Добавьте код ниже в свой класс
Startup
- Запустите веб-приложение. Он будет размещен в IIS Express. Процесс вычисления начнется. Теперь либо убейте свое приложение через диспетчер задач, либо закройте его с помощью значка IIS Express на панели задач.
- Процесс Calc будет убит. (Иногда вам нужно обновить свою офлайн-страницу)
var process = new Process
{
StartInfo = new ProcessStartInfo("calc.exe")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
},
};
process.Start();
Edit2: проблема, кажется, в IIS. У меня есть два профиля в файле launchSettings.json. Если я запускаю его с IISExpress, то он закрывается, однако при использовании второго он живет.
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebApplication3Core": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:52135/"
}
Edit4:
Я провел некоторые исследования с монитором процесса, и вот результат:
Как вы можете видеть в ss1, что есть операция "Выход из процесса" с iisexpress, тогда есть много ненужных журналов и через некоторое время есть выход из процесса для calc.exe. Это ничем не отличается от нормального выхода. Разница лишь в последнем журнале, который говорит "CloseFile" и путь к моему веб-приложению, я не знаю, что это на самом деле означает. Это определенно тот, кто убивает calc.exe. У меня есть версия IIS Express 10.0.14358 (версия сервера, где я нашел это также 10)
Ответы
Ответ 1
Как я уже сказал в своем комментарии, существует объект Job, который создается ядром ASP.NET в сценарии с отсутствием процесса. Соответствующая часть исходного кода находится здесь:
https://github.com/aspnet/AspNetCore/blob/master/src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp#L89
HRESULT
SERVER_PROCESS::SetupJobObject(VOID)
{
HRESULT hr = S_OK;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { 0 };
if (m_hJobObject == NULL)
{
....
jobInfo.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if (!SetInformationJobObject(m_hJobObject,
JobObjectExtendedLimitInformation,
&jobInfo,
sizeof jobInfo))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
}
return hr;
}
Согласно документации, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE:
Вызывает завершение всех процессов, связанных с заданием, когда закрывается последний дескриптор задания.
Если вы копаете источник дальше, он не является необязательным.
Теперь вы можете увидеть это сами, если воспроизведете свои шаги. Используйте Process Explorer и перейдите к процессу dotnet.exe, вот что он отобразит:
- примечание 1: на самом деле
calc.exe
остается живым (по крайней мере, в моей установке Windows 10, потому что теперь это приложение WinRT, поэтому оно не станет дочерним по отношению к dotnet.exe), поэтому я использовал notepad.exe
- примечание 2:
iisexpress.exe
также создает объект задания, но он настроен как отколовшийся, что означает, что он не будет убивать дочерние процессы. - Примечание 3: если вы работаете с Visual Studio (не мой скриншот), вы можете увидеть промежуточный
VSIISExeLauncher.exe
процесс между iisexpress.exe
и dotnet.exe
. Этот также создает объект Job с "kill on close", чтобы добавить к путанице...
Ответ 2
редактировать
Решение не работает для notepad.exe - я постараюсь выяснить, есть ли другой вариант, чем решение Саймона, но сейчас его ответ, кажется, будет верным.
Старый ответ (будет удален)
Я должен вымыть руки сейчас для написания этого кода, но я думаю, что вы можете обойти это ограничение, если это то, что вам нужно в вашем контексте. Я советую не запускать внешние процессы, особенно с "веб-сервера". Это напрашивается на неприятности. Замена процесса каким-либо злоумышленником, повышение учетных данных, непредвиденное поведение из-за изменения синтаксиса - это лишь некоторые из них.
В любом случае это может сработать для вас (вставив cmd.exe между ними, чтобы попытаться разорвать отношения между детьми и родителями):
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName ="cmd.exe",
Arguments = "/c calc.exe",
WindowStyle = ProcessWindowStyle.Hidden
}
};
process.Start();
Ответ 3
AFAIK процесс знает только о своем непосредственном родителе. Итак, что происходит, если вы запускаете дочерний процесс, единственная цель - запустить грандиозный дочерний процесс, а затем сразу же закрыться. После этого процесс вашего внука остается живым без какого-либо живого родителя и должен пережить смерть прародителя.