檢視原始碼 Port (Elixir v1.16.2)
透過埠與外部世界互動的函式。
埠提供一種機制,可以啟動 Erlang VM 外部的作業系統程序,並透過訊息傳遞與它們通訊。
範例
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 類作業系統中可用的程式,它會接收來自多個輸入的資料,並將它們串接在輸出中。
在建立埠之後,我們使用 send/2
以訊息的形式傳送了兩個指令給它。第一個指令具有「hello」的二進位酬載,第二個具有「world」。
在傳送這兩個訊息之後,我們呼叫 IEx 輔助函式 flush()
,它會列印從埠接收到的所有訊息,在這個案例中,我們收到了「hello」和「world」。請注意,訊息是二進位的,因為我們在 Port.open/2
中開啟埠時傳遞了 :binary
選項。如果不使用這個選項,它會產生一個位元組清單。
在完成所有動作後,我們關閉了埠。
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
:spawn
元組會接收一個二進位檔,該二進位檔將作為完整呼叫執行。例如,我們可以使用它直接呼叫「echo hello」
iex> port = Port.open({:spawn, "echo hello"}, [:binary])
iex> flush()
{#Port<0.1444>, {:data, "hello\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
的文件。
fd
:fd
名稱選項允許開發人員存取 Erlang VM 使用的 in
和 out
檔案描述子。只有在重新實作執行時期系統的核心部分(例如 :user
和 :shell
程序)時,您才會使用它們。
僵屍作業系統程序
可透過 close/1
函數或傳送 {pid, :close}
訊息來關閉埠。但是,如果 VM 發生故障,埠啟動的長期執行程式將關閉其 stdin 和 stdout 通道,但它不會自動終止。
雖然大多數 Unix 命令列工具會在其通訊通道關閉後退出,但並非所有命令列應用程式都會這麼做。您可以透過啟動埠,然後關閉 VM 並檢查您的作業系統以查看埠程序是否仍在執行,來輕鬆檢查這一點。
雖然我們鼓勵透過偵測 stdin/stdout 是否已關閉來優雅地終止,但我們並不總是能控制第三方軟體如何終止。在這種情況下,您可以將應用程式包裝在檢查 stdin 的指令碼中。以下是一個已驗證可在 bash shell 上執行的指令碼
#!/usr/bin/env bash
# Start the program in the background
exec "$@" &
pid1=$!
# Silence warnings from here on
exec >/dev/null 2>&1
# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
while read; do :; done
kill -KILL $pid1
) &
pid2=$!
# Clean up
wait $pid1
ret=$?
kill -KILL $pid2
exit $ret
請注意,上述程式會劫持 stdin,因此您將無法透過 stdin 與底層軟體通訊(好處是,通常會在 stdin 關閉時終止從 stdin 讀取的軟體)。
現在,請改為
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"]
)
摘要
函數
關閉 port
。
將 data
傳送至埠驅動程式 port
。
將 port
識別碼與 pid
關聯。
取消監控由提供的 reference
識別的監視器。
傳回有關 port
的資訊(或在埠已關閉時傳回 nil
)。
傳回 port
內特定欄位的資訊(或在埠已關閉時傳回 nil
)。
傳回目前節點中所有埠的清單。
開始從呼叫處理程序監控指定的 port
。
開啟一個埠,提供一個 name
元組和一個 options
清單。
類型
@type name() :: {:spawn, charlist() | binary()} | {:spawn_driver, charlist() | binary()} | {:spawn_executable, :file.name_all()} | {:fd, non_neg_integer(), non_neg_integer()}
函數
@spec close(port()) :: true
關閉 port
。
如需更多資訊,請參閱 :erlang.port_close/1
。
由編譯器內嵌。
將 data
傳送至埠驅動程式 port
。
如需更多資訊,請參閱 :erlang.port_command/3
。
由編譯器內嵌。
將 port
識別碼與 pid
關聯。
如需更多資訊,請參閱 :erlang.port_connect/2
。
由編譯器內嵌。
取消監控由提供的 reference
識別的監視器。
如果 monitor_ref
是呼叫處理程序透過呼叫 monitor/1
取得的參考,則會關閉該監控。如果監控已關閉,則不會有任何動作。
有關更多資訊,請參閱 :erlang.demonitor/2
。
由編譯器內嵌。
傳回有關 port
的資訊(或在埠已關閉時傳回 nil
)。
有關更多資訊,請參閱 :erlang.port_info/1
。
傳回 port
內特定欄位的資訊(或在埠已關閉時傳回 nil
)。
有關更多資訊,請參閱 :erlang.port_info/2
。
@spec list() :: [port()]
傳回目前節點中所有埠的清單。
由編譯器內嵌。
開始從呼叫處理程序監控指定的 port
。
一旦受監控的埠處理程序死亡,訊息將以以下形式傳遞給監控處理程序
{:DOWN, ref, :port, object, reason}
其中
ref
是此函式傳回的監控參考;object
是受監控的port
(以埠 ID 監控時)或{name, node}
(以埠名稱監控時);reason
是退出原因。
有關更多資訊,請參閱 :erlang.monitor/2
。
由編譯器內嵌。
開啟一個埠,提供一個 name
元組和一個 options
清單。
上述模組文件包含受支援 name
值的文件和範例,摘要如下
{:spawn, command}
- 執行外部程式。command
必須包含程式名稱,並可選擇包含以空格分隔的引數清單。如果傳遞名稱中帶有空格的程式或引數,請使用下一個選項。{:spawn_executable, filename}
- 執行由絕對檔案名稱filename
給定的可執行檔。可透過:args
選項傳遞引數。{:spawn_driver, command}
- 產生所謂的埠驅動程式。{:fd, fd_in, fd_out}
- 存取由 VM 開啟的檔案描述子fd_in
和fd_out
。
有關更多資訊和選項清單,請參閱 :erlang.open_port/2
。
由編譯器內嵌。