Невозможно убить дочерний процесс в Windows
Следующее никогда не выйдет из
var child_process = require('child_process');
var ps = child_process.spawn('.\\node_modules\\.bin\\babel.cmd', ['input.js', '--out-file', 'output.js', '--watch']);
ps.on('exit', function() {
console.log('exit');
});
ps.on('close', function() {
console.log('close');
});
setTimeout(function () {
ps.kill();
}, 2000);
Что здесь происходит? и что здесь делать правильно? Единственный способ закрыть этот процесс - убить родительский процесс. Я подозреваю, что он ждет, когда stdio начнет флеш или что-то в этом роде?
Он умирает, если ему задана конфигурация 'ignore'
stdio, но мне нужны потоки.
var ps = child_process.spawn('.\\node_modules\\.bin\\babel.cmd', ['test.js', '--out-file', 'output.js', '--watch'], {
stdio: 'ignore'
});
Ответы
Ответ 1
Самое прямое решение, не создавайте скрипты, а не создавайте node напрямую.
Для unix script является исполняемым файлом npm. Однако для окон нам нужно разрешить имя из файла .cmd.
if (path.extname(command).toLowerCase() == '.cmd') {
var content = '' + fs.readFileSync(command);
var link = (/node "%~dp0\\(.*?)"/.exec(content) || [])[1];
if (link) {
command = path.resolve(path.dirname(command), link);
}
}
var ps = child.spawn('node', [command].concat(args), options);
Ответ 2
Вы создаете дочерний процесс cmd.exe
с помощью babel.cmd
. Затем этот процесс запускает другой процесс внука node
и запускает babel script. Когда вы выполняете ps.kill()
, он убивает только cmd.exe
, но не создает дочерние процессы, созданные им.
Хотя cmd.exe
закрыт, поток stdio родительского процесса по-прежнему используется совместно с другими процессами в дереве. Таким образом, родительский процесс ожидает закрытия потока. Использование stdio: 'ignore'
приведет к прекращению совместного использования потока stdio с другими процессами в дереве, но эти процессы внуков будут продолжать работать.
Обновление:
После обсуждения с OP я тестировал все три параметра stdio: pipe
(по умолчанию), inherit
, ignore
в iojs 3.3.0, Windows 7 (32 бит). Ни один из них не убивает процесс внука.
С inherit
и ignore
оба родительского процесса и дочерний процесс (cmd.exe) убиваются, но большой дочерний процесс все еще плавает вокруг и не принадлежит ни одному дереву процессов.
С pipe
завершается только дочерний процесс, но как родительский, так и большой дочерний процесс продолжают работать. Причина, по которой родительский процесс не выходит, скорее всего вызвана тем, что stdio родительского node.exe по-прежнему используется совместно с другими процессами, как указано в исходном ответе.
child.unref()
не влияет на убийство большого дочернего процесса. Он только удаляет дочерний процесс из родительского цикла событий, чтобы родительский процесс мог нормально выйти.
Я мог бы подумать о нескольких решениях:
-
Вызовите babel.js
непосредственно без этого cmd script:
var ps = child_process.spawn('node', ['.\\node_modules\\babel\\bin\\babel.js', 'input.js', '--out-file', 'output.js', '--watch'])
-
Используйте команду windows taskkill
с флагом \T
, чтобы убить процесс и все дочерние процессы, запущенные им:
os = require('os');
if(os.platform() === 'win32'){
child_process.exec('taskkill /pid ' + ps.pid + ' /T /F')
}else{
ps.kill();
}
Есть несколько модулей npm, которые могут обрабатывать убийство дочерних процессов как в Unix, так и в Windows, например. tree-kill
. Для окон он использует taskkill
или tasklist
.
Ответ 3
Одним из возможных решений является вызов ReadableStream.end
или ReadableStream.destroy
Ответ 4
У меня такая же проблема в Windows (Win 10 64x). Я не могу закончить процесс порожденного дочернего процесса.
Я запускаю службу (настраиваемый HTTP-сервер service.windows
) с помощью child_process.spawn()
:
const { spawn } = require('child_process');
let cp = spawn('"C:\\Users\\user\\app\\service.windows"', [], { shell: true, });
cp.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
console.log('cp.connected', cp.connected);
console.log('process.pid', process.pid); // 6632 <<= main node.js PID
console.log('cp.pid', cp.pid); // 9424 <<= child PID
// All these are useless, they just kill `cp`
cp.kill('SIGINT'); // Doesn't terminate service.windows
cp.kill('SIGKILL'); // Doesn't terminate service.windows
cp.kill('SIGTERM'); // Doesn't terminate service.windows
});
И я хочу прекратить службу HTTP-сервера (service.windows
). И это невозможно в Windows с помощью cp.kill('ANY SIGNAL')
. Node.js убивает свой дочерний процесс (cp
), но мой HTTP-сервер (service.windows
) все еще работает нормально.
Когда я проверяю его на другом терминале, я вижу, что мой сервер работает нормально:
$ netstat -ano | findstr :9090
TCP 0.0.0.0:9090 0.0.0.0:0 LISTENING 1340
TCP [::]:9090 [::]:0 LISTENING 1340
Я пытаюсь вручную убить свой сервер PID
, но с флагом T
, а не F
. Разница:
T
завершать все дочерние процессы вместе с родительским процессом, обычно называемым уничтожением дерева.
F
процесс принудительно прекращается.
$ taskkill -T -PID 1340
ERROR: The process with PID 1340 (child process of PID 9424) could not be terminated.
Reason: This process can only be terminated forcefully (with /F option).
И он точно говорит, что мой сервер 1340
является дочерним элементом этого cp
- PID 9424
.
ОК, я пытаюсь закончить это cp
, используя флаг T
. И бум, это невозможно. cp
является дочерним элементом моего основного Node.js process.pid
6632:
$ taskkill -T -PID 9424
ERROR: The process with PID 1340 (child process of PID 9424) could not be terminated.
Reason: This process can only be terminated forcefully (with /F option).
ERROR: The process with PID 9424 (child process of PID 6632) could not be terminated.
Reason: One or more child processes of this process were still running.
Я могу только убить его с помощью флага F
:
$ taskkill -F -T -PID 9424
SUCCESS: The process with PID 1340 (child process of PID 9424) has been terminated.
SUCCESS: The process with PID 9424 (child process of PID 6632) has been terminated.
Самое разочаровывающее, что Node.js docs не говорит jack ship о том, как справиться с этой проблемой. Они говорят только "да, мы знаем, что проблема существует, и мы просто информируем вас об этом".
Единственный вариант, который я вижу в Windows, - использовать taskkill -F -T -PID 9424
в порожденном процессе:
exec('taskkill -F -T -PID 9424');