Узел предназначен для эффективной обработки операций ввода -вывода, но вы должны знать, что некоторые типы программ не подходят для этого режима. Например, если вы планируете использовать узел для обработки задачи, интенсивной процессора, вы можете заблокировать цикл события и, таким образом, уменьшить ответ программы. Альтернативой является назначение задач с интенсивным процессором для отдельного процесса для обработки, что освобождает цикл событий. Узел позволяет вам создать процесс и использовать этот новый процесс в качестве ребенка своего родительского процесса. В узле дочерний процесс может общаться в двустороннем движении с родительским процессом, и в некоторой степени родительский процесс также может отслеживать и управлять детьми.
Еще один случай, когда вам нужно использовать дочерний процесс, - это когда вы хотите просто выполнить внешнюю команду и позволить узлу получить возвращаемое значение команды. Например, вы можете выполнить команду UNIX, скрипт или другие команды, которые не могут быть непосредственно выполнены в узле.
Эта глава покажет вам, как выполнять внешние команды, создавать и общаться с детьми и прекратить детей. Дело в том, чтобы дать вам представление о том, как выполнить серию задач вне процесса узла. Выполнить внешние команды Когда вам нужно выполнить команду внешней оболочки или исполняемый файл, вы можете использовать модуль child_process, чтобы импортировать его так: Кода -копия выглядит следующим образом: var Child_process = require ('child_process') Затем вы можете использовать функцию EXEC в модуле для выполнения внешних команд: Кода -копия выглядит следующим образом: var exec = child_process.exec; exec (команда, обратный вызов); Первым параметром EXEC является строка команды Shell, которую вы готовите к выполнению, а второй параметр - функция обратного вызова. Эта функция обратного вызова будет вызвана, когда Exec завершит выполнение внешних команд или возникает ошибка. Функция обратного вызова имеет три параметра: ошибка, stdout, stderr, см. Следующий пример: Кода -копия выглядит следующим образом: exec ('ls', function (err, stdout, stderr) { // Примечание переводчика: если вы используете Windows, вы можете изменить его на команду Windows, например, DIR, и я не буду повторять его позже. }); Если возникает ошибка, первый параметр будет экземпляром класса ошибок. Если первый параметр не содержит ошибки, второй параметр stdout будет содержать стандартный выход команды. Последний параметр содержит вывод ошибки, связанный с командой. В листинге 8-1 показан более сложный пример выполнения внешних команд Листинг 8-1: выполнять внешние команды (исходный код: Глава8/01_EXTERNAL_COMMAND.JS) Кода -копия выглядит следующим образом: // Импорт функции EXEC модуля Child_process var exec = require ('child_process'). Exec; // вызов команды "Cat *.js | wc -l" exec ('cat *.js | wc l', function (err, stdout, stderr) {// line 4 // Команда выходит или вызов не удается if (err) { // не удалось начать внешний процесс console.log ('kild_process Exit, код ошибки:', err.code); возвращаться; } } В четвертой строке мы передаем "cat *.js | wc -l" в качестве первого параметра для exec. Вы также можете попробовать любую другую команду, если команда, которую вы использовали в оболочке, в порядке. Затем воспринимайте функцию обратного вызова в качестве второго параметра, который будет вызван, когда возникает ошибка или дочерний процесс заканчивается. Вы также можете передать третий необязательный параметр перед функцией обратного вызова, которая содержит некоторые параметры конфигурации, такие как: Кода -копия выглядит следующим образом: var exec = require ('child_process'). Exec; var options = { Тайм -аут: 1000, KillSignal: 'Sigkill' }; exec ('cat *.js | wc l', опции, функция (err, stdout, stderr) { //… }); Параметры, которые можно использовать: 1.cwd - текущий каталог, вы можете указать текущий рабочий каталог. 2. Кодирование-формат кодирования выходного контента процесса дочернего процесса, значение по умолчанию-«UTF8», то есть кодирование UTF-8. Если выход дочернего процесса не является UTF8, вы можете использовать этот параметр для его установки. Поддерживаемые форматы кодирования: Кода -копия выглядит следующим образом: Асии UTF8 UCS2 База64 Если вы хотите узнать больше об этих форматах кодирования, поддерживаемых узлом, пожалуйста, см. Главу 4 «Используя буфер для обработки, кодирования и декодирования бинарных данных». 1. Timeout - тайм -аут выполнения команды В миллисекундах, по умолчанию равна 0, то есть нет ограничения, и подождите, пока дочерний процесс не закончится. 2.maxbuffer - указывает максимальное количество байтов, разрешенных потоком stdout и потоком Stderr. Если максимальное значение достигнуто, дочерний процесс будет убит. Значение по умолчанию составляет 200*1024. 3. killSignal - конечный сигнал, отправленный дочернему процессу, когда тайм -аут или выходной кэш достигает максимального значения. Значение по умолчанию - «Sigterm», который отправит сигнал прекращения в процесс ребенка. Это обычно используется для окончания процесса упорядоченным образом. При использовании Sigterm Signals процесс также может обрабатывать или переписать поведение по умолчанию сигнального процессора после его получения. Если целевой процесс нуждается в этом, вы можете передавать другие сигналы одновременно (например, SIGUSR1). Вы также можете отправить сигнал Sigkill, который будет обрабатываться операционной системой и заставить дочерний процесс немедленно прекратить прекращение, чтобы не выполняться любая операция очистки дочернего процесса. Если вы хотите дополнительно контролировать конец процесса, вы можете использовать команду Child_process.spawn, которая будет введена позже. 1.EVN - Указывает переменную среды, передаваемую в процесс ребенка. По умолчанию ноль, что означает, что дочерний процесс будет наследовать переменные среды всех родительских процессов до его создания. Примечание. Используя опцию KillSignal, вы можете отправлять сигналы в целевой процесс в качестве строки. В узле сигнал существует как строка. Вот список сигналов UNIX и соответствующих операций по умолчанию: Возможно, вы захотите предоставить набор расширяемых переменных родительской среды для детского процесса. Если вы напрямую измените объект Process.ENV, вы измените переменные среды всех модулей в процессе узла, что вызовет множество проблем. Альтернатива состоит в том, чтобы создать новый объект и скопировать все параметры в Process.env, см. Пример 8-2: Список 8-2: Используйте переменные параметризованной среды для выполнения команд (исходный код: Глава8/02_ENV_VARS_AUGMANT.JS) Кода -копия выглядит следующим образом: var env = process.env, вариант, envcopy = {}, exec = require ('child_process'). Exec; // Copy Process.env в envcopy для (vaname in ev) { envcopy [varname] = env [varname]; } // Установить некоторые пользовательские переменные envcopy ['custom env var1'] = 'некоторое значение'; envcopy ['custom env var2'] = 'какое -то другое значение'; // выполнять команды с использованием process.env и пользовательских переменных exec ('ls la', {env: envcopy}, function (err, stdout, stderr) { if (err) {throw err; } console.log ('stdout:', stdout); console.log ('stderr:', stderr); } В приведенном выше примере создается переменная Envcopy для сохранения переменных среды. Сначала он копирует переменные среды процесса узла из процесса. Помните, что переменные среды передаются между процессами через операционную систему, и все типы значений переменной среды поступают в процесс ребенка в качестве строк. Например, если родительский процесс принимает число 123 в качестве переменной среды, дочерний процесс получит «123» в качестве строки. В следующем примере в одном и том же каталоге будут созданы два сценария узлов: Parent.js и Child.js. Первый сценарий позвонит второму. Давайте создадим эти два файла: Листинг 8-3: родительские процессы устанавливают переменные среды (глава8/03_environment_number_parent.js) Кода -копия выглядит следующим образом: var exec = require ('child_process'). Exec; exec ('node Child.js', {env: {number: 123}}, function (err, stdout, stderr) { if (err) {throw err; } console.log ('stdout:/n', stdout); console.log ('stderr:/n', stderr); }); Сохраните этот код в parent.js. Ниже приведен исходный код дочернего процесса и сохранить их до child.js (см. Пример 8-4) Пример 8-4: переменные среды подпроцессов (глава8/04_environment_number_child.js) Кода -копия выглядит следующим образом: var number = process.env.number; console.log (typeof (номер)); // → "строка" число = parseint (номер, 10); console.log (typeof (номер)); // → "номер" После того, как вы сохраните этот файл в качестве child.js, вы можете запустить следующую команду в этом каталоге: Кода -копия выглядит следующим образом: $ node parent.js Вы увидите следующий вывод: Кода -копия выглядит следующим образом: sdtou: нить число Stderr: Как вы можете видеть, хотя родительский процесс передает числовую переменную среды, дочерний процесс получает ее как строку (см. Вторую строку вывода), а в третьей строке вы разбираете строку в число. Генерировать дочерний процесс Как вы можете видеть, вы можете использовать функцию child_process.exec (), чтобы запустить внешний процесс и вызвать функцию обратного вызова в конце процесса. Это очень просто в использовании, но есть некоторые недостатки: 1. В дополнение к использованию параметров командной строки и переменных среды, exec () не может общаться с дочерними процессами. 2. Вывод дочернего процесса кэшируется, так что вы не можете его транслировать, он может исчерпывать память К счастью, модуль Node Child_process позволяет лучше контролировать начало, остановку и другие обычные операции детских процессов. Вы можете начать новый дочерний процесс в приложении. Узел предоставляет двухсторонний канал связи, позволяя родителям и дочерним процессам отправлять и получать строковые данные друг от друга. Родительский процесс также может иметь некоторые управленческие операции для детского процесса, отправлять сигналы в процесс ребенка и насильственно закрыть дочерний процесс. Создать дочерний процесс Вы можете использовать функцию child_process.spawn для создания нового дочернего процесса, см. Пример 8-5: Пример 8-5: генерировать дочерний процесс. (Глава8/05_spawning_child.js) Кода -копия выглядит следующим образом: // Импорт функции появления модуля child_process var spawn = require ('child_process'). Spawn; // генерировать дочерние процессы, используемые для выполнения команды «tail -f /var/log/system.log» var Child = spawn ('hail', ['-f', '/var/log/system.log']); Приведенный выше код генерирует дочерний процесс, используемый для выполнения команд хвоста и принимает «-f» и «/bar/log/system.log» в качестве параметров. Команда Tail будет отслеживать файл /var/log/system.og (если присутствует), а затем выведет все добавленные новые данные в стандартный выходной поток Stdout. Функция Spawn возвращает объект PhideProcess, который является объектом Pointer, инкапсулирующим интерфейс доступа реального процесса. В этом примере мы назначаем этот новый дескриптор переменной, называемой ребенком. Слушать данные из детских процессов Любая дочерняя обработка, содержащая атрибут stdout, примет стандартный выходной процесс дочернего процесса в качестве объекта потока. Вы можете связать событие Data на этом объекте потока, чтобы всякий раз, когда доступен блок данных, будет вызвана соответствующая функция обратного вызова, см. Следующий пример: Кода -копия выглядит следующим образом: // распечатать вывод дочернего процесса на консоли child.stdout.on ('data', function (data) { console.log ('hail output:' + data); }); Всякий раз, когда дочерний процесс выводит данные в стандартный выходной stdout, родительский процесс уведомляется и печатает данные в консоли. В дополнение к стандартному выходу, процесс имеет еще один выходной поток по умолчанию: стандартный поток ошибок, который обычно используется для вывода информации об ошибках. В этом примере, если файл /var/log/system.log не существует, процесс хвоста выведет сообщение, аналогичное следующему: «/var/log/system.log: нет такого файла или каталога». Прислушиваясь к потоку STDERR, родительский процесс будет уведомлен при возникновении этой ошибки. Родительский процесс может прислушиваться к стандартным потокам ошибок, как это: Кода -копия выглядит следующим образом: child.stderr.on ('data', function (data) { console.log ('upture ошибка хвоста:', data); }); Свойство Stderr, как и Stdout, также является потоком только для чтения. Всякий раз, когда дочерний процесс выводит данные в стандартный поток ошибок, родительский процесс будет уведомлен и выходные данные. Отправить данные в процесс ребенка В дополнение к получению данных из выходного потока дочернего процесса, родительский процесс также может записать данные в стандартный ввод дочернего процесса через свойство childpoces.stdin для отправки данных в процесс ребенка. Дочерний процесс может прислушиваться к стандартным входным данным через поток только для чтения. Пример 8-6 создаст программу, содержащую следующие функции: 1.+1 Приложение: простое приложение, которое может получать целые числа от стандартного ввода, затем добавить их, а затем выводить результат после добавления в стандартный выходной поток. В качестве простой вычислительной службы это приложение имитирует процесс узла как внешнюю службу, которая может выполнять определенные задачи. 2. Проверьте клиент приложения +1, отправьте случайное целое число, а затем выведите результат. Используется для демонстрации того, как процесс узла генерирует дочерний процесс, а затем позволяет ему выполнять определенные задачи. Используйте следующий код в примере 8-6 для создания файла с именем plus_one.js: Пример 8-6: +1 Приложение (Глава8/06_PLUS_ONE.JS) Кода -копия выглядит следующим образом: // восстановить стандартный входной поток, который приостановился по умолчанию process.stdin.resume (); process.stdin.on ('data', function (data) { var номер; пытаться { // Проанализировать входные данные в целое число number = parseint (data.toString (), 10); // +1 число += 1; // результат вывода process.stdout.write (номер + "/n"); } catch (err) { process.stderr.write (err.message + "/n"); } }); В приведенном выше коде мы ждем данных из стандартного входного потока Stdin. Всякий раз, когда доступны данные, мы предполагаем, что это целое число, и разрабатывать их в целочисленную переменную, затем добавляем 1 и выведет результат в стандартный выходной поток. Вы можете запустить эту программу через следующую команду: Кода -копия выглядит следующим образом: $ node plus_one.js После запуска программа начинает ждать ввода. Если вы введете целое число и нажмите Enter, вы увидите номер после добавления 1 для отображения на экране. Вы можете выйти из программы, нажав CTRL-C. Тестовый клиент Теперь вы хотите создать процесс узла для использования вычислительных служб, предоставляемых предыдущим «Приложение +1». Сначала создайте файл с именем plus_one_test.js, см. Пример 8-7: Пример 8-7: тест +1 приложение (глава8/07_plus_one_test.js) Кода -копия выглядит следующим образом: var spawn = require ('child_process'). Spawn; // генерировать дочерний процесс для выполнения приложения +1 var Child = spawn ('node', ['plus_one.js']); // вызовать функцию каждую секунду setInterval (function () { // Создать случайное число меньше 10.000 var number = math.floor (math.random () * 10000); // Отправить этот номер в детский процесс: child.stdin.write (номер + "/n"); // Получите ответ от дочернего процесса и распечатайте его: child.stdout.once ('data', function (data) { console.log («Ребенок ответил» + number + 'с:' + data); }); }, 1000); child.stderr.on ('data', function (data) { process.stdout.write (data); }); От первой строки до четвертой строки начинается дочерний процесс для запуска «приложения +1», и затем следующие операции выполняются каждую секунду, используя функцию SetInterval: 1. Создайте новое случайное число менее 10000 2. Передайте этот номер в качестве строки дочернему процессу 3. Подождите, пока дочерний процесс ответит на строку 4. Поскольку вы хотите получать только 1 номер за раз, вам нужно использовать Child.stdout.once вместо ребенка.stdout.on. Если последнее используется, функция обратного вызова события данных будет зарегистрирована каждые 1 секунду. Каждая зарегистрированная функция обратного вызова будет выполнена, когда stdout дочернего процесса получает данные. Таким образом, вы обнаружите, что один и тот же результат вычисления будет выходить несколько раз. Такое поведение явно неверно. Получить уведомления, когда детский процесс выходит Когда детский процесс выйдет, событие выхода будет уволено. Пример 8-8 показывает, как это слушать: Пример 8-8: Слушайте событие выхода дочернего процесса (глава8/09_Listen_child_exit.js) Кода -копия выглядит следующим образом: var spawn = require ('child_process'). Spawn; // генерировать дочерний процесс для выполнения команды "ls -la" var Child = spawn ('ls', ['-la']); child.stdout.on ('data', function (data) { console.log ('данные от ребенка:' + data); }); // Когда дочерний процесс выходит: <strong> child.on ('exit', function (code) { console.log («Детский процесс прекращен с кодом» + код); }); </strong> В последних нескольких строках черного кода родительский процесс использует событие выхода дочернего процесса для прослушивания события выхода. Когда событие происходит, консоль отображает соответствующий вывод. Код выхода дочернего процесса будет передаваться в функцию обратного вызова в качестве первого параметра. Некоторые программы используют код выхода без 0 для представления определенного состояния сбоя. Например, если вы попытаетесь выполнить команду «ls al click filename.txt», но текущий каталог не имеет этого файла, вы получите код выхода со значением 1, см. Пример 8-9: Пример 8-9: Получите код выхода дочернего процесса (глава8/10_CHILD_EXIT_CODE.JS) Кода -копия выглядит следующим образом: var spawn = require ('child_process'). Spawn; // генерировать дочерний процесс и выполнить команду "LS DOS_NOT_EXIST.TXT" var Child = spawn ('ls', ['dos_not_exist.txt']); // Когда дочерний процесс выходит child.on ('exit', function (code) { console.log («Детский процесс прекращен с кодом» + код); }); В этом примере событие выхода запускает функцию обратного вызова и передает код выхода дочернего процесса в качестве первого параметра. Если дочерний процесс выходит из аномально из-за убийства сигналом, соответствующий код сигнала будет передаваться функции обратного вызова в качестве второго параметра, как в примере 8-10: Список 8-10: Получите сигнал выхода дочернего процесса (глава8/11_CHILD_EXIT_SIGNAL.JS) Кода -копия выглядит следующим образом: var spawn = require ('child_process'). Spawn; // генерировать дочерний процесс и запустить команду "Sleep 10" var Child = Spawn ('Sleep', ['10']); settimeout (function () { Child.kill (); }, 1000); child.on ('exit', function (код, сигнал) { if (code) { console.log («Детский процесс прекращен с кодом» + код); } else if (сигнал) { console.log («Детский процесс завершился из -за сигнала» + сигнал); } }); В этом примере дочерний процесс начинает выполнять сон 10 секунд, но сигнал Sigkill отправляется на дочерний процесс до 10 секунд, что приведет к следующему выходу: Кода -копия выглядит следующим образом: Детский процесс прекращен из -за сигнала Отправить сигнал и убить процесс В этом разделе вы узнаете, как использовать сигналы для управления подпроцессами. Сигналы - это простой способ для родительского процесса общаться с детьми и даже убивать детей. Различные сигнальные коды представляют собой разные значения, и есть много сигналов, некоторые из которых являются наиболее распространенными для убийства процессов. Если процесс получает сигнал, который он не знает, как обрабатывать, программа будет прервана исключением. Некоторые сигналы обрабатываются подпроцессами, в то время как другие могут обрабатываться только операционной системой. Как правило, вы можете использовать метод ребенка. КИЛЛ для отправки сигнала в процесс ребенка и по умолчанию отправлять сигнал Sigterm: Кода -копия выглядит следующим образом: var spawn = require ('child_process'). Spawn; var Child = Spawn ('Sleep', ['10']); settimeout (function () { Child.kill (); }, 1000); Вы также можете отправить определенный сигнал, передавая строку, идентифицирующую сигнал как единственный параметр метода убийства: Кода -копия выглядит следующим образом: Child.kill ('sigusr2'); Следует отметить, что, хотя название этого метода является убийством, отправленный сигнал не обязательно убивает дочерний процесс. Если ребенок обрабатывал сигнал, поведение сигнала по умолчанию было перезаписано. Подпроцессы, записанные в узле, могут переписать определение процессора сигнала, как следующее: Кода -копия выглядит следующим образом: process.on ('sigusr2', function () { console.log ('Получил сигнал SIGUSR2'); }); Теперь вы определили сигнальный процессор SIGUSR2. Когда ваш процесс снова получит сигнал SIGUSR2, он не будет убит, но вместо этого выводит предложение «Получил сигнал SIGUSR2». Используя этот механизм, вы можете спроектировать простой способ общения с дочерним процессом или даже командовать им. Хотя он не так богат, как использование стандартного ввода, этот метод намного проще. краткое содержание В этой главе мы научились использовать метод child_process.exec для выполнения внешних команд. Таким образом, мы можем передать параметры дочернему процессу вместо использования параметров командной строки, но вместо этого определять переменные среды. Я также научился генерировать детские процессы, вызывая метод child_process.spawn для вызова внешних команд. Таким образом, вы можете использовать входные потоки и выходные потоки для общения с детскими процессами или использовать сигналы для общения с детскими процессами и убивать процессы.