端口 | 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
名选项允许开发人员访问in
和out
被二郎神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)
返回关于端口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
由VM打开。
有关更多信息和选项列表,请参阅:erlang.open_port/2
。
编译器插入。