Невозможно убить дочерний процесс в 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');