Spec-Zone.ru › Elixir 1.4

Порт

Функции для взаимодействия с внешним миром через порты.

Порты предоставляют механизм для запуска процессов операционной системы, внешних по отношению к виртуальной машине Erlang, и взаимодействия с ними посредством передачи сообщений.

Пример

iex> port = Port.open({:spawn, "cat"}, [:binary])
iex> send port, {self(), {:command, "hello"}}
iex> send port, {self(), {:command, "world"}}
iex> flush()
{#Port<0.1444>, {:data, "hello"}}
{#Port<0.1444>, {:data, "world"}}
iex> send port, {self(), :close}
:ok
iex> flush()
{#Port<0.1464>, :closed}
:ok

В примере выше мы создали новый порт, который выполняет программу cat. cat — это программа, доступная в системах UNIX, которая получает данные из нескольких входов и конкатенирует их в выходе.

После создания порта мы отправили ему две команды в виде сообщений, используя Kernel.send/2. Первая команда имеет двоичную нагрузку «hello», а вторая — «world».

После отправки этих двух сообщений мы вызвали помощника IEx flush(), который напечатал все полученные от порта сообщения, в этом случае мы получили обратно «hello» и «world». Обратите внимание, что сообщения находятся в двоичном формате, потому что мы передали :binary опцию при открытии порта в Port.open/2. Без такой опции она вернула бы список байтов.

После завершения всех действий мы закрыли порт.

Elixir предоставляет много удобств для работы с портами и некоторые недостатки. Мы рассмотрим их ниже.

API сообщений и функций

Существует два API для работы с портами. Это может быть асинхронный обмен сообщениями, как в примере выше, или вызов функций данного модуля.

Ниже перечислены сообщения, поддерживаемые портами, и соответствующие им API функций:

  • {pid, {:command, binary}} — отправляет заданные данные в порт. См. command/3.

  • {pid, :close} — закрывает порт. Если порт еще не закрыт, порт ответит сообщением {port, :closed} после того, как очистит свои буферы и эффективно закроется. См. close/1.

  • {pid, {:connect, new_pid}} — устанавливает new_pid в качестве нового владельца порта. После открытия порта порт связывается и подключается к процессу вызова, и взаимодействие с портом происходит только через подключенный процесс. Это сообщение делает new_pid новыми подключенными процессами. Если порт не завершен, порт ответит старому владельцу сообщением {port, :connected}. См. connect/2.

В свою очередь, порт отправит подключенному процессу следующие сообщения:

  • {port, {:port, data}} — данные, отправленные портом
  • {port, :closed} — ответ на сообщение {pid, :close}
  • {port, :connected} — ответ на сообщение {pid, {:connect, new_pid}}
  • {:EXIT, port, reason} — сигналы завершения в случае сбоя порта и если процесс-владелец обрабатывает завершения

Механизмы открытия

Порт можно открыть с помощью четырех основных механизмов.

В качестве краткого резюме, предпочтительнее использовать :spawn и :spawn_executable опции, упомянутые ниже. Остальные две опции, :spawn_driver и :fd предназначены для расширенного использования внутри виртуальной машины. Также рассмотрите использование System.cmd/3, если все, что вам нужно, это выполнить программу и получить ее возвращаемое значение.

spawn

Кортеж :spawn получает двоичные данные, которые будут выполнены как полное вызов. Например, мы можем использовать его для прямого вызова «echo hello»:

iex> port = Port.open({:spawn, "echo oops"}, [:binary])
iex> flush()
{#Port<0.1444>, {:data, "oops\n"}}

:spawn извлечет имя программы из аргумента и пройдется по переменной среды вашей операционной системы $PATH в поисках соответствующей программы.

Хотя это удобно, это означает, что невозможно вызвать исполняемый файл, имеющий пробелы в своем имени или в любом из своих аргументов. По этим причинам чаще всего предпочтительнее выполнять :spawn_executable.

spawn_executable

Spawn executable — это более ограниченная и явная версия spawn. Она ожидает полные пути к исполняемому файлу, который вы хотите выполнить. Если они находятся в вашей $PATH, их можно получить, вызвав System.find_executable/1:

iex> path = System.find_executable("echo")
iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
iex> flush()
{#Port<0.1380>, {:data, "hello world\n"}}

При использовании :spawn_executable, список аргументов можно передать через опцию :args, как сделано выше. Полный список опций см. в документации для функции Erlang :erlang.open_port/2.

spawn_driver

Spawn driver используется для запуска драйверов портов, которые представляют собой программы, написанные на C, реализующие определенные протоколы обмена, и динамически подключающиеся к виртуальной машине Erlang. Драйверы портов — это продвинутая тема и один из механизмов интеграции кода C наряду с NIF. Для получения дополнительной информации, пожалуйста, ознакомьтесь с документацией Erlang здесь.

fd

Опция имени :fd позволяет разработчикам получить доступ к in и out дескрипторам файлов, используемым виртуальной машиной Erlang. Вы будете использовать их только в том случае, если переопределяете ядро системы выполнения, например, процессы :user и :shell.

Процессы-зомби

Порт можно закрыть с помощью функции close/1 или отправив сообщение {pid, :close}. Однако, если виртуальная машина аварийно завершит работу, долго выполняемая программа, запущенная портом, будет иметь закрытыми каналы stdin и stdout, но не будет автоматически завершена.

В то время как большинство инструментов командной строки UNIX завершат работу после закрытия каналов связи, не все приложения командной строки сделают это. Мы рекомендуем плавное завершение, обнаруживая, был ли закрыт stdin/stdout, но мы не всегда контролируем, как завершают работу сторонние программы. В этих случаях вы можете обернуть приложение в скрипт, который проверяет stdin. Вот такой скрипт на bash:

#!/bin/sh
"$@"
pid=$!
while read line ; do
  :
done
kill -KILL $pid

Теперь вместо:

Port.open({:spawn_executable, "/path/to/program"},
          [args: ["a", "b", "c"]])

Вы можете вызвать:

Port.open({:spawn_executable, "/path/to/wrapper"},
          [args: ["/path/to/program", "a", "b", "c"]])

Резюме

Типы

name()

Функции

close(port)

Закрывает port

command(port, data, options \\ [])

Отправляет data в драйвер порта port

connect(port, pid)

Связывает идентификатор port с pid

info(port)

Возвращает информацию о port или nil если порт закрыт

info(port, spec)

Возвращает информацию о port или nil если порт закрыт

list()

Возвращает список всех портов в текущем узле

open(name, settings)

Открывает порт, задавая кортеж name и список options

Типы

name()

name() ::
  {:spawn, charlist() | binary()} |
  {:spawn_driver, charlist() | binary()} |
  {:spawn_executable, charlist() | atom()} |
  {:fd, non_neg_integer(), non_neg_integer()}

Функции

close(port)

close(port()) :: true

Закрывает port.

Для получения дополнительной информации, см. :erlang.port_close/1.

Встраивается компилятором.

command(port, data, options \\ [])

command(port(), iodata(), [:force | :nosuspend]) :: boolean()

Отправляет data в драйвер порта port.

Для получения дополнительной информации, см. :erlang.port_command/2.

Встраивается компилятором.

connect(port, pid)

connect(port(), pid()) :: true

Связывает идентификатор port с pid.

Для получения дополнительной информации, см. :erlang.port_connect/2.

Встраивается компилятором.

info(port)

Возвращает информацию о port или nil если порт закрыт.

Для получения дополнительной информации, см. :erlang.port_info/1.

info(port, spec)

info(port(), atom()) :: {atom(), term()} | nil

Возвращает информацию о port или nil если порт закрыт.

Для получения дополнительной информации, см. :erlang.port_info/2.

list()

list() :: [port()]

Возвращает список всех портов в текущем узле.

Встраивается компилятором.

open(name, settings)

open(name(), list()) :: port()

Открывает порт, задавая кортеж name и список options.

В документации модуля выше содержатся документация и примеры для поддерживаемых значений name, обобщенные ниже:

  • {:spawn, command} — запускает внешнюю программу. command должен содержать имя программы и, необязательно, список аргументов, разделенных пробелами. При передаче программ или аргументов с пробелами в их имени, используйте следующую опцию.
  • {:spawn_executable, filename} — запускает исполняемый файл, указанный абсолютным путем filename. Аргументы можно передать через опцию :args.
  • {:spawn_driver, command} — запускает так называемые драйверы портов.
  • {:fd, fd_in, fd_out} — получает доступ к дескрипторам файлов fd_in и fd_out открытым виртуальной машиной.

Для получения дополнительной информации и списка опций см. :erlang.open_port/2.

Встраивается компилятором.

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.4.5/Port.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API