semop() - Unix、Linux系統呼叫
廣告
名稱semop, semtimedop - 訊號量操作概要
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
|
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout); 描述訊號量集中每個訊號量都有以下關聯值
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid; /* process that did last op */
|
semop() 對由 semid 指示的集合中選定的訊號量執行操作。由 sops 指向的陣列中的每個 nsops 元素都指定要對單個訊號量執行的操作。此結構的元素的型別為 struct sembuf,包含以下成員
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
|
sem_flg 中識別的標誌為 IPC_NOWAIT 和 SEM_UNDO。如果操作指定 SEM_UNDO,則在程序終止時會自動撤消該操作。包含在 sops 中的操作集是原子執行的,也就是說,這些操作同時執行,並且只有在它們都能同時執行的情況下才能執行。如果並非所有操作都能立即執行,則系統呼叫的行為取決於 IPC_NOWAIT 標誌在各個 sem_flg 欄位中的存在情況,如下所述。 每個操作都在訊號量集的第 sem_num 個訊號量上執行,其中集合的第一個訊號量的編號為 0。根據 sem_op 的值,有三種類型的操作。 如果 sem_op 是一個正整數,則該操作會將此值新增到訊號量值 (semval)。此外,如果為此操作指定了 SEM_UNDO,則系統會更新此訊號量的程序撤消計數 (semadj)。此操作始終可以繼續進行 — 它永遠不會強制程序等待。呼叫程序必須對訊號量集具有更改許可權。 如果 sem_op 為零,則該程序必須對訊號量集具有讀取許可權。這是一個“等待為零”操作:如果 semval 為零,則該操作可以立即繼續。否則,如果在 sem_flg 中指定了 IPC_NOWAIT,則 semop() 失敗,並將 errno 設定為 EAGAIN(並且不執行 sops 中的任何操作)。否則,semzcnt(直到此訊號量值變為零為止的等待程序計數)將遞增一,並且程序將休眠,直到發生以下情況之一:
標籤 | 描述 |
o |
semval 變為 0,此時 semzcnt 的值將遞減。 |
o | 訊號量集被刪除:semop() 失敗,並將 errno 設定為 EIDRM。 |
o | 呼叫程序捕獲訊號:semzcnt 的值將遞減,並且 semop() 失敗,並將 errno 設定為 EINTR。 |
o | 在 semtimedop() 呼叫中指定的 timeout 時間限制過期:semop() 失敗,並將 errno 設定為 EAGAIN。 |
如果 sem_op 小於零,則該程序必須對訊號量集具有更改許可權。如果 semval 大於或等於 sem_op 的絕對值,則該操作可以立即繼續:sem_op 的絕對值將從 semval 中減去,並且,如果為此操作指定了 SEM_UNDO,則系統會更新此訊號量的程序撤消計數 (semadj)。如果 sem_op 的絕對值大於 semval,並且在 sem_flg 中指定了 IPC_NOWAIT,則 semop() 失敗,並將 errno 設定為 EAGAIN(並且不執行 sops 中的任何操作)。否則,semncnt(等待此訊號量值增加的程序計數器)將遞增一,並且程序將休眠,直到發生以下情況之一: |
o |
semval 變得大於或等於 sem_op 的絕對值,此時 semncnt 的值將遞減,sem_op 的絕對值將從 semval 中減去,並且,如果為此操作指定了 SEM_UNDO,則系統會更新此訊號量的程序撤消計數 (semadj)。 |
o | 訊號量集從系統中刪除:semop() 失敗,並將 errno 設定為 EIDRM。 |
o | 呼叫程序捕獲訊號:semncnt 的值將遞減,並且 semop() 失敗,並將 errno 設定為 EINTR。 |
o | 在 semtimedop() 呼叫中指定的 timeout 時間限制過期:系統呼叫失敗,並將 errno 設定為 EAGAIN。 |
成功完成時,將為由 sops 指向的陣列中指定的每個訊號量設定 sempid 值,以指示呼叫程序的程序 ID。此外,sem_otime 將設定為當前時間。
semtimedop() 的行為與 semop() 完全相同,只是在呼叫程序將休眠的情況下,休眠的持續時間受由 timespec 結構指定的已用時間量限制,其地址已傳遞到 timeout 引數中。如果已達到指定的時間限制,則 semtimedop() 失敗,並將 errno 設定為 EAGAIN(並且不執行 sops 中的任何操作)。如果 timeout 引數為 NULL,則 semtimedop() 的行為與 semop() 完全相同。 返回值如果成功,semop() 和 semtimedop() 返回 0;否則,它們返回 -1,其中 errno 指示錯誤。錯誤失敗時,errno 將設定為以下值之一:
標籤 | 描述 |
E2BIG | 引數 nsops 大於 SEMOPM(每次系統呼叫允許的最大運算元)。 |
EACCES | 呼叫程序不具有執行指定的訊號量操作所需的許可權,並且不具有 CAP_IPC_OWNER 功能。 |
EAGAIN | 操作無法立即繼續,並且在 sem_flg 中指定了 IPC_NOWAIT 或 timeout 中指定的時間限制已過期。 |
EFAULT | sops 或 timeout 引數中指定的地址不可訪問。 |
EFBIG | 對於某些操作,sem_num 的值小於 0 或大於或等於集合中訊號量的數量。 |
EIDRM | 訊號量集已被刪除。 |
EINTR | 在此係統呼叫中阻塞時,程序捕獲了訊號。 |
EINVAL | 訊號量集不存在,或者 semid 小於零,或者 nsops 的值為非正數。 |
ENOMEM | 某些操作的 sem_flg 指定了 SEM_UNDO,並且系統沒有足夠的記憶體來分配撤消結構。 |
ERANGE | 對於某些操作,sem_op+semval 大於 SEMVMX(semval 的實現相關的最大值)。 |
備註程序的 sem_undo 結構不會在 fork(2) 系統呼叫中繼承,但會在 execve(2) 系統呼叫中繼承。
無論在建立訊號處理程式時 SA_RESTART 標誌的設定如何,semop() 都不會在被訊號處理程式中斷後自動重新啟動。
semadj 是一個每個程序的整數,它只是指定了 SEM_UNDO 標誌的所有訊號量操作的(負)計數。當使用 semctl(2) 對 SETVAL 或 SETALL 請求直接設定訊號量的值時,所有程序中相應的 semadj 值都將被清除。 可以使用適當的 semctl(2) 呼叫檢索訊號量的 semval、sempid、semzcnt 和 semnct 值。 以下對訊號量集資源的限制會影響 semop() 呼叫:
標籤 | 描述 |
SEMOPM | 一次 semop() 呼叫允許的最大運算元 (32)(在 Linux 上,此限制可以透過 /proc/sys/kernel/sem 的第三個欄位讀取和修改)。 |
SEMVMX | semval 的最大允許值:實現相關 (32767)。 |
該實現對退出最大調整值 (SEMAEM)、系統範圍內的最大撤消結構數 (SEMMNU) 和每個程序的最大撤消條目系統引數沒有固有限制。
semtimedop() 首次出現在 Linux 2.5.52 中,隨後被反向移植到核心 2.4.22 中。 錯誤當程序終止時,其關聯的 semadj 結構集將用於撤消使用 SEM_UNDO 標誌執行的所有訊號量操作的效果。這會引發一個難題:如果這些訊號量調整中的一個(或多個)會導致嘗試將訊號量的值降低到零以下,則實現應該怎麼做?一種可能的方法是阻塞,直到所有訊號量調整都能執行。但這卻是不理想的,因為它可能會強制程序終止長時間阻塞。另一種可能性是完全忽略這些訊號量調整(有點類似於在為訊號量操作指定 IPC_NOWAIT 時失敗)。Linux 採用第三種方法:儘可能降低訊號量值(即降至零),並允許程序終止立即繼續。在核心 2.6.x(x <= 10)中,存在一個錯誤,在某些情況下,它會阻止等待訊號量值變為零的程序在值實際變為零時被喚醒。此錯誤已在核心 2.6.11 中修復。 符合標準SVr4、POSIX.1-2001。參見
廣告
|