非常教程

Elixir 1.5参考手册

端口 | Port

端口 | Port

通过端口与外部世界交互的函数。

端口提供了一种机制来启动ErlangVM外部的操作系统进程,并通过消息传递与它们通信。

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, {:data, data}} - 由端口发送的数据
  • {port, :closed}- 回复{pid, :close}邮件
  • {port, :connected}- 回复{pid, {:connect, new_pid}}邮件
  • {:EXIT, port, reason} - 在端口崩溃的情况下退出信号。如果原因不是:normal,则只有在所有者进程陷入退出时才会收到此消息

开放机制

港口可以通过四个主要机制打开。

作为一个简短的总结,更喜欢使用下面提到的:spawn:spawn_executable选项。另外两种选择,:spawn_driver并且:fd用于VM内的高级使用。System.cmd/3如果您只想执行一个程序并检索其返回值,请考虑使用。

: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可执行文件是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驱动程序用于启动端口驱动程序,它是用C语言编写的程序,用于实现特定的通信协议并动态链接到Erlang虚拟机。端口驱动程序是一个高级主题,也是集成C代码和NIF一起的机制之一。欲了解更多信息,请查看Erlang文档。

fd

:fd名选项允许开发人员访问inout被二郎神VM使用的文件描述符。只有在重新实现运行系统的核心部分时,才会使用这些部分,例如: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)

返回关于端口portnil端口是否关闭的信息

info(port, spec)

返回关于端口portnil端口是否关闭的信息

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)

返回关于端口portnil端口是否关闭的信息。

有关更多信息,请参阅:erlang.port_info/1

info(port, spec)

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

返回关于端口portnil端口是否关闭的信息。

有关更多信息,请参阅: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_infd_out由VM打开。

有关更多信息和选项列表,请参阅:erlang.open_port/2

编译器插入。

端口 | Port相关

Elixir 1.5

Elixir 基于 Erlang 虚拟机的函数式、面向并行,是一种较好的编程语言。它以 Erlang 为基础,支持分布式、高容错、实时应用程序的开发。

主页 https://elixir-lang.org/
源码 https://github.com/elixir-lang/elixir
版本 1.5
发布版本 1.5.2