檢視原始碼 驅動程式
本節簡要概述如何撰寫高效的驅動程式。
假設您對驅動程式有良好的理解。
驅動程式與並行
執行時系統在驅動程式中執行任何程式碼之前,總是會先取得鎖定。
預設情況下,該鎖定是在驅動程式層級,也就是說,如果有多個埠已開啟到同一個驅動程式,則一次只能執行一個埠的程式碼。
可以將驅動程式設定為每個埠都擁有一個鎖定。
如果驅動程式以函數式的方式使用(也就是說,不保留任何狀態,僅執行一些繁重的計算並返回結果),則可以預先開啟多個具有已註冊名稱的埠,並且可以根據排程器 ID 選擇要使用的埠,如下所示:
-define(PORT_NAMES(),
{some_driver_01, some_driver_02, some_driver_03, some_driver_04,
some_driver_05, some_driver_06, some_driver_07, some_driver_08,
some_driver_09, some_driver_10, some_driver_11, some_driver_12,
some_driver_13, some_driver_14, some_driver_15, some_driver_16}).
client_port() ->
element(erlang:system_info(scheduler_id) rem tuple_size(?PORT_NAMES()) + 1,
?PORT_NAMES()).
只要排程器不超過 16 個,就不會發生驅動程式埠鎖定的鎖定爭用。
避免在呼叫驅動程式時複製二進位資料
基本上有兩種方法可以避免複製傳送到驅動程式的二進位資料
如果
port_control/3
的Data
引數是二進位資料,則會將指向二進位資料內容的指標傳遞給驅動程式,並且不會複製二進位資料。如果Data
引數是 iolist(二進位資料和列表的列表),則會複製 iolist 中的所有二進位資料。因此,如果您想在不複製二進位資料的情況下,將預先存在的二進位資料和一些額外的資料傳送到驅動程式,則必須呼叫
port_control/3
兩次;一次傳遞二進位資料,一次傳遞額外資料。但是,只有在只有一個程序與埠通訊時才有效(因為否則另一個程序可能會在呼叫之間呼叫驅動程式)。在驅動程式中實作
outputv
回呼(而不是output
回呼)。如果驅動程式具有outputv
回呼,則在port_command/2
的Data
引數中,以 iolist 傳遞的 refc 二進位資料將會以參考的形式傳遞給驅動程式。
從驅動程式返回小型二進位資料
執行時系統可以將最多 64 個位元組的二進位資料表示為堆積二進位資料。它們在訊息中傳送時總是會被複製,但如果它們不被傳送到另一個程序,則需要的記憶體更少,並且垃圾回收的成本更低。
如果您知道您返回的二進位資料總是小型資料,建議您使用不需要預先配置二進位資料的驅動程式 API 呼叫,例如 driver_output()
或 erl_drv_output_term()
,並使用 ERL_DRV_BUF2BINARY
格式,以允許執行時系統建構堆積二進位資料。
從驅動程式返回大型二進位資料而不複製
為了避免在將大型二進位資料從驅動程式傳送到 Erlang 程序或從 Erlang 程序返回時複製資料,驅動程式必須先配置二進位資料,然後以某種方式將其傳送到 Erlang 程序。
使用 driver_alloc_binary()
來配置二進位資料。
有幾種方法可以傳送使用 driver_alloc_binary()
建立的二進位資料
- 從
control
回呼中,如果已使用旗標值PORT_CONTROL_FLAG_BINARY
呼叫set_port_control_flags()
,則可以返回二進位資料。 - 可以使用
driver_output_binary()
傳送單個二進位資料。 - 使用
erl_drv_output_term()
或erl_drv_send_term()
,二進位資料可以包含在 Erlang 項中。