檢視原始碼 erl_driver
Erlang 驅動程式的 API 函式。
描述
Erlang 驅動程式是一個函式庫,包含一組原生驅動程式回呼函式,當特定事件發生時,Erlang 虛擬機器會呼叫這些函式。一個驅動程式可以有多個實例,每個實例都與一個 Erlang 連接埠相關聯。
警告
使用此功能時請務必格外小心。
驅動程式回呼函式會作為 VM 原生程式碼的直接擴展執行。執行並非在安全環境中進行。VM *無法* 提供與執行 Erlang 程式碼時相同的服務,例如搶佔式排程或記憶體保護。如果驅動程式回呼函式行為不佳,整個 VM 將會行為異常。
- 驅動程式回呼發生當機將會導致整個 VM 當機。
- 錯誤實作的驅動程式回呼可能會導致 VM 內部狀態不一致,這可能會導致 VM 當機,或在呼叫驅動程式回呼函式之後的任何時間點發生 VM 的其他異常行為。
- 在返回之前進行長時間工作的驅動程式回呼會降低 VM 的回應速度,並可能導致各種奇怪的行為。此類奇怪的行為包括但不限於極端的記憶體使用量以及排程器之間的不良負載平衡。由於長時間工作而可能發生的奇怪行為也可能因 Erlang/OTP 版本而異。
從 ERTS 5.5.3 開始,驅動程式介面已擴充(請參閱extended marker
)。擴充介面引入了版本管理,在驅動程式初始化時將功能旗標(請參閱driver_flags
)傳遞給執行階段系統的可能性,以及一些新的驅動程式 API 函式。
注意
從 ERTS 5.9 開始,舊的驅動程式必須重新編譯並使用擴充介面。它們還必須調整為具備 64 位元能力的驅動程式介面。
驅動程式使用在 erl_driver.h
中宣告的 API 函式回呼模擬器。它們用於從驅動程式輸出資料、使用計時器等等。
每個驅動程式實例都與一個連接埠相關聯。每個連接埠都有一個連接埠擁有者處理序。與連接埠的通訊通常透過連接埠擁有者處理序進行。大多數函式都將 port
控制代碼作為引數。這會識別驅動程式實例。請注意,此連接埠控制代碼必須由驅動程式儲存,從模擬器呼叫驅動程式時不會提供(請參閱driver_entry
)。
某些函式採用類型為 ErlDrvBinary
的參數,即驅動程式二進位資料。它應由呼叫者配置和釋放。直接使用二進位資料可避免額外複製資料。
許多輸出函式都有一個「標頭緩衝區」,其中包含 hbuf
和 hlen
參數。此緩衝區會以清單的形式,在二進位資料(或清單,取決於連接埠模式)之前傳送。這在比對從連接埠收到的訊息時很方便。(雖然在最新的 Erlang 版本中,有二進位語法,可讓您比對二進位資料的開頭。)
驅動程式會在驅動程式層級或連接埠層級(驅動程式實例層級)鎖定。依預設,將使用驅動程式層級鎖定,也就是說,一次只有一個模擬器執行緒會在驅動程式中執行程式碼。如果使用連接埠層級鎖定,多個模擬器執行緒可以同時在驅動程式中執行程式碼。但是,一次只有一個執行緒會呼叫對應於相同連接埠的驅動程式回呼。若要啟用連接埠層級鎖定,請在驅動程式使用的driver_entry
中設定 ERL_DRV_FLAG_USE_PORT_LOCKING
驅動程式旗標。使用連接埠層級鎖定時,驅動程式撰寫者有責任同步所有對連接埠(驅動程式實例)共用資料的存取。
如果使用驅動程式層級鎖定,則大多數在具有 SMP 支援的執行階段系統存在之前撰寫的驅動程式,都可以在具有 SMP 支援的執行階段系統中執行,而無需重新撰寫。
注意
假設驅動程式不會存取其他驅動程式。如果驅動程式彼此存取,則它們必須提供自己的執行緒安全同步機制。強烈建議不要進行這種「驅動程式間通訊」。
注意
無論使用何種鎖定配置,都可以從不同的執行緒呼叫驅動程式回呼。
此 API 中的大多數函式都是*非*執行緒安全的,也就是說,*無法*從任意執行緒呼叫它們。未記錄為執行緒安全的函式只能從驅動程式回呼或從驅動程式回呼呼叫衍生的函式呼叫。請注意,驅動程式回呼可以從不同的執行緒呼叫。但是,這對此 API 中的任何函式都沒有問題,因為模擬器可以控制這些執行緒。
警告
未明確記錄為執行緒安全的函式*不是*執行緒安全的。
未明確記錄為執行緒安全的函式,在執行階段系統中可能會在某個時間點具有執行緒安全的實作。但是,此實作可以隨時變更為執行緒*不安全*的實作,*恕不另行通知*。
只能從任意執行緒使用明確記錄為執行緒安全的函式。
如本節開頭的警告文字中所述,驅動程式回呼必須相對快速地返回,這至關重要。很難確切說明允許驅動程式回呼工作的最長時間,但通常運作良好的驅動程式回呼應在一毫秒內返回。這可以使用不同的方法來實現。如果您可以完全控制要在驅動程式回呼中執行的程式碼,最好的方法是將工作分成多個工作區塊,並使用零逾時觸發對逾時回呼的多個呼叫。函式erl_drv_consume_timeslice
可用於判斷何時觸發此類逾時回呼呼叫。但是,有時無法以這種方式實作,例如在呼叫第三方函式庫時。在這種情況下,您通常會想要將工作分派到另一個執行緒。以下提供有關執行緒基本元素的資訊。
功能
驅動程式需要使用 Erlang 完成的所有函式都是透過驅動程式 API 函式執行。以下功能存在函式:
計時器函式 - 控制驅動程式可以使用的計時器。計時器會在指定時間後讓模擬器呼叫
timeout
進入函式。每個驅動程式實例只有一個計時器可用。佇列處理 - 每個驅動程式實例都有一個相關聯的佇列。此佇列是一個
SysIOVec
,作為緩衝區使用。它主要用於讓驅動程式緩衝要寫入裝置的資料,它是一個位元組串流。如果連接埠擁有者處理序關閉驅動程式,並且佇列不為空,則驅動程式不會關閉。這可讓驅動程式在關閉之前刷新其緩衝區。如果使用連接埠資料鎖定,則可以從任何執行緒操作佇列。如需詳細資訊,請參閱
ErlDrvPDL
。輸出函式 - 使用這些函式,驅動程式會將資料傳送回模擬器。資料會由連接埠擁有者處理序以訊息形式接收,請參閱
erlang:open_port/2
。向量函式和採用驅動程式二進位資料的函式速度更快,因為它們避免複製資料緩衝區。還有一種從驅動程式傳送術語的快速方法,無需使用二進位術語格式。失敗 - 驅動程式可以結束並將錯誤訊號傳送到 Erlang。這僅適用於嚴重錯誤,當驅動程式無法保持開啟時。
非同步呼叫 - Erlang/OTP R7B 和更新版本提供使用 Erlang 提供的執行緒集區進行非同步函式呼叫的功能。還有一個 select 呼叫,可用於非同步驅動程式。
多執行緒
提供類似 POSIX 執行緒的多執行緒 API。Erlang 驅動程式執行緒 API 僅提供 POSIX 執行緒 API 所提供功能的一個子集。提供的子集或多或少是多執行緒程式設計所需的基本功能Erlang 驅動程式執行緒 API 可以與 UN-ice 上的 POSIX 執行緒 API 以及 Windows 上的 Windows 原生執行緒 API 結合使用。Erlang 驅動程式執行緒 API 的優點是可移植,但可能存在想要使用 POSIX 執行緒 API 或 Windows 原生執行緒 API 功能的情況。
Erlang 驅動程式執行緒 API 僅在從錯誤狀況中恢復是合理時,才會傳回錯誤碼。如果從錯誤狀況中恢復是不合理的,則會終止整個執行階段系統。例如,如果建立互斥鎖操作失敗,則會傳回錯誤碼,但如果對互斥鎖的鎖定操作失敗,則會終止整個執行階段系統。
請注意,在 Erlang 驅動程式執行緒 API 中,沒有「具有逾時的條件變數等待」。這是因為
pthread_cond_timedwait
的問題。當系統時鐘突然變更時,無法保證您會如預期地從呼叫中喚醒。Erlang 執行階段系統必須能夠應對系統時鐘的突然變更。因此,我們已從 Erlang 驅動程式執行緒 API 中省略它。在 Erlang 驅動程式案例中,逾時可以使用 Erlang 驅動程式 API 的計時器功能來處理。請注意,Erlang 驅動程式 API 中的許多函式都是*非*執行緒安全的。如果函式未記錄為執行緒安全,則它*不是*執行緒安全的。
注意
在模擬器執行緒中執行時,*非常重要*的是,在讓執行緒失去您的控制之前,您必須解除鎖定*所有*您已鎖定的鎖定;否則您*很可能*會造成整個模擬器死結。
如果需要在模擬器執行緒中使用執行緒特定資料,請僅在執行緒受您控制時設定執行緒特定資料,並在讓執行緒失去您的控制之前清除執行緒特定資料。
未來,除錯功能可能會與 Erlang 驅動程式執行緒 API 整合。所有建立實體的功能都需要一個
name
參數。目前,name
參數未使用,但當除錯功能實作後將會使用。如果您能將所有建立的實體妥善命名,除錯功能將能夠提供更好的錯誤報告。新增/移除驅動程式 - 驅動程式可以新增,並在之後移除驅動程式。
監控行程 - 驅動程式可以監控不擁有埠口的行程。
版本管理
對於將其extended_marker
欄位設定為ERL_DRV_EXTENDED_MARKER
的驅動程式,會啟用版本管理。erl_driver.h
定義了ERL_DRV_EXTENDED_MARKER
ERL_DRV_EXTENDED_MAJOR_VERSION
,當 Erlang 執行期系統進行不相容的變更時,此值會遞增。通常,當ERL_DRV_EXTENDED_MAJOR_VERSION
變更時,重新編譯驅動程式就足夠了,但在極少數情況下,可能表示驅動程式必須稍微修改。如果發生這種情況,當然會有文件記錄。ERL_DRV_EXTENDED_MINOR_VERSION
,當新增新功能時,此值會遞增。執行期系統會使用驅動程式的次要版本來決定要使用的功能。
如果主要版本不同,或主要版本相同,但驅動程式使用的次要版本大於執行期系統使用的次要版本,執行期系統通常會拒絕載入驅動程式。然而,在兩個主要版本的轉換期間,主要版本升級後,允許使用較舊的、主要版本較低的驅動程式。但是,如果使用已棄用的功能,這些較舊的驅動程式可能會失敗。
模擬器拒絕載入未使用擴充驅動程式介面的驅動程式,以允許使用 64 位元功能的驅動程式,因為在 Erlang/OTP R15B 中,針對回呼
output
、control
和call
引入了不相容的類型變更。使用舊類型編寫的驅動程式會產生警告並在呼叫時向模擬器傳回垃圾大小,導致模擬器讀取隨機記憶體並建立巨大的不正確結果 blob。因此,僅重新編譯使用預 R15B 類型進行版本管理的驅動程式是不夠的;必須在驅動程式中變更類型,這表示需要進行其他重寫,特別是關於大小變數的部分。重新編譯時請調查所有警告。
此外,API 驅動程式函數
driver_output*
和driver_vec_to_buf
、driver_alloc/realloc*
以及driver_*
佇列函數已變更為具有較大的長度參數和傳回值。這是一個較小的問題,因為傳遞較小類型的程式碼會在呼叫中自動轉換它們,而且只要驅動程式不處理溢出int
的大小,一切都會像以前一樣運作。
64 位元驅動程式介面的重寫
ERTS 5.9 引入了兩種新的整數類型,ErlDrvSizeT
和 ErlDrvSSizeT
,如果需要,它們可以容納 64 位元的大小。
若要不更新驅動程式而僅重新編譯,當為 32 位元機器建置時,它可能會運作,產生一種錯誤的安全感。希望這會產生許多重要的警告。但是當稍後為 64 位元機器重新編譯相同的驅動程式時,將會出現警告,而且幾乎肯定會崩潰。因此,延遲更新驅動程式而不修正警告是一個壞主意。
使用 gcc
重新編譯時,請使用旗標 -Wstrict-prototypes
以取得更好的警告。如果您使用其他編譯器,請嘗試尋找類似的旗標。
以下是重寫 ERTS 5.9 之前的驅動程式的檢查清單,最重要的優先
驅動程式回呼的傳回類型 - 重寫驅動程式回呼
control
,使其使用傳回類型ErlDrvSSizeT
而不是int
。重寫驅動程式回呼
call
,使其使用傳回類型ErlDrvSSizeT
而不是int
。注意
這些變更對於避免模擬器崩潰或更糟的情況(導致故障)至關重要。沒有這些變更,驅動程式可能會向模擬器傳回高 32 位元中的垃圾,導致模擬器從隨機位元組建立一個巨大的結果,不是在記憶體配置上崩潰,就是從驅動程式呼叫成功產生隨機結果。
驅動程式回呼的參數 - 驅動程式回呼
output
現在會以ErlDrvSizeT
作為第 3 個參數,而不是先前的int
。驅動程式回呼
control
現在會以ErlDrvSizeT
作為第 4 個和第 6 個參數,而不是先前的int
。驅動程式回呼
call
現在會以ErlDrvSizeT
作為第 4 個和第 6 個參數,而不是先前的int
。健全的編譯器呼叫慣例可能使得這些變更僅對於驅動程式處理需要 64 位元大小欄位的資料區塊是必要的(主要大於 2 GB,因為那是 32 位元的
int
可以容納的)。但是,可以想到不健全的呼叫慣例,這會導致驅動程式回呼混淆參數,從而導致故障。注意
參數類型變更是從帶正負號變更為不帶正負號。如果您只是將所有位置的類型變更,這可能會導致問題,例如迴圈終止條件或錯誤條件。
ErlIOVec
中較大的size
欄位 -ErlIOVec
中的size
欄位已從int
變更為ErlDrvSizeT
。檢查使用該欄位的所有程式碼。自動類型轉換可能僅對於遇到大小 > 32 位元的驅動程式而言是必要的。
注意
size
欄位從帶正負號變更為不帶正負號。如果您只是將所有位置的類型變更,這可能會導致問題,例如迴圈終止條件或錯誤條件。驅動程式 API 中的參數和傳回值 - 許多驅動程式 API 函數的參數類型和/或傳回值已從主要是
int
變更為ErlDrvSizeT
。自動類型轉換可能僅對於遇到大小 > 32 位元的驅動程式而言是必要的。driver_output
- 第 3 個參數driver_output2
- 第 3 個和第 5 個參數driver_output_binary
- 第 3 個、第 5 個和第 6 個參數driver_outputv
- 第 3 個和第 5 個參數driver_vec_to_buf
- 第 3 個參數和傳回值driver_alloc
- 第 1 個參數driver_realloc
- 第 2 個參數driver_alloc_binary
- 第 1 個參數driver_realloc_binary
- 第 2 個參數driver_enq
- 第 3 個參數driver_pushq
- 第 3 個參數driver_deq
- 第 2 個參數和傳回值driver_sizeq
- 傳回值driver_enq_bin
- 第 3 個和第 4 個參數driver_pushq_bin
- 第 3 個和第 4 個參數driver_enqv
- 第 3 個參數driver_pushqv
- 第 3 個參數driver_peekqv
- 傳回值
注意
這是一個從帶正負號變更為不帶正負號的變更。如果您只是將所有位置的類型變更,這可能會導致問題,例如迴圈終止條件和錯誤條件。
資料類型
ErlDrvSizeT
- 一種要用作size_t
的不帶正負號的整數類型。ErlDrvSSizeT
- 一種帶正負號的整數類型,大小與ErlDrvSizeT
相同。ErlDrvSysInfo
typedef struct ErlDrvSysInfo { int driver_major_version; int driver_minor_version; char *erts_version; char *otp_release; int thread_support; int smp_support; int async_threads; int scheduler_threads; int nif_major_version; int nif_minor_version; int dirty_scheduler_support; } ErlDrvSysInfo;
ErlDrvSysInfo
結構用於儲存有關 Erlang 執行期系統的資訊。driver_system_info
在傳遞ErlDrvSysInfo
結構的參考時寫入系統資訊。結構中的欄位如下:driver_major_version
- 編譯執行期系統時的ERL_DRV_EXTENDED_MAJOR_VERSION
值。此值與編譯驅動程式時使用的ERL_DRV_EXTENDED_MAJOR_VERSION
值相同;否則,執行期系統會拒絕載入驅動程式。driver_minor_version
- 編譯執行期系統時的ERL_DRV_EXTENDED_MINOR_VERSION
值。此值可能與編譯驅動程式時使用的ERL_DRV_EXTENDED_MINOR_VERSION
值不同。erts_version
- 包含執行期系統版本號的字串(與erlang:system_info(version)
傳回的相同)。otp_release
- 包含 OTP 發行編號的字串(與erlang:system_info(otp_release)
傳回的相同)。thread_support
- 如果執行期系統具有執行緒支援,則值為!= 0
;否則為0
。smp_support
- 如果執行期系統具有 SMP 支援,則值為!= 0
;否則為0
。async_threads
-driver_async
使用的非同步執行緒集區中的非同步執行緒數目(與erlang:system_info(thread_pool_size)
傳回的相同)。scheduler_threads
- 執行期系統使用的排程器執行緒數目(與erlang:system_info(schedulers)
傳回的相同)。nif_major_version
- 編譯執行期系統時的ERL_NIF_MAJOR_VERSION
值。nif_minor_version
- 編譯執行期系統時的ERL_NIF_MINOR_VERSION
值。dirty_scheduler_support
- 如果執行期系統支援髒排程器執行緒,則值為!= 0
;否則為0
。
ErlDrvBinary
typedef struct ErlDrvBinary { ErlDrvSint orig_size; char orig_bytes[]; } ErlDrvBinary;
ErlDrvBinary
結構是一個二進制數據,在模擬器和驅動程式之間傳送。所有二進制數據都有參考計數;當呼叫driver_binary_free
時,參考計數會遞減,當計數達到零時,二進制數據會被釋放。orig_size
是二進制數據的大小,而orig_bytes
是緩衝區。ErlDrvBinary
的大小不是固定的,其大小為orig_size + 2 * sizeof(int)
。注意
refc
欄位已被移除。ErlDrvBinary
的參考計數現在儲存在其他地方。可以透過driver_binary_get_refc
、driver_binary_inc_refc
和driver_binary_dec_refc
來存取ErlDrvBinary
的參考計數。某些驅動程式呼叫,例如
driver_enq_binary
,會遞增驅動程式的參考計數,而其他呼叫,例如driver_deq
,則會遞減它。使用驅動程式二進制數據而不是一般緩衝區通常更快,因為模擬器不需要複製數據,只需要使用指標即可。
在驅動程式中使用
driver_alloc_binary
分配的驅動程式二進制數據,應該在驅動程式中使用driver_free_binary
釋放(除非另有說明)。(請注意,如果模擬器仍然參照驅動程式,參考計數不會歸零,這不一定會釋放它。)驅動程式二進制數據用於
driver_output2
和driver_outputv
呼叫,以及佇列中。此外,驅動程式回呼outputv
也會使用驅動程式二進制數據。如果驅動程式由於某些原因想要保留驅動程式二進制數據,例如在靜態變數中,則必須遞增參考計數,並且稍後可以在
stop
回呼中使用driver_free_binary
釋放該二進制數據。請注意,驅動程式二進制數據是由驅動程式和模擬器共用的。驅動程式不得更改從模擬器接收或傳送到模擬器的二進制數據。
自 ERTS 5.5 (Erlang/OTP R11B) 起,保證
orig_bytes
已正確對齊,以便儲存雙精度浮點數陣列(通常為 8 位元組對齊)。ErlDrvData
- 驅動程式特定數據的句柄,傳遞給驅動程式回呼。它是一個指標,通常在驅動程式中轉換為特定的指標類型。SysIOVec
- 系統 I/O 向量,如 Unix 上的writev
和 Win32 上的WSASend
所使用。它在ErlIOVec
中使用。ErlIOVec
typedef struct ErlIOVec { int vsize; ErlDrvSizeT size; SysIOVec* iov; ErlDrvBinary** binv; } ErlIOVec;
模擬器和驅動程式使用的 I/O 向量是一個二進制數據列表,其中
SysIOVec
指向二進制數據的緩衝區。它在driver_outputv
和outputv
驅動程式回呼中使用。此外,驅動程式佇列也是一個ErlIOVec
。ErlDrvMonitor
- 當驅動程式為進程建立監視器時,會填入ErlDrvMonitor
。這是一個不透明的數據類型,可以分配值,但不能在不使用提供的比較函數的情況下進行比較(也就是說,它的行為類似於結構)。當呼叫
driver_monitor_process
時,驅動程式撰寫者應提供儲存監視器的記憶體。數據的位址不會儲存在驅動程式之外,因此ErlDrvMonitor
可以像任何其他數據一樣使用,它可以複製、在記憶體中移動、遺忘等等。ErlDrvNowData
-ErlDrvNowData
結構包含一個時間戳記,由從過去某個任意時間點測量的三個值組成。三個結構成員是megasecs
- 自任意時間點以來經過的完整百萬秒數secs
- 自任意時間點以來經過的完整秒數microsecs
- 自任意時間點以來經過的完整微秒數
ErlDrvPDL
- 如果必須從呼叫驅動程式回呼以外的執行緒存取某些特定於埠的數據,則可以使用埠數據鎖定來同步對數據的操作。目前,模擬器與埠數據鎖定關聯的唯一特定於埠的數據是驅動程式佇列。通常,驅動程式實例沒有埠數據鎖定。如果驅動程式實例想要使用埠數據鎖定,它必須呼叫
driver_pdl_create
來建立埠數據鎖定。注意
建立埠數據鎖定後,每次存取與埠數據鎖定關聯的數據都必須在埠數據鎖定鎖定時完成。埠數據鎖定分別由
driver_pdl_lock
和driver_pdl_unlock
鎖定和解鎖。埠數據鎖定是參考計數的,當參考計數達到零時,它會被銷毀。模擬器至少會在建立鎖定時將參考計數遞增一次,並在與鎖定關聯的埠終止時遞減一次。當非同步作業加入佇列時,模擬器也會遞增參考計數,並在非同步作業被呼叫時遞減參考計數。此外,驅動程式有責任確保參考計數不會在驅動程式最後一次使用鎖定之前達到零。參考計數可以分別使用
driver_pdl_get_refc
、driver_pdl_inc_refc
和driver_pdl_dec_refc
讀取、遞增和遞減。ErlDrvTid
- 執行緒識別碼。另請參閱
erl_drv_thread_create
、erl_drv_thread_exit
、erl_drv_thread_join
、erl_drv_thread_self
和erl_drv_equal_tids
。ErlDrvThreadOpts
int suggested_stack_size;
傳遞給
erl_drv_thread_create
的執行緒選項結構。存在以下欄位suggested_stack_size
- 關於要使用多大的堆疊的建議(以千字為單位)。小於 0 的值表示預設大小。
另請參閱
erl_drv_thread_opts_create
、erl_drv_thread_opts_destroy
和erl_drv_thread_create
。ErlDrvMutex
- 互斥鎖。用於同步對共用數據的存取。一次只能有一個執行緒可以鎖定互斥鎖。另請參閱
erl_drv_mutex_create
、erl_drv_mutex_destroy
、erl_drv_mutex_lock
、erl_drv_mutex_trylock
和erl_drv_mutex_unlock
。ErlDrvCond
- 條件變數。當執行緒必須等待特定條件出現才能繼續執行時使用。條件變數必須與相關聯的互斥鎖一起使用。另請參閱
erl_drv_cond_create
、erl_drv_cond_destroy
、erl_drv_cond_signal
、erl_drv_cond_broadcast
和erl_drv_cond_wait
。ErlDrvRWLock
- 讀/寫鎖。用於允許多個執行緒讀取共用數據,同時只允許一個執行緒寫入相同數據。多個執行緒可以同時讀取鎖定一個 rwlock,而一次只能有一個執行緒可以讀/寫鎖定一個 rwlock。另請參閱
erl_drv_rwlock_create
、erl_drv_rwlock_destroy
、erl_drv_rwlock_rlock
、erl_drv_rwlock_tryrlock
、erl_drv_rwlock_runlock
、erl_drv_rwlock_rwlock
、erl_drv_rwlock_tryrwlock
和erl_drv_rwlock_rwunlock
。ErlDrvTSDKey
- 可以與執行緒特定數據關聯的鍵。另請參閱
erl_drv_tsd_key_create
、erl_drv_tsd_key_destroy
、erl_drv_tsd_set
和erl_drv_tsd_get
。ErlDrvTime
- 用於時間表示的有號 64 位元整數類型。ErlDrvTimeUnit
- 驅動程式 API 支援的時間單位列舉ERL_DRV_SEC
- 秒ERL_DRV_MSEC
- 毫秒ERL_DRV_USEC
- 微秒ERL_DRV_NSEC
- 奈秒
add_driver_entry()
void add_driver_entry(ErlDrvEntry
*de);
將驅動程式項目新增到 Erlang 已知的驅動程式清單中。呼叫參數 de
的 init
函數。
注意
將此函數用於新增駐留在動態載入程式碼中的驅動程式是危險的。如果新增的驅動程式的驅動程式程式碼與正常的動態載入驅動程式(使用
erl_ddll
介面載入)駐留在相同的動態載入模組(即.so
檔案)中,則呼叫者必須在新增驅動程式項目之前呼叫driver_lock_driver
。通常不建議使用此函數。
driver_alloc()
void * driver_alloc(ErlDrvSizeT size);
分配 size
中指定大小的記憶體區塊,並傳回它。只有在記憶體不足時才會失敗,在這種情況下會傳回 NULL
。(這通常是 malloc
的包裝函式)。
分配的記憶體必須使用對應的 driver_free
呼叫顯式釋放(除非另有說明)。
此函式為執行緒安全。
driver_alloc_binary()
ErlDrvBinary * driver_alloc_binary(ErlDrvSizeT size);
配置一個驅動程式二進位檔,其記憶體區塊大小至少為 size
位元組,並返回指向它的指標。如果配置失敗(記憶體不足),則返回 NULL
。當驅動程式二進位檔被傳送到模擬器後,不得再變更。每個配置的二進位檔都必須透過對 driver_free_binary
的相應呼叫來釋放(除非另有說明)。
請注意,驅動程式二進位檔有一個內部參考計數器。這表示呼叫 driver_free_binary
可能不會實際處置它。如果它被傳送到模擬器,則可以在那裡被引用。
驅動程式二進位檔有一個欄位 orig_bytes
,標示二進位檔中資料的起始位置。
此函式為執行緒安全。
driver_async()
long driver_async(ErlDrvPort port, unsigned
int* key, void (*async_invoke)(void*), void* async_data, void
(*async_free)(void*));
執行非同步呼叫。函式 async_invoke
在與模擬器執行緒分離的執行緒中呼叫。這使驅動程式能夠執行耗時的阻塞操作,而不會阻塞模擬器。
可以使用命令列引數 +A
在 erl(1)
中設定非同步執行緒池大小。如果非同步執行緒池不可用,則在呼叫 driver_async
的執行緒中同步進行呼叫。非同步執行緒池中目前非同步執行緒的數量可以透過 driver_system_info
取得。
如果執行緒池可用,則會使用執行緒。如果引數 key
為 NULL
,則會以循環方式使用池中的執行緒,每次呼叫 driver_async
都會使用池中的下一個執行緒。設定引數 key
後,此行為會變更。相同的兩個 *key
值總是會取得相同的執行緒。
為了確保驅動程式實例始終使用相同的執行緒,可以使用以下呼叫
unsigned int myKey = driver_async_port_key(myPort);
r = driver_async(myPort, &myKey, myData, myFunc);
對於每個驅動程式實例,只需初始化 myKey
一次。
如果執行緒已在工作,則會將呼叫排隊並按順序執行。為每個驅動程式實例使用相同的執行緒可確保呼叫按順序進行。
async_data
是函式 async_invoke
和 async_free
的引數。它通常是指向結構的指標,其中包含可用於發出非同步操作完成訊號的管道或事件。資料將在 async_free
中釋放。
當非同步操作完成時,會呼叫 ready_async
驅動程式進入函式。如果驅動程式進入中的 ready_async
為 NULL
,則會改為呼叫 async_free
函式。
如果 driver_async
呼叫失敗,則傳回值為 -1
。
注意
自 ERTS 5.5.4.3 起,非同步執行緒池中執行緒的預設堆疊大小為 16 千字組,也就是在 32 位元架構上為 64 KB。之所以選擇此小型預設大小,是因為非同步執行緒的數量可能相當大。預設堆疊大小對於 Erlang/OTP 提供的驅動程式來說已足夠,但對於使用
driver_async
功能的其他動態連結驅動程式來說,可能不夠大。可以透過命令列引數+a
在erl(1)
中設定建議的非同步執行緒池中執行緒的堆疊大小。
driver_async_port_key()
unsigned int driver_async_port_key(ErlDrvPort port);
計算稍後在 driver_async
中使用的金鑰。金鑰會均勻分佈,以便在連接埠 ID 與非同步執行緒 ID 之間實現公平的對應關係。
注意
在 Erlang/OTP R16 之前,可以使用適當的轉換將連接埠 ID 作為金鑰,但在連接埠子系統重寫後,情況不再如此。使用此函式,您可以實現與 Erlang/OTP R16 之前相同的基於連接埠 ID 的分佈。
自 OTP R16B02 起提供
driver_binary_dec_refc()
long driver_binary_dec_refc(ErlDrvBinary *bin);
遞減 bin
上的參考計數,並傳回遞減後達到的參考計數。
此函式為執行緒安全。
注意
驅動程式二進位檔的參考計數通常是透過呼叫
driver_free_binary
來遞減。如果參考計數達到零,
driver_binary_dec_refc
不會釋放二進位檔。只有在您確定不會達到零的參考計數時,才使用driver_binary_dec_refc
。
driver_binary_get_refc()
long driver_binary_get_refc(ErlDrvBinary *bin);
傳回 bin
上的目前參考計數。
此函式為執行緒安全。
driver_binary_inc_refc()
long driver_binary_inc_refc(ErlDrvBinary *bin);
遞增 bin
上的參考計數,並傳回遞增後達到的參考計數。
此函式為執行緒安全。
driver_caller()
ErlDrvTermData driver_caller(ErlDrvPort
port);
傳回對驅動程式進行目前呼叫的處理序 ID。可以使用 driver_send_term
將資料傳回給呼叫者。只有在目前在下列驅動程式回呼之一中執行時,driver_caller
才會傳回有效資料
start
- 從erlang:open_port/2
呼叫。output
- 從erlang:send/2
和erlang:port_command/2
呼叫。outputv
- 從erlang:send/2
和erlang:port_command/2
呼叫。control
- 從erlang:port_control/3
呼叫。call
- 從erlang:port_call/3
呼叫。
請注意,此函式不是執行緒安全。
driver_cancel_timer()
int driver_cancel_timer(ErlDrvPort port);
取消使用 driver_set_timer
設定的計時器。
傳回值為 0
。
driver_compare_monitors()
int driver_compare_monitors(const ErlDrvMonitor
*monitor1, const ErlDrvMonitor *monitor2);
比較兩個 ErlDrvMonitor
。也可以出於任何原因,用於暗示監視器上的一些人工順序。
如果 monitor1
和 monitor2
相等,則傳回 0
;如果 monitor1
< monitor2
,則傳回 < 0
;如果 monitor1
> monitor2
,則傳回 > 0
。
driver_connected()
ErlDrvTermData driver_connected(ErlDrvPort
port);
傳回連接埠擁有者處理序。
請注意,此函式不是執行緒安全。
driver_create_port()
ErlDrvPort driver_create_port(ErlDrvPort port,
ErlDrvTermData owner_pid, char* name,
ErlDrvData drv_data);
建立一個新的連接埠,執行與建立新連接埠的連接埠相同的驅動程式碼。
port
- 建立新連接埠的連接埠(驅動程式實例)的連接埠控制代碼。owner_pid
- 將成為新連接埠擁有者的 Erlang 處理序的處理序 ID。此處理序將會連結到新的連接埠。您通常會想要使用driver_caller(port)
作為owner_pid
。name
- 新連接埠的連接埠名稱。您通常會想要使用與驅動程式名稱相同的連接埠名稱(driver_name
driver_entry
的欄位)。drv_data
- 驅動程式定義的控制代碼,將在稍後呼叫驅動程式回呼時傳遞。請注意,不會針對此新的驅動程式實例呼叫 驅動程式啟動回呼。驅動程式定義的控制代碼通常是在透過erlang:open_port/2
建立連接埠時,在 驅動程式啟動回呼 中建立的。
當 driver_create_port
傳回時,driver_create_port
的呼叫者可以操作新建立的連接埠。當使用連接埠層級鎖定時,只允許建立連接埠的連接埠操作新建立的連接埠,直到目前由模擬器呼叫的驅動程式回呼傳回為止。
driver_demonitor_process()
int driver_demonitor_process(ErlDrvPort port,
const ErlDrvMonitor *monitor);
取消先前建立的監視器。
如果移除監視器,則傳回 0
;如果監視器不再存在,則傳回 > 0。
driver_deq()
ErlDrvSizeT driver_deq(ErlDrvPort port,
ErlDrvSizeT size);
透過將驅動程式佇列中的頭指標向前移動 size
個位元組來將資料出隊。佇列中的資料會被釋放。
如果成功,則傳回佇列中剩餘的位元組數;否則傳回 -1
。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_enq()
int driver_enq(ErlDrvPort port, char* buf,
ErlDrvSizeT len);
將資料加入驅動程式佇列中。buf
中的資料會被複製(len
位元組),並放置在驅動程式佇列的末端。驅動程式佇列通常以 FIFO 方式使用。
驅動程式佇列可用於將來自模擬器的輸出排隊到驅動程式(從驅動程式到模擬器的資料是由模擬器在正常的 Erlang 訊息佇列中排隊)。如果驅動程式必須等待緩慢的裝置,等等,並希望將控制權交還給模擬器,這會很有用。驅動程式佇列實作為 ErlIOVec
。
當佇列包含資料時,驅動程式不會關閉,直到佇列為空。
傳回值為 0
。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_enq_bin()
int driver_enq_bin(ErlDrvPort port,
ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len);
將驅動程式二進位檔加入驅動程式佇列中。bin
中 offset
處的資料,其長度為 len
,將放置在佇列的末端。此函式通常比 driver_enq
快,因為不必複製資料。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
傳回值為 0
。
driver_enqv()
int driver_enqv(ErlDrvPort port, ErlIOVec *ev,
ErlDrvSizeT skip);
將 ev
中的資料(跳過開頭的 skip
個位元組)加入驅動程式佇列的末端。它比 driver_enq
快,因為不必複製資料。
傳回值為 0
。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_failure()
driver_failure_atom()
driver_failure_posix()
int driver_failure(ErlDrvPort port, int
error);
int driver_failure_atom(ErlDrvPort port, char
*string);
int driver_failure_posix(ErlDrvPort port, int
error);
向 Erlang 發出訊號,表示驅動程式發生錯誤且應關閉。此時 Port 會關閉,並將元組 {'EXIT', error, Err}
送至 Port 的擁有者程序,其中 error 為錯誤原子 (error atom)(driver_failure_atom
和 driver_failure_posix
),或是一個整數 (driver_failure
)。
只有在發生嚴重錯誤情況,驅動程式不可能繼續開啟時,才應讓驅動程式失敗,例如緩衝區記憶體配置耗盡。對於正常錯誤,使用 driver_output
發送錯誤代碼更為合適。
傳回值為 0
。
driver_failure_eof()
int driver_failure_eof(ErlDrvPort
port);
向 Erlang 發出訊號,表示驅動程式已遇到 EOF 且應關閉,除非 Port 開啟時帶有選項 eof
,在這種情況下,會將 eof
送至 Port。否則,Port 會關閉,並將 'EXIT'
訊息傳送至 Port 的擁有者程序。
傳回值為 0
。
driver_free()
void driver_free(void *ptr);
釋放 ptr
指向的記憶體。該記憶體必須是使用 driver_alloc
配置的。所有配置的記憶體都必須僅釋放一次。驅動程式中沒有垃圾回收。
此函式為執行緒安全。
driver_free_binary()
void driver_free_binary(ErlDrvBinary *bin);
釋放先前使用 driver_alloc_binary
配置的驅動程式二進制檔 bin
。由於 Erlang 中的二進制檔是參考計數的,該二進制檔可能仍然存在。
此函式為執行緒安全。
driver_get_monitored_process()
ErlDrvTermData driver_get_monitored_process(ErlDrvPort port, const
ErlDrvMonitor *monitor);
傳回與現有監控器相關聯的程序 ID。它可以用於 process_exit
回呼中,以取得正在結束的程序的識別資訊。
如果監控器不再存在,則傳回 driver_term_nil
。
driver_get_now()
int driver_get_now(ErlDrvNowData *now);
警告
此函數已棄用。請勿使用。 請改用
erl_drv_monotonic_time
(或許可與erl_drv_time_offset
結合使用)。
將時間戳記讀取至參數 now
指向的記憶體中。有關特定欄位的資訊,請參閱 ErlDrvNowData
。
傳回值為 0
,除非 now
指標無效,在這種情況下,傳回值為 < 0
。
driver_lock_driver()
int driver_lock_driver(ErlDrvPort
port);
將 Port port
使用的驅動程式鎖定在記憶體中,以供模擬器程序的其餘生命週期使用。在呼叫此函數後,驅動程式的行為會如同 Erlang 的靜態連結驅動程式之一。
driver_mk_atom()
ErlDrvTermData driver_mk_atom(char*
string);
傳回指定名稱 string
的原子。原子會被建立且不會變更,因此可以儲存並重複使用傳回值,這比多次查詢原子更快。
請注意,此函式不是執行緒安全。
driver_mk_port()
ErlDrvTermData driver_mk_port(ErlDrvPort
port);
將 Port 句柄轉換為 Erlang Term 格式,可用於 erl_drv_output_term
和 erl_drv_send_term
。
請注意,此函式不是執行緒安全。
driver_monitor_process()
int driver_monitor_process(ErlDrvPort port,
ErlDrvTermData process, ErlDrvMonitor *monitor);
開始從驅動程式監控程序。當程序受到監控時,程序結束會導致在 ErlDrvEntry
結構中呼叫提供的 process_exit
回呼。會填入 ErlDrvMonitor
結構,以供稍後移除或比較。
參數 process
必須是先前呼叫 driver_caller
或 driver_connected
的傳回值。
成功時傳回 0
,如果未提供回呼則傳回 < 0,如果程序不再存在則傳回 > 0。
driver_output()
int driver_output(ErlDrvPort port, char *buf,
ErlDrvSizeT len);
將資料從驅動程式傳送到模擬器。資料會以 Term 或二進制資料的形式接收,具體取決於驅動程式 Port 的開啟方式。
資料會排入 Port 擁有者程序的訊息佇列中。請注意,這不會讓步於模擬器(因為驅動程式和模擬器在同一個執行緒中執行)。
參數 buf
指向要傳送的資料,len
則為位元組數。
所有輸出函數的傳回值在正常使用情況下為 0
。如果驅動程式用於分佈,則可能會失敗並傳回 -1
。
driver_output_binary()
int driver_output_binary(ErlDrvPort port, char
*hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset,
ErlDrvSizeT len);
從驅動程式二進制檔將資料傳送至 Port 擁有者程序。它具有與 driver_output2
相同的標頭緩衝區(hbuf
和 hlen
)。參數 hbuf
可以是 NULL
。
參數 offset
是二進制檔中的偏移量,len
是要傳送的位元組數。
驅動程式二進制檔是使用 driver_alloc_binary
建立的。
標頭中的資料會以清單的形式傳送,二進制檔則會以 Erlang 二進制檔的形式傳送至清單的尾部。
例如,如果 hlen
為 2
,Port 擁有者程序會接收 [H1, H2 | <<T>>]
。
傳回值在正常使用情況下為 0
。
請注意,使用 Erlang 中的二進制語法,驅動程式應用程式可以直接從二進制檔比對標頭,因此標頭可以放入二進制檔中,且 hlen
可以設定為 0
。
driver_output_term()
int driver_output_term(ErlDrvPort port,
ErlDrvTermData* term, int n);
警告
此函數已棄用。請改用
erl_drv_output_term
。
參數 term
和 n
的運作方式與 erl_drv_output_term
中相同。
請注意,此函式不是執行緒安全。
driver_output2()
int driver_output2(ErlDrvPort port, char *hbuf,
ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len);
首先,將 hbuf
(長度為 hlen
)資料以清單的形式傳送,無論 Port 設定為何。然後,將 buf
以二進制檔或清單的形式傳送。例如,如果 hlen
為 3
,Port 擁有者程序會接收 [H1, H2, H3 | T]
。
以清單標頭的形式傳送資料的目的是方便對接收到的資料進行比對。
傳回值在正常使用情況下為 0
。
driver_outputv()
int driver_outputv(ErlDrvPort port, char* hbuf,
ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip);
將資料從 I/O 向量 ev
傳送至 Port 擁有者程序。它具有與 driver_output2
相同的標頭緩衝區(hbuf
和 hlen
)。
參數 skip
是要從 ev
向量的開頭略過的位元組數。
您可以從驅動程式佇列(請參閱下文)和 outputv
驅動程式進入函數中取得 ErlIOVec
類型的向量。如果想要一次傳送多個 ErlDrvBinary
緩衝區,也可以自行建立。通常使用 driver_output
或 driver_output2
會更快。
例如,如果 hlen
為 2
,且 ev
指向三個二進制檔的陣列,則 Port 擁有者程序會接收 [H1, H2, <<B1>>, <<B2>> | <<B3>>]
。
傳回值在正常使用情況下為 0
。
driver_output_binary
的註解也適用於 driver_outputv
。
driver_pdl_create()
ErlDrvPDL driver_pdl_create(ErlDrvPort port);
建立與 port
相關聯的 Port 資料鎖定。
注意
一旦建立 Port 資料鎖定,在對
port
的驅動程式佇列執行所有操作期間,都必須鎖定它。
成功時傳回新建立的 Port 資料鎖定,否則傳回 NULL
。如果 port
無效,或如果 Port 資料鎖定已與 port
相關聯,則函數會失敗。
driver_pdl_dec_refc()
long driver_pdl_dec_refc(ErlDrvPDL
pdl);
遞減作為引數傳遞的 Port 資料鎖定 (pdl
) 的參考計數。
傳回在執行遞減後的目前參考計數。
此函式為執行緒安全。
driver_pdl_get_refc()
long driver_pdl_get_refc(ErlDrvPDL pdl);
傳回作為引數傳遞的 Port 資料鎖定 (pdl
) 的目前參考計數。
此函式為執行緒安全。
driver_pdl_inc_refc()
long driver_pdl_inc_refc(ErlDrvPDL pdl);
遞增作為引數傳遞的 Port 資料鎖定 (pdl
) 的參考計數。
傳回在執行遞增後的目前參考計數。
此函式為執行緒安全。
driver_pdl_lock()
void driver_pdl_lock(ErlDrvPDL pdl);
鎖定作為引數傳遞的 Port 資料鎖定 (pdl
)。
此函式為執行緒安全。
driver_pdl_unlock()
void driver_pdl_unlock(ErlDrvPDL pdl);
解除鎖定作為引數傳遞的 Port 資料鎖定 (pdl
)。
此函式為執行緒安全。
driver_peekq()
SysIOVec * driver_peekq(ErlDrvPort port, int
*vlen);
將驅動程式佇列擷取為指向 SysIOVec
陣列的指標。它也會傳回 vlen
中的元素數。這是從佇列中取得資料的兩種方法之一。
此函數不會從佇列中移除任何內容,必須使用 driver_deq
來執行。
傳回的陣列適合與 Unix 系統呼叫 writev
搭配使用。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_peekqv()
ErlDrvSizeT driver_peekqv(ErlDrvPort port,
ErlIOVec *ev);
將驅動程式佇列擷取到提供的 ErlIOVec
ev
中。它也會傳回佇列大小。這是從佇列中取出資料的兩種方式之一。
如果 ev
為 NULL
,則會傳回所有類型轉換為 ErlDrvSizeT
的 -1
。
此函數不會從佇列中移除任何內容,必須使用 driver_deq
來執行。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
自 OTP R15B 開始提供
driver_pushq()
int driver_pushq(ErlDrvPort port, char* buf,
ErlDrvSizeT len);
將資料置於驅動程式佇列的開頭。buf
中的資料會被複製(len
個位元組),並放置在佇列的開頭。
傳回值為 0
。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_pushq_bin()
int driver_pushq_bin(ErlDrvPort port,
ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len);
將二進制資料 bin
中,位於 offset
且長度為 len
的資料,置於驅動程式佇列的開頭。它通常比 driver_pushq
快,因為不需要複製資料。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
傳回值為 0
。
driver_pushqv()
int driver_pushqv(ErlDrvPort port, ErlIOVec
*ev, ErlDrvSizeT skip);
將 ev
中的資料(跳過其前 skip
個位元組)置於驅動程式佇列的開頭。它比 driver_pushq
快,因為不需要複製資料。
傳回值為 0
。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_read_timer()
int driver_read_timer(ErlDrvPort port, unsigned
long *time_left);
讀取計時器的目前時間,並將結果放入 time_left
中。這是逾時發生前的剩餘時間,單位為毫秒。
傳回值為 0
。
driver_realloc()
void * driver_realloc(void *ptr, ErlDrvSizeT size);
調整記憶體區塊的大小,可以就地調整,或是分配一個新的區塊,複製資料,然後釋放舊的區塊。傳回重新配置的記憶體指標。如果失敗(記憶體不足),則會傳回 NULL
。(這通常是 realloc
的包裝函式。)
此函式為執行緒安全。
driver_realloc_binary()
ErlDrvBinary * driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size);
調整驅動程式二進制檔的大小,同時保留資料。
成功時傳回調整大小後的驅動程式二進制檔。失敗時(記憶體不足)傳回 NULL
。
此函式為執行緒安全。
driver_select()
int driver_select(ErlDrvPort port, ErlDrvEvent
event, int mode, int on);
驅動程式使用此函式向模擬器提供要檢查的事件。這使模擬器可以在發生非同步事件時呼叫驅動程式。
參數 event
識別作業系統特定的事件物件。在 Unix 系統上,使用 select
/poll
函式。事件物件必須是 socket 或 pipe(或其他 select
/poll
可以使用的物件)。在 Windows 上,使用 Win32 API 函式 WaitForMultipleObjects
。這對事件物件施加了其他限制;請參閱 Win32 SDK 文件。
參數 on
設定事件時為 1
,清除事件時為 0
。
參數 mode
是 ERL_DRV_READ
、ERL_DRV_WRITE
和 ERL_DRV_USE
的按位 OR 組合。前兩個指定是否等待讀取事件和/或寫入事件。觸發讀取事件會呼叫 ready_input
,觸發寫入事件會呼叫 ready_output
。
注意
某些作業系統 (Windows) 不區分讀取事件和寫入事件。觸發事件的回呼只取決於
mode
的值。
ERL_DRV_USE
指定我們是否正在使用事件物件,或者我們是否要關閉它。在 driver_select
傳回後清除所有事件,然後關閉事件物件是不安全的。另一個執行緒可能仍在內部使用事件物件。要安全地關閉事件物件,請使用 ERL_DRV_USE
和 on==0
呼叫 driver_select
,這會清除所有事件,然後呼叫 stop_select
,或者在關閉事件物件安全時安排呼叫。 ERL_DRV_USE
應與事件物件的第一個事件一起設定。即使已經完成設定 ERL_DRV_USE
也沒有害處。清除所有事件但保持 ERL_DRV_USE
設定表示我們正在使用事件物件,並且可能會再次為其設定事件。
注意
ERL_DRV_USE
在 Erlang/OTP R13 中新增。舊的驅動程式仍然像以前一樣工作,但建議更新它們以使用ERL_DRV_USE
和stop_select
,以確保事件物件以安全的方式關閉。
傳回值為 0
,除非 ready_input
/ready_output
為 NULL
,在這種情況下傳回值為 -1
。
driver_send_term()
int driver_send_term(ErlDrvPort port,
ErlDrvTermData receiver, ErlDrvTermData* term, int n);
警告
此函式已過時。請改用
erl_drv_send_term
。
注意
當任意執行緒執行此函式時,執行階段系統無法正確檢查此函式的參數。這可能會導致函式在應該失敗時沒有失敗。
參數 term
和 n
的運作方式與 erl_drv_output_term
中相同。
此函式為執行緒安全。
driver_set_timer()
int driver_set_timer(ErlDrvPort port, unsigned
long time);
在驅動程式上設定計時器,該計時器會倒數計時,並在逾時時呼叫驅動程式。參數 time
是計時器過期前的時間,單位為毫秒。
當計時器達到 0
並過期時,會呼叫驅動程式進入函式 timeout
。
請注意,每個驅動程式執行個體只有一個計時器;設定新的計時器會取代舊的計時器。
傳回值為 0
,除非 timeout
驅動程式函式為 NULL
,在這種情況下傳回值為 -1
。
driver_sizeq()
ErlDrvSizeT driver_sizeq(ErlDrvPort port);
傳回驅動程式佇列中目前的位元組數。
如果呼叫執行緒在呼叫期間鎖定與 port
關聯的 連接埠資料鎖定,則可以從任何執行緒呼叫此函式。
driver_system_info()
void driver_system_info(ErlDrvSysInfo
*sys_info_ptr, size_t size);
將 Erlang 執行階段系統的相關資訊寫入第一個引數所參照的 ErlDrvSysInfo
結構中。第二個引數應為 ErlDrvSysInfo
結構的大小,即 sizeof(ErlDrvSysInfo)
。
有關特定欄位的資訊,請參閱 ErlDrvSysInfo
。
driver_vec_to_buf()
ErlDrvSizeT driver_vec_to_buf(ErlIOVec *ev,
char *buf, ErlDrvSizeT len);
透過依序複製到大小為 len
的緩衝區 buf
中,收集由 ev
參照的幾個資料區段。
如果要將資料從驅動程式傳送到連接埠擁有者處理序,則使用 driver_outputv
會更快。
傳回值是緩衝區中剩餘的空間,也就是說,如果 ev
包含少於 len
個位元組,則為差值;如果 ev
包含 len
個位元組或更多,則為 0
。如果有超過一個標頭位元組,則速度會更快,因為二進制語法可以直接從二進制檔建構整數。
erl_drv_busy_msgq_limits()
void erl_drv_busy_msgq_limits(ErlDrvPort port,
ErlDrvSizeT *low, ErlDrvSizeT *high);
設定和取得將用於控制連接埠訊息佇列的忙碌狀態的限制。
當訊息佇列中排隊的命令資料量達到 high
限制時,連接埠訊息佇列會設定為忙碌狀態。當訊息佇列中排隊的命令資料量低於 low
限制時,連接埠訊息佇列會設定為非忙碌狀態。命令資料在此上下文中是使用 Port ! {Owner, {command, Data}}
或 port_command/[2,3]
傳遞到連接埠的資料。請注意,這些限制僅涉及尚未到達連接埠的命令資料。忙碌連接埠功能可用於已到達連接埠的資料。
有效的限制是範圍 [ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX]
中的值。限制會自動調整為合理的值。也就是說,系統會調整值,使使用的低限制低於或等於使用的高限制。預設情況下,高限制為 8 kB,低限制為 4 kB。
透過傳遞一個指向包含值 ERL_DRV_BUSY_MSGQ_READ_ONLY
的整數變數的指標,目前使用的限制會被讀取並寫回整數變數。可以透過傳遞一個指向包含有效限制的整數變數的指標來設定新的限制。傳遞的值會被寫入內部限制。然後調整內部限制。在此之後,調整後的限制會從讀取新值的整數變數寫回。值以位元組為單位。
可以使用在驅動程式使用的 driver_entry
中設定 ERL_DRV_FLAG_NO_BUSY_MSGQ
驅動程式旗標,或者透過使用 ERL_DRV_BUSY_MSGQ_DISABLED
作為限制(低或高)呼叫此函式來停用忙碌訊息佇列功能。停用此功能後,將無法再次啟用。讀取限制時,如果此功能已停用,則兩者都為 ERL_DRV_BUSY_MSGQ_DISABLED
。
如果連接埠忙碌或連接埠訊息佇列忙碌,則傳送命令資料到連接埠的處理序會被暫停。當連接埠或連接埠訊息佇列都不忙碌時,暫停的處理序會繼續執行。
有關忙碌連接埠功能的資訊,請參閱 set_busy_port
。
自 OTP R16B 開始提供
erl_drv_cond_broadcast()
void erl_drv_cond_broadcast(ErlDrvCond
*cnd);
在條件變數上廣播。也就是說,如果其他執行緒正在等待廣播的條件變數,則所有執行緒都會被喚醒。
cnd
是一個指向條件變數的指標,用於廣播。
此函式為執行緒安全。
erl_drv_cond_create()
ErlDrvCond * erl_drv_cond_create(char
*name);
建立一個條件變數並返回指向它的指標。
name
是一個字串,用於識別建立的條件變數。它用於在未來計劃的除錯功能中識別條件變數。
失敗時返回 NULL
。建立條件變數的驅動程式負責在驅動程式卸載之前銷毀它。
此函式為執行緒安全。
erl_drv_cond_destroy()
void erl_drv_cond_destroy(ErlDrvCond
*cnd);
銷毀先前由 erl_drv_cond_create
建立的條件變數。
cnd
是一個指向要銷毀的條件變數的指標。
此函式為執行緒安全。
erl_drv_cond_name()
char * erl_drv_cond_name(ErlDrvCond
*cnd);
返回指向條件名稱的指標。
cnd
是一個指向已初始化條件的指標。
注意
此函式僅用於除錯目的。
自 OTP R16B02 起提供
erl_drv_cond_signal()
void erl_drv_cond_signal(ErlDrvCond
*cnd);
在條件變數上發出訊號。也就是說,如果其他執行緒正在等待被發出訊號的條件變數,則喚醒其中一個。
cnd
是一個指向要發出訊號的條件變數的指標。
此函式為執行緒安全。
erl_drv_cond_wait()
void erl_drv_cond_wait(ErlDrvCond *cnd,
ErlDrvMutex *mtx);
等待條件變數。呼叫的執行緒會被封鎖,直到另一個執行緒透過在條件變數上發出訊號或廣播來喚醒它。在呼叫的執行緒被封鎖之前,它會解除鎖定作為引數傳遞的互斥鎖。當呼叫的執行緒被喚醒時,它會在返回之前鎖定相同的互斥鎖。也就是說,在呼叫此函式時,目前必須由呼叫的執行緒鎖定互斥鎖。
cnd
是一個指向要等待的條件變數的指標。mtx
是一個指向等待時要解除鎖定的互斥鎖的指標。
注意
即使沒有人在條件變數上發出訊號或廣播,
erl_drv_cond_wait
也可以返回。呼叫erl_drv_cond_wait
的程式碼始終要準備好讓erl_drv_cond_wait
返回,即使執行緒等待的條件尚未發生也是如此。也就是說,從erl_drv_cond_wait
返回時,請務必檢查條件是否已發生,如果沒有,則再次呼叫erl_drv_cond_wait
。
此函式為執行緒安全。
erl_drv_consume_timeslice()
int erl_drv_consume_timeslice(ErlDrvPort port,
int percent);
向執行時系統提供提示,說明自上次提示以來,或自上次沒有提示時回呼開始以來,目前的驅動程式回呼呼叫已消耗多少 CPU 時間。
port
- 執行中埠的埠控制代碼。percent
- 完整時間配額中約略消耗的百分比。
時間指定為完整時間配額的百分比,埠在將 CPU 讓給其他可執行的埠或進程之前,允許執行的時間配額百分比。有效範圍是 [1, 100]
。排程時間配額不是一個確切的實體,但通常可以約略估計為大約 1 毫秒。
請注意,由執行時系統決定是否以及如何使用此資訊。某些平台上的實作可以使用其他方法來決定時間配額的消耗比例。無論如何,冗長的驅動程式回呼都應頻繁呼叫此函式,以判斷是否允許繼續執行。
如果時間配額已用完,則此函式會返回非零值,如果允許回呼繼續執行,則返回零。如果返回非零值,則驅動程式回呼應盡快返回,以便埠能夠讓出控制權。
提供此函式是為了更好地支援合作式排程、提高系統回應能力,並更容易防止由於埠獨佔排程器執行緒而導致的 VM 行為異常。當將冗長的工作劃分為一些重複的驅動程式回呼呼叫時,可以使用它,而無需使用執行緒。
另請參閱本手冊頁開頭的重要 警告 文字。
自 OTP R16B 開始提供
erl_drv_convert_time_unit()
ErlDrvTime erl_drv_convert_time_unit(ErlDrvTime
val, ErlDrvTimeUnit from, ErlDrvTimeUnit to);
將時間單位 from
的 val
值轉換為對應的時間單位 to
值。結果會使用 floor 函式捨去。
val
- 要轉換時間單位的值。from
-val
的時間單位。to
- 返回值的时间單位。
如果呼叫時使用無效的時間單位引數,則返回 ERL_DRV_TIME_ERROR
。
另請參閱 ErlDrvTime
和 ErlDrvTimeUnit
。
自 OTP 18.3 起可用
erl_drv_equal_tids()
int erl_drv_equal_tids(ErlDrvTid tid1,
ErlDrvTid tid2);
比較兩個執行緒識別碼 tid1
和 tid2
是否相等。
如果不相等,則返回 0
,如果相等,則返回不等於 0
的值。
注意
執行緒終止後,可以非常快速地重複使用執行緒識別碼。因此,如果自儲存執行緒識別碼以來,對應於其中一個涉及的執行緒識別碼的執行緒已終止,則
erl_drv_equal_tids
的結果可能不會產生預期的結果。
此函式為執行緒安全。
erl_drv_getenv()
int erl_drv_getenv(const char *key, char
*value, size_t *value_size);
擷取環境變數的值。
key
- 包含環境變數名稱的NULL
終止字串。value
- 指向輸出緩衝區的指標。value_size
- 指向整數的指標。整數用於傳遞輸入和輸出大小(請參閱下文)。
呼叫此函式時,*value_size
必須包含 value
緩衝區的大小。
成功時,返回 0
,環境變數的值已寫入 value
緩衝區,並且 *value_size
包含寫入 value
緩衝區的值的字串長度(不包括終止 NULL
字元)。
失敗時,也就是說,未找到此類環境變數,則返回 < 0
的值。當 value
緩衝區的大小太小時,將返回 > 0
的值,並且 *value_size
已設定為所需的緩衝區大小。
警告
此函式讀取
os:getenv/1
使用的模擬環境,而不是 libc 的getenv(3)
或類似函式使用的環境。需要這些同步的驅動程式需要自行執行此操作,但請記住,它們之所以隔離是有原因的;getenv(3)
及其朋友不是執行緒安全的,可能會導致不相關的程式碼行為異常或使模擬器崩潰。
此函式為執行緒安全。
erl_drv_init_ack()
void erl_drv_init_ack(ErlDrvPort port,
ErlDrvData res);
確認埠的啟動。
port
- 正在執行確認的埠(驅動程式執行個體)的埠控制代碼。res
- 埠初始化的結果。可以是與start
的傳回值相同的值,也就是說,任何錯誤程式碼或將用於此埠的ErlDrvData
。
呼叫此函式時,啟動的 erlang:open_port
呼叫會被返回,就像剛呼叫 start
函式一樣。只有在連結驅動程式上設定了標誌 ERL_DRV_FLAG_USE_INIT_ACK
時才能使用它。
自 OTP 19.0 起可用
erl_drv_monotonic_time()
ErlDrvTime erl_drv_monotonic_time(ErlDrvTimeUnit time_unit);
返回 Erlang 單調時間。請注意,負值並不少見。
time_unit
是傳回值的時間單位。
如果呼叫時使用無效的時間單位引數,或從不是排程器執行緒的執行緒呼叫,則返回 ERL_DRV_TIME_ERROR
。
另請參閱 ErlDrvTime
和 ErlDrvTimeUnit
。
自 OTP 18.3 起可用
erl_drv_mutex_create()
ErlDrvMutex * erl_drv_mutex_create(char
*name);
建立一個互斥鎖並返回指向它的指標。
name
是一個字串,用於識別建立的互斥鎖。它用於在除錯功能中識別互斥鎖(請參閱注意事項)。
失敗時返回 NULL
。建立互斥鎖的驅動程式負責在驅動程式卸載之前銷毀它。
此函式為執行緒安全。
注意
其中一個除錯功能是鎖定檢查器,它可以偵測鎖定順序違規,從而發現潛在的死鎖錯誤。為了使鎖定檢查器正常運作,
name
應採用"App.Type"
或"App.Type[Instance]"
格式,其中 App 是應用程式的名稱,Type 是鎖定類型的名稱,而 Instance 是每個鎖定執行個體的選用資訊。"App.Type" 應是唯一的名稱,以便鎖定檢查器偵測不同類型鎖定之間的鎖定順序違規。目前會忽略 Instance 資訊。例如,如果我們有類型為 "myapp.xtable" 和 "myapp.xitem" 的互斥鎖,則鎖定檢查器會確保永遠不會在 "myapp.xitem" 鎖定之後鎖定 "myapp.xtable" 鎖定,反之亦然。
erl_drv_mutex_destroy()
void erl_drv_mutex_destroy(ErlDrvMutex
*mtx);
銷毀先前由 erl_drv_mutex_create
建立的互斥鎖。互斥鎖在銷毀前必須處於未鎖定的狀態。
mtx
是一個指向要銷毀的互斥鎖的指標。
此函式為執行緒安全。
erl_drv_mutex_lock()
void erl_drv_mutex_lock(ErlDrvMutex
*mtx);
鎖定一個互斥鎖。呼叫的執行緒會被阻塞,直到互斥鎖被鎖定為止。目前已鎖定互斥鎖的執行緒不能再次鎖定同一個互斥鎖。
mtx
是一個指向要鎖定的互斥鎖的指標。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓互斥鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_mutex_name()
char * erl_drv_mutex_name(ErlDrvMutex
*mtx);
返回指向互斥鎖名稱的指標。
mtx
是一個指向已初始化的互斥鎖的指標。
注意
此函式僅用於除錯目的。
自 OTP R16B02 起提供
erl_drv_mutex_trylock()
int erl_drv_mutex_trylock(ErlDrvMutex
*mtx);
嘗試鎖定一個互斥鎖。目前已鎖定互斥鎖的執行緒不能嘗試再次鎖定同一個互斥鎖。
mtx
是一個指向要嘗試鎖定的互斥鎖的指標。
成功時返回 0
,否則返回 EBUSY
。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓互斥鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_mutex_unlock()
void erl_drv_mutex_unlock(ErlDrvMutex
*mtx);
解除鎖定一個互斥鎖。互斥鎖目前必須由呼叫的執行緒鎖定。
mtx
是一個指向要解除鎖定的互斥鎖的指標。
此函式為執行緒安全。
erl_drv_output_term()
int erl_drv_output_term(ErlDrvTermData port,
ErlDrvTermData* term, int n);
將特殊驅動程式詞彙格式的資料傳送到埠擁有者程序。這是一種從驅動程式傳遞詞彙資料的快速方法。它不需要二進位轉換,因此埠擁有者程序會將資料接收為一般的 Erlang 詞彙。erl_drv_send_term
函式可用於傳送到本機節點上的任何程序。
注意
參數
port
不是一般的埠控制代碼,而是使用driver_mk_port
轉換的埠控制代碼。
參數 term
指向一個具有 n
個元素的 ErlDrvTermData
陣列。此陣列包含以驅動程式詞彙格式描述的詞彙。每個詞彙在陣列中由 1-4 個元素組成。第一個詞彙具有詞彙類型,然後是參數。參數 port
指定傳送埠。
元組、映射和列表(字串除外,請參閱下文)是以逆波蘭表示法建構的,因此要建構一個元組,會先指定元素,然後指定元組詞彙,並帶有計數。列表和映射也是如此。
- 必須指定元組的元素數量。(元素位於
ERL_DRV_TUPLE
詞彙之前。) - 必須使用索引鍵-值對的數量
N
指定映射。索引鍵-值對必須按照此順序位於ERL_DRV_MAP
之前:key1,value1,key2,value2,...,keyN,valueN
。不允許重複的索引鍵。 - 必須指定列表的元素數量,包括尾部,也就是位於
ERL_DRV_LIST
前的最後一個詞彙。
特殊詞彙 ERL_DRV_STRING_CONS
用於在列表中「拼接」字串,以這種方式指定的字串本身不是列表,但元素是周圍列表的元素。
Term type Arguments
--------- ---------
ERL_DRV_NIL
ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
ERL_DRV_INT ErlDrvSInt integer
ERL_DRV_UINT ErlDrvUInt integer
ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
ERL_DRV_STRING char *str, int len
ERL_DRV_TUPLE int sz
ERL_DRV_LIST int sz
ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port)
or driver_caller(ErlDrvPort port))
ERL_DRV_STRING_CONS char *str, int len
ERL_DRV_FLOAT double *dbl
ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
ERL_DRV_MAP int sz
無符號整數資料類型 ErlDrvUInt
和帶符號整數資料類型 ErlDrvSInt
在 64 位元執行時期系統上為 64 位元寬,在 32 位元執行時期系統上為 32 位元寬。它們是在 ERTS 5.6 中引入的,並取代了上面清單中的某些 int
引數。
無符號整數資料類型 ErlDrvUInt64
和帶符號整數資料類型 ErlDrvSInt64
始終為 64 位元寬。它們是在 ERTS 5.7.4 中引入的。
要建構元組 {tcp, Port, [100 | Binary]}
,可以進行以下呼叫。
ErlDrvBinary* bin = ...
ErlDrvPort port = ...
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("tcp"),
ERL_DRV_PORT, driver_mk_port(drvport),
ERL_DRV_INT, 100,
ERL_DRV_BINARY, bin, 50, 0,
ERL_DRV_LIST, 2,
ERL_DRV_TUPLE, 3,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
這裡 bin
是一個長度至少為 50 的驅動程式二進位檔,而 drvport
是一個埠控制代碼。請注意,ERL_DRV_LIST
位於列表元素之後,ERL_DRV_TUPLE
也是如此。
ERL_DRV_STRING_CONS
詞彙是一種建構字串的方式。它的工作方式與 ERL_DRV_STRING
的工作方式不同。ERL_DRV_STRING_CONS
會以相反的順序(與 ERL_DRV_LIST
的工作方式相反)建構字串列表,並將新增至列表的字串串連起來。尾部必須在 ERL_DRV_STRING_CONS
之前指定。
ERL_DRV_STRING
會建構一個字串,並結束它。(所以它與 ERL_DRV_NIL
後面接著 ERL_DRV_STRING_CONS
相同。)
/* to send [x, "abc", y] to the port: */
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("x"),
ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
ERL_DRV_ATOM, driver_mk_atom("y"),
ERL_DRV_NIL,
ERL_DRV_LIST, 4
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
/* to send "abc123" to the port: */
ErlDrvTermData spec[] = {
ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
ERL_DRV_EXT2TERM
詞彙類型用於傳遞以外部格式編碼的詞彙,也就是已由 erlang:term_to_binary()
、erl_interface:ei(3)
等編碼的詞彙。例如,如果 binp
是一個指向 ErlDrvBinary
的指標,其中包含以外部格式編碼的詞彙 {17, 4711}
,並且您想要將其包裝在具有標籤 my_tag
的雙元組中,也就是 {my_tag, {17, 4711}}
,您可以執行如下操作
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("my_tag"),
ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
ERL_DRV_TUPLE, 2,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
要建構映射 #{key1 => 100, key2 => {200, 300}}
,可以進行以下呼叫。
ErlDrvPort port = ...
ErlDrvTermData spec[] = {
ERL_DRV_ATOM, driver_mk_atom("key1"),
ERL_DRV_INT, 100,
ERL_DRV_ATOM, driver_mk_atom("key2"),
ERL_DRV_INT, 200,
ERL_DRV_INT, 300,
ERL_DRV_TUPLE, 2,
ERL_DRV_MAP, 2
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));
如果您想傳遞二進位檔,並且在 ErlDrvBinary
中沒有二進位檔的內容,則可以使用 ERL_DRV_BUF2BINARY
,而不是透過 driver_alloc_binary
建立 ErlDrvBinary
,然後透過 ERL_DRV_BINARY
傳遞二進位檔。如果使用 ERL_DRV_BUF2BINARY
,執行時期系統通常會更聰明地配置二進位檔。但是,如果想要傳遞的二進位檔內容已經存在於 ErlDrvBinary
中,則通常最好使用 ERL_DRV_BINARY
和相關的 ErlDrvBinary
來傳遞二進位檔。
ERL_DRV_UINT
、ERL_DRV_BUF2BINARY
和 ERL_DRV_EXT2TERM
詞彙類型是在 ERTS 5.6 中引入的。
此函式為執行緒安全。
自 OTP R16B 開始提供
erl_drv_putenv()
int erl_drv_putenv(const char *key, char
*value);
設定環境變數的值。
key
是一個包含環境變數名稱的以 NULL
結尾的字串。
value
是一個包含環境變數新值的以 NULL
結尾的字串。
成功時返回 0
,否則返回一個值 != 0
。
注意
將空字串(
""
)作為值傳遞的結果取決於平台。在某些平台上,變數值會設定為空字串,而在其他平台上,則會移除環境變數。
警告
此函式會修改
os:putenv/2
使用的模擬環境,而不是 libc 的putenv(3)
或類似函式所使用的環境。需要這些同步的驅動程式將需要自行執行同步,但請記住,它們被隔離是有原因的;putenv(3)
及其相關函式不是執行緒安全的,可能會導致不相關的程式碼行為異常或導致模擬器崩潰。
此函式為執行緒安全。
erl_drv_rwlock_create()
ErlDrvRWLock * erl_drv_rwlock_create(char
*name);
建立一個讀寫鎖,並返回指向它的指標。
name
是一個識別已建立讀寫鎖的字串。它用於在除錯功能中識別讀寫鎖(請參閱有關鎖定檢查器的附註)。
失敗時返回 NULL
。建立讀寫鎖的驅動程式負責在卸載驅動程式之前銷毀它。
此函式為執行緒安全。
erl_drv_rwlock_destroy()
void erl_drv_rwlock_destroy(ErlDrvRWLock
*rwlck);
銷毀先前由 erl_drv_rwlock_create
建立的讀寫鎖。讀寫鎖在銷毀前必須處於未鎖定的狀態。
rwlck
是一個指向要銷毀的讀寫鎖的指標。
此函式為執行緒安全。
erl_drv_rwlock_name()
char * erl_drv_rwlock_name(ErlDrvRWLock
*rwlck);
返回指向讀寫鎖名稱的指標。
rwlck
是一個指向已初始化的讀寫鎖的指標。
注意
此函式僅用於除錯目的。
自 OTP R16B02 起提供
erl_drv_rwlock_rlock()
void erl_drv_rwlock_rlock(ErlDrvRWLock
*rwlck);
讀取鎖定一個讀寫鎖。呼叫的執行緒會被阻塞,直到讀寫鎖被讀取鎖定為止。目前已讀取或讀取/寫入鎖定讀寫鎖的執行緒不能再次鎖定同一個讀寫鎖。
rwlck
是一個指向要讀取鎖定的讀寫鎖的指標。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓讀寫鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_rwlock_runlock()
void erl_drv_rwlock_runlock(ErlDrvRWLock
*rwlck);
讀取解除鎖定一個讀寫鎖。讀寫鎖目前必須由呼叫的執行緒讀取鎖定。
rwlck
是一個指向要讀取解除鎖定的讀寫鎖的指標。
此函式為執行緒安全。
erl_drv_rwlock_rwlock()
void erl_drv_rwlock_rwlock(ErlDrvRWLock
*rwlck);
讀取/寫入鎖定一個讀寫鎖。呼叫的執行緒會被阻塞,直到讀寫鎖被讀取/寫入鎖定為止。目前已讀取或讀取/寫入鎖定讀寫鎖的執行緒不能再次鎖定同一個讀寫鎖。
rwlck
是一個指向要讀取/寫入鎖定的讀寫鎖的指標。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓讀寫鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_rwlock_rwunlock()
void erl_drv_rwlock_rwunlock(ErlDrvRWLock
*rwlck);
讀取/寫入解除鎖定一個讀寫鎖。讀寫鎖目前必須由呼叫的執行緒讀取/寫入鎖定。
rwlck
是一個指向要讀取/寫入解除鎖定的讀寫鎖的指標。
此函式為執行緒安全。
erl_drv_rwlock_tryrlock()
int erl_drv_rwlock_tryrlock(ErlDrvRWLock
*rwlck);
嘗試讀取鎖定一個讀寫鎖。
rwlck
是一個指向要嘗試讀取鎖定的讀寫鎖的指標。
成功時返回 0
,否則返回 EBUSY
。目前擁有讀取鎖定或讀取/寫入鎖定的執行緒不能再次嘗試鎖定同一個讀寫鎖。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓讀寫鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_rwlock_tryrwlock()
int erl_drv_rwlock_tryrwlock(ErlDrvRWLock
*rwlck);
嘗試讀取/寫入鎖定一個讀寫鎖。目前擁有讀取鎖定或讀取/寫入鎖定的執行緒不能再次嘗試鎖定同一個讀寫鎖。
rwlck
是指向要嘗試讀取/寫入鎖定的讀寫鎖的指標。
成功時返回 0
,否則返回 EBUSY
。
警告
當您讓執行緒超出您的控制範圍時,若在模擬器執行緒中讓讀寫鎖處於鎖定狀態,很可能會造成整個模擬器死鎖。
此函式為執行緒安全。
erl_drv_send_term()
int erl_drv_send_term(ErlDrvTermData port,
ErlDrvTermData receiver, ErlDrvTermData* term, int n);
此函數是驅動程式將資料傳送至非埠擁有者程序之其他程序的唯一方式。參數 receiver
指定接收資料的程序。
注意
參數
port
不是一般的埠控制代碼,而是使用driver_mk_port
轉換的埠控制代碼。
參數 port
、term
和 n
的作用與 erl_drv_output_term
中的相同。
此函式為執行緒安全。
自 OTP R16B 開始提供
erl_drv_set_os_pid()
void erl_drv_set_os_pid(ErlDrvPort port,
ErlDrvSInt pid);
設定執行此埠的 erlang:port_info/2
時所看到的 os_pid
。
port
是要設定 pid 的埠(驅動程式實例)的埠控制代碼。pid
是要設定的 pid。
自 OTP 19.0 起可用
erl_drv_thread_create()
int erl_drv_thread_create(char *name, ErlDrvTid
*tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts
*opts);
建立一個新執行緒。
name
- 一個識別已建立執行緒的字串。它用於在計畫的未來偵錯功能中識別執行緒。tid
- 指向執行緒識別碼變數的指標。func
- 指向要在已建立執行緒中執行的函數的指標。arg
- 指向func
函數之引數的指標。opts
- 指向要使用的執行緒選項的指標,或NULL
。
成功時返回 0
,否則返回 errno
值以指示錯誤。新建立的執行緒開始在 func
所指向的函數中執行,並將 arg
作為引數傳遞給 func
。當 erl_drv_thread_create
返回時,新建立的執行緒的執行緒識別碼可在 *tid
中取得。opts
可以是指向 NULL
的指標,或指向 ErlDrvThreadOpts
結構的指標。如果 opts
是 NULL
指標,則使用預設選項,否則使用傳遞的選項。
警告
您不得自行配置
ErlDrvThreadOpts
結構。它必須由erl_drv_thread_opts_create
配置和初始化。
當 func
返回時,或如果執行緒呼叫 erl_drv_thread_exit
,則建立的執行緒會終止。執行緒的結束值會從 func
返回,或作為引數傳遞給 erl_drv_thread_exit
。建立執行緒的驅動程式負責在驅動程式卸載前,透過 erl_drv_thread_join
加入執行緒。「分離式」執行緒無法建立,也就是不需要加入的執行緒。
警告
所有建立的執行緒都必須在驅動程式卸載前由驅動程式加入。如果驅動程式無法在卸載前加入所有建立的執行緒,則在卸載驅動程式程式碼時,執行階段系統很可能會崩潰。
此函式為執行緒安全。
erl_drv_thread_exit()
void erl_drv_thread_exit(void
*exit_value);
使用作為引數傳遞的結束值終止呼叫執行緒。exit_value
是指向結束值的指標,或 NULL
。
您只允許終止使用 erl_drv_thread_create
建立的執行緒。
結束值稍後可由另一個執行緒透過 erl_drv_thread_join
擷取。
此函式為執行緒安全。
erl_drv_thread_join()
int erl_drv_thread_join(ErlDrvTid tid, void
**exit_value);
將呼叫執行緒與另一個執行緒結合,也就是說,呼叫執行緒會被封鎖,直到由 tid
識別的執行緒終止。
tid
是要加入之執行緒的執行緒識別碼。exit_value
是指向結束值指標的指標,或 NULL
。
成功時返回 0
,否則返回 errno
值以指示錯誤。
一個執行緒只能加入一次。加入多次的行為未定義,很可能會導致模擬器崩潰。如果 exit_value == NULL
,則會忽略已終止執行緒的結束值,否則已終止執行緒的結束值會儲存在 *exit_value
。
此函式為執行緒安全。
erl_drv_thread_name()
char * erl_drv_thread_name(ErlDrvTid
tid);
返回指向執行緒名稱的指標。
tid
是執行緒識別碼。
注意
此函式僅用於除錯目的。
自 OTP R16B02 起提供
erl_drv_thread_opts_create()
ErlDrvThreadOpts * erl_drv_thread_opts_create(char *name);
配置和初始化執行緒選項結構。
name
是一個識別已建立執行緒選項的字串。它用於在計畫的未來偵錯功能中識別執行緒選項。
失敗時返回 NULL
。執行緒選項結構用於將選項傳遞至 erl_drv_thread_create
。如果結構在傳遞至 erl_drv_thread_create
之前未修改,則使用預設值。
警告
您不得自行配置
ErlDrvThreadOpts
結構。它必須由erl_drv_thread_opts_create
配置和初始化。
此函式為執行緒安全。
erl_drv_thread_opts_destroy()
void erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts);
銷毀先前由 erl_drv_thread_opts_create
建立的執行緒選項。
opts
是指向要銷毀之執行緒選項的指標。
此函式為執行緒安全。
erl_drv_thread_self()
ErlDrvTid erl_drv_thread_self(void);
返回呼叫執行緒的執行緒識別碼。
此函式為執行緒安全。
erl_drv_time_offset()
ErlDrvTime erl_drv_time_offset(ErlDrvTimeUnit
time_unit);
返回 Erlang 單調時間 和 Erlang 系統時間 之間目前的時間偏移量,並將其轉換為作為引數傳遞的 time_unit
。
time_unit
是傳回值的時間單位。
如果呼叫時使用無效的時間單位引數,或從不是排程器執行緒的執行緒呼叫,則返回 ERL_DRV_TIME_ERROR
。
另請參閱 ErlDrvTime
和 ErlDrvTimeUnit
。
自 OTP 18.3 起可用
erl_drv_tsd_get()
void * erl_drv_tsd_get(ErlDrvTSDKey
key);
返回與呼叫執行緒之 key
相關聯的執行緒特定資料。
key
是執行緒特定資料索引鍵。
如果沒有資料與呼叫執行緒之 key
相關聯,則返回 NULL
。
此函式為執行緒安全。
erl_drv_tsd_key_create()
int erl_drv_tsd_key_create(char *name,
ErlDrvTSDKey *key);
建立執行緒特定資料索引鍵。
name
是一個識別已建立索引鍵的字串。它用於在計畫的未來偵錯功能中識別索引鍵。
key
是指向執行緒特定資料索引鍵變數的指標。
成功時返回 0
,否則返回 errno
值以指示錯誤。建立索引鍵的驅動程式負責在驅動程式卸載前銷毀索引鍵。
此函式為執行緒安全。
erl_drv_tsd_key_destroy()
void erl_drv_tsd_key_destroy(ErlDrvTSDKey
key);
銷毀先前由 erl_drv_tsd_key_create
建立的執行緒特定資料索引鍵。在使用此索引鍵的所有執行緒中的所有執行緒特定資料都必須清除(請參閱 erl_drv_tsd_set
)才能呼叫 erl_drv_tsd_key_destroy
。
key
是要銷毀的執行緒特定資料索引鍵。
警告
已銷毀的索引鍵很可能會很快重複使用。因此,如果您在銷毀索引鍵之前,無法清除執行緒中使用此索引鍵的執行緒特定資料,則很可能會在系統的其他部分中收到意外錯誤。
此函式為執行緒安全。
erl_drv_tsd_set()
void erl_drv_tsd_set(ErlDrvTSDKey key, void
*data);
設定與呼叫執行緒之 key
相關聯的執行緒特定資料。您只允許在執行緒完全在您的控制下時,設定執行緒的執行緒特定資料。例如,如果您在呼叫驅動程式回呼函數的執行緒中設定執行緒特定資料,則必須在從驅動程式回呼函數返回之前清除該資料,也就是設定為 NULL
。
key
是執行緒特定資料索引鍵。
data
是指向要與呼叫執行緒之 key
關聯的資料的指標。
警告
如果您在讓模擬器執行緒脫離您的控制之前,無法清除模擬器執行緒中的執行緒特定資料,則您可能永遠無法清除此資料,並且稍後在系統的其他部分中產生意外錯誤。
此函式為執行緒安全。
erl_errno_id()
char * erl_errno_id(int error);
傳回 error
中的錯誤數字對應的 Erlang 錯誤原子名稱。錯誤原子為 einval
、enoent
等。它可用於從驅動程式建立錯誤詞。
remove_driver_entry()
int remove_driver_entry(ErlDrvEntry
*de);
移除先前使用 add_driver_entry
新增的驅動程式項目 de
。
由 erl_ddll
Erlang 介面新增的驅動程式項目無法使用此介面移除。
set_busy_port()
void set_busy_port(ErlDrvPort port, int
on);
設定和取消設定埠的忙碌狀態。如果 on
為非零值,則將埠設定為忙碌。如果為零,則將埠設定為非忙碌。您通常希望將此功能與 忙碌埠訊息佇列功能結合使用。
如果埠或埠訊息佇列忙碌,則將命令資料傳送至埠的程序會被暫停。當埠或埠訊息佇列都不忙碌時,會繼續執行暫停的程序。在此內容中,命令資料是指使用 Port ! {Owner, {command, Data}}
或 port_command/[2,3]
傳遞至埠的資料。
如果在 driver_entry
中設定了 ERL_DRV_FLAG_SOFT_BUSY,則即使驅動程式已發出忙碌訊號,也可以透過 erlang:port_command(Port, Data, [force])
強制將資料輸入至驅動程式。
如需忙碌埠訊息佇列功能的資訊,請參閱 erl_drv_busy_msgq_limits
。
set_port_control_flags()
void set_port_control_flags(ErlDrvPort port,
int flags);
設定 control
驅動程式項目函數如何將資料返回至埠擁有者程序的旗標。(control
函數是從 erlang:port_control/3
呼叫。)
目前 flags
只有兩個有意義的值:0
代表資料以列表形式回傳,而 PORT_CONTROL_FLAG_BINARY
代表資料以二進制形式從 control
回傳。
另請參閱
driver_entry(3)
、erlang
、erl_ddll
、使用者指南中的 如何為 Erlang 分散式處理實現替代載體 章節