NT 同步原語驅動程式

此頁面記錄了 ntsync 驅動程式的使用者空間 API。

ntsync 是一個支援驅動程式,用於使用者空間 NT 模擬器模擬 NT 同步原語。它存在的原因是,使用現有工具在使用者空間中實現,無法在提供準確語義的同時匹配 Windows 的效能。它完全在軟體中實現,並且不驅動任何硬體裝置。

此介面僅用作相容性工具,不應用於通用同步。相反,應使用通用的、多功能的介面,例如 futex(2) 和 poll(2)。

同步原語

ntsync 驅動程式公開三種類型的同步原語:訊號量、互斥鎖和事件。

訊號量儲存一個 32 位易失計數器,以及一個表示最大值的靜態 32 位整數。當計數器非零時,它被認為是已發訊號(也就是說,可以在沒有爭用的情況下獲取,或者會喚醒等待執行緒)。當滿足等待時,計數器減一。初始計數和最大計數都在建立訊號量時建立。

互斥鎖儲存一個 32 位易失遞迴計數,以及一個表示其所有者的 32 位易失識別符號。當其所有者為零(表示它未被擁有)時,互斥鎖被認為是已發訊號。當滿足等待時,遞迴計數遞增,所有權設定為給定的識別符號。

互斥鎖還儲存一個內部標誌,表示其先前所有者是否已死亡;這樣的互斥鎖被稱為已放棄。所有者死亡不會根據執行緒死亡自動跟蹤,而是必須使用 NTSYNC_IOC_MUTEX_KILL 進行通訊。放棄的互斥鎖本質上被認為是未擁有的。

除了零的“未擁有”語義之外,ntsync 驅動程式根本不解釋所有者識別符號的實際值。其預期用途是儲存執行緒識別符號;但是,ntsync 驅動程式實際上並不驗證呼叫執行緒是否提供一致或唯一的識別符號。

事件類似於最大計數為 1 的訊號量。它儲存一個表示它是否已發訊號的易失布林狀態。有兩種型別的事件:自動重置和手動重置。自動重置事件在滿足等待時取消發訊號;手動重置事件則不然。事件型別在建立事件時指定。

除非另有說明,否則對物件的所有操作都是原子的,並且相對於同一物件的其他操作完全有序。

物件由檔案表示。當關閉物件的所有檔案描述符時,該物件將被刪除。

字元裝置

ntsync 驅動程式建立一個單獨的字元裝置 /dev/ntsync。在該裝置上開啟的每個檔案描述都代表一個唯一例項,旨在支援單個 NT 虛擬機器。由一個 ntsync 例項建立的物件只能與由同一例項建立的其他物件一起使用。

ioctl 參考

對裝置的所有操作都透過 ioctl 完成。ioctl 呼叫中使用了四個結構

struct ntsync_sem_args {
     __u32 count;
     __u32 max;
};

struct ntsync_mutex_args {
     __u32 owner;
     __u32 count;
};

struct ntsync_event_args {
     __u32 signaled;
     __u32 manual;
};

struct ntsync_wait_args {
     __u64 timeout;
     __u64 objs;
     __u32 count;
     __u32 owner;
     __u32 index;
     __u32 alert;
     __u32 flags;
     __u32 pad;
};

根據 ioctl,結構的成員可以用作輸入、輸出或根本不用。

裝置檔案上的 ioctl 如下

NTSYNC_IOC_CREATE_SEM

建立訊號量物件。接受指向 struct ntsync_sem_args 的指標,該結構的使用方式如下

count

訊號量的初始計數。

max

訊號量的最大計數。

如果 count 大於 max,則失敗並返回 EINVAL。成功後,返回建立的訊號量的檔案描述符。

NTSYNC_IOC_CREATE_MUTEX

建立互斥鎖物件。接受指向 struct ntsync_mutex_args 的指標,該結構的使用方式如下

count

互斥鎖的初始遞迴計數。

owner

互斥鎖的初始所有者。

如果 owner 非零且 count 為零,或者如果 owner 為零且 count 非零,則該函式失敗並返回 EINVAL。成功後,返回建立的互斥鎖的檔案描述符。

NTSYNC_IOC_CREATE_EVENT

建立事件物件。接受指向 struct ntsync_event_args 的指標,該結構的使用方式如下

signaled

如果非零,則事件最初已發訊號,否則未發訊號。

manual

如果非零,則事件是手動重置事件,否則是自動重置事件。

成功後,返回建立的事件的檔案描述符。

單個物件上的 ioctl 如下

NTSYNC_IOC_SEM_POST

釋出到訊號量物件。接受指向一個 32 位整數的指標,該整數在輸入時儲存要新增到訊號量的計數,在輸出時包含其先前的計數。

如果新增到訊號量的當前計數會導致後者超過訊號量的最大計數,則 ioctl 失敗並返回 EOVERFLOW,並且訊號量不受影響。如果增加訊號量的計數導致它變為已發訊號,則在此訊號量上等待的符合條件的執行緒將被喚醒,並且訊號量的計數將適當地減少。

NTSYNC_IOC_MUTEX_UNLOCK

釋放互斥鎖物件。接受指向 struct ntsync_mutex_args 的指標,該結構的使用方式如下

owner

指定嘗試釋放此互斥鎖的所有者。

count

在輸出時,包含先前的遞迴計數。

如果 owner 為零,則 ioctl 失敗並返回 EINVAL。如果 owner 不是互斥鎖的當前所有者,則 ioctl 失敗並返回 EPERM

互斥鎖的計數將減一。如果減少互斥鎖的計數導致它變為零,則互斥鎖將被標記為未擁有和已發訊號,並且在其上等待的符合條件的執行緒將適當地被喚醒。

NTSYNC_IOC_SET_EVENT

對事件物件發訊號。接受指向一個 32 位整數的指標,該整數在輸出時包含事件的先前狀態。

符合條件的執行緒將被喚醒,並且自動重置事件將被適當地取消發訊號。

NTSYNC_IOC_RESET_EVENT

取消對事件物件發訊號。接受指向一個 32 位整數的指標,該整數在輸出時包含事件的先前狀態。

NTSYNC_IOC_PULSE_EVENT

喚醒等待事件物件的執行緒,同時將其保持在未發訊號狀態。接受指向一個 32 位整數的指標,該整數在輸出時包含事件的先前狀態。

脈衝操作可以被認為是設定,然後是重置,作為一個原子操作執行。如果兩個執行緒正在等待被脈衝的自動重置事件,則只會喚醒一個執行緒。如果兩個執行緒正在等待被脈衝的手動重置事件,則兩個執行緒都將被喚醒。但是,在這兩種情況下,事件之後都將取消發訊號,並且同時進行的讀取操作將始終報告事件為未發訊號。

NTSYNC_IOC_READ_SEM

讀取訊號量物件的當前狀態。接受指向 struct ntsync_sem_args 的指標,該結構的使用方式如下

count

在輸出時,包含訊號量的當前計數。

max

在輸出時,包含訊號量的最大計數。

NTSYNC_IOC_READ_MUTEX

讀取互斥鎖物件的當前狀態。接受指向 struct ntsync_mutex_args 的指標,該結構的使用方式如下

owner

在輸出時,包含互斥鎖的當前所有者,如果互斥鎖當前未被擁有,則為零。

count

在輸出時,包含互斥鎖的當前遞迴計數。

如果互斥鎖被標記為已放棄,則該函式失敗並返回 EOWNERDEAD。在這種情況下,countowner 設定為零。

NTSYNC_IOC_READ_EVENT

讀取事件物件的當前狀態。接受指向 struct ntsync_event_args 的指標,該結構的使用方式如下

signaled

在輸出時,包含事件的當前狀態。

manual

在輸出時,如果事件是手動重置事件,則包含 1,否則包含 0。

NTSYNC_IOC_KILL_OWNER

如果互斥鎖由給定的所有者擁有,則將其標記為未擁有和已放棄。接受一個僅輸入指標,指向表示所有者的 32 位整數。如果所有者為零,則 ioctl 失敗並返回 EINVAL。如果所有者不擁有互斥鎖,則該函式失敗並返回 EPERM

等待互斥鎖的符合條件的執行緒將被適當地喚醒(並且此類等待將失敗並返回 EOWNERDEAD,如下所述)。

NTSYNC_IOC_WAIT_ANY

輪詢物件列表中的任何一個物件,原子地最多獲取一個。接受指向 struct ntsync_wait_args 的指標,該結構的使用方式如下

timeout

以納秒為單位的絕對超時。如果設定了 NTSYNC_WAIT_REALTIME,則超時是根據 REALTIME 時鐘測量的;否則,它是根據 MONOTONIC 時鐘測量的。如果超時等於或早於當前時間,則該函式會立即返回,而不休眠。如果 timeout 等於 U64_MAX,則該函式將休眠,直到物件被髮訊號,並且不會失敗並返回 ETIMEDOUT

objs

指向 count 檔案描述符陣列的指標(指定為整數,以便該結構具有相同的大小,而與架構無關)。如果任何物件無效,則該函式失敗並返回 EINVAL

count

objs 陣列中指定的物件數。如果大於 NTSYNC_MAX_WAIT_COUNT,則該函式失敗並返回 EINVAL

owner

互斥鎖所有者識別符號。如果 objs 中的任何物件是互斥鎖,則 ioctl 將嘗試代表 owner 獲取該互斥鎖。如果 owner 為零,則 ioctl 失敗並返回 EINVAL

index

成功後,包含已發訊號的物件的索引(在 objs 中)。如果改為對 alert 發訊號,則它包含 count

alert

可選的事件物件檔案描述符。如果非零,則指定一個“警報”事件物件,如果該物件被髮訊號,則將終止等待。如果非零,則識別符號必須指向有效的事件。

flags

零個或多個標誌。目前,唯一的標誌是 NTSYNC_WAIT_REALTIME,它導致超時是根據 REALTIME 時鐘而不是 MONOTONIC 測量的。

pad

未使用,必須設定為零。

此函式嘗試獲取給定的物件之一。如果無法這樣做,則它會休眠,直到物件變為已發訊號,隨後獲取它,或者超時到期。在後一種情況下,ioctl 失敗並返回 ETIMEDOUT。即使多個物件已發訊號,該函式也只會獲取一個物件。

如果訊號量的計數非零,則認為訊號量已發訊號,並且透過將其計數減一來獲取。如果互斥鎖未被擁有或者其所有者與 owner 引數匹配,則認為互斥鎖已發訊號,並且透過將其遞迴計數遞增一併將所有者設定為 owner 引數來獲取。自動重置事件透過取消發訊號來獲取;手動重置事件不受獲取的影響。

獲取是原子的,並且相對於同一物件的其他操作完全有序。如果兩個等待操作(具有不同的 owner 識別符號)在同一互斥鎖上排隊,則只會對一個進行發訊號。如果兩個等待操作在同一訊號量上排隊,並且將值 1 釋出到該訊號量,則只會對一個進行發訊號。

如果獲取了放棄的互斥鎖,則 ioctl 失敗並返回 EOWNERDEAD。儘管這是一個失敗返回,但該函式在其他方面可以被認為是成功的。互斥鎖被標記為由給定所有者擁有(遞迴計數為 1)並且不再被放棄,並且 index 仍然設定為互斥鎖的索引。

alert 引數是一個“額外”事件,可以獨立於所有其他物件終止等待。

多次傳遞同一物件是有效的,包括透過在 objs 陣列和 alert 中傳遞同一事件。如果由於該物件被髮訊號而發生喚醒,則 index 將設定為對應於該物件的最低索引。

如果收到訊號,則該函式可能會失敗並返回 EINTR

NTSYNC_IOC_WAIT_ALL

輪詢物件列表,原子地獲取所有物件。接受指向 struct ntsync_wait_args 的指標,該結構的使用方式與 NTSYNC_IOC_WAIT_ANY 相同,只是如果不是透過警報喚醒,則 index 始終填充為零。

此函式嘗試同時獲取所有給定的物件。如果無法這樣做,則它會休眠,直到所有物件同時變為已發訊號,隨後獲取它們,或者超時到期。在後一種情況下,ioctl 失敗並返回 ETIMEDOUT,並且不修改任何物件。

當此執行緒休眠時,物件可能會變為已發訊號,隨後被取消發訊號(透過其他執行緒獲取)。只有當所有物件同時被髮訊號時,ioctl 才會獲取它們並返回。整個獲取是原子的,並且相對於任何給定物件上的其他操作完全有序。

如果獲取了放棄的互斥鎖,則 ioctl 失敗並返回 EOWNERDEAD。與 NTSYNC_IOC_WAIT_ANY 類似,所有物件仍然被標記為已獲取。請注意,如果指定了多個互斥鎖物件,則無法知道哪些物件被標記為已放棄。

與“任何”等待一樣,alert 引數是一個“額外”事件,可以終止等待。但至關重要的是,如果 objs 中的所有成員都已發訊號,或者如果 alert 已發訊號,則“所有”等待都將成功。在後一種情況下,index 將設定為 count。與“任何”等待一樣,如果同時滿足這兩個條件,則前者優先,並且將獲取 objs 中的物件。

NTSYNC_IOC_WAIT_ANY 不同,多次傳遞同一物件是無效的,並且在 objsalert 中傳遞同一物件也是無效的。如果嘗試這樣做,則該函式失敗並返回 EINVAL