System V訊號量
目錄
- 1. System V IPC
- 概述
- IPC鍵和ftok函式
- ipc_perm結構
- 建立與開啟IPC物件
- ipcs和ipcrm命令
- 2. System V訊號量
- 計數訊號量集
- semget
- semop
- semctl
- 3. 測試程式
- 程式碼實現
- semcreate.c
- semrmid.c
- semsetvalues.c
- semgetvalues.c
- semops.c
- 執行測試
- 程式碼實現
1. System V IPC
概述
以下三種類型的IPC合稱為System V IPC:
- System V訊號量
- System V訊息佇列
- System V共享記憶體
System V IPC在訪問它們的函式和核心為它們維護的資訊上有一些類似點,主要包括:
- IPC鍵和ftok函式
- ipc_perm結構
- 建立或開啟時指定的使用者訪問許可權
- ipcs和ipcrm命令
下表彙總了所有System V IPC函式。
訊號量 | 訊息佇列 | 共享記憶體 | |
---|---|---|---|
標頭檔案 | sys/sem.h | sys/msg.h | sys/shm.h |
建立或開啟IPC的函式 | semget | msgget | shmget |
控制IPC操作的函式 | semctl | msgctl | shmctl |
IPC操作函式 | semop | msgsnd msgrcv |
shmat shmdt |
IPC鍵和ftok函式
三種類型的System V IPC都使用IPC鍵作為它們的標識,IPC鍵是一個key_t型別的整數,該型別在sys/types.h
中定義。
IPC鍵通常是由ftok
函式賦予的,該函式把一個已存在的路徑名pathname和一個非0整數id組合轉換成一個key_t值,即IPC鍵。
#include <sys/ipc.h>
//成功返回IPC鍵,失敗返回-1
key_t ftok(const char *pathname, int id);
引數說明:
- pathname在是程式執行期間必須穩定存在,不能反覆建立與刪除
- id不能為0,可以是正數或者負數
ipc_perm結構
核心給每個IPC物件維護一個資訊結構,即struct ipc_perm
結構,該結構及System V IPC函式經常使用的常值定義在sys/ipc.h
標頭檔案中。
struct ipc_perm
{
uid_t uid; //owner's user id
gid_t gid; //owner's group id
uid_t cuid; //creator's group id
gid_t cgid; //creator's group id
mode_t mode; //read-write permissions
ulong_t seq; //slot usage sequence number
key_t key; //IPC key
};
建立與開啟IPC物件
建立或開啟一個IPC物件使用相應的xxxget函式,它們都有兩個共同的引數:
- 引數key,key_t型別的IPC鍵
- 引數oflag,用於指定IPC物件的讀寫許可權(ipc_perm.mode),並選擇是建立一個新的IPC物件還是開啟一個已存在的IPC物件
對於引數key,應用程式有兩種選擇:
- 呼叫ftok,給它傳pathname和id
- 指定key為
IPC_PRIVATE
,這將保證會建立一個新的、唯一的IPC物件,但該標誌不能用於開啟已存在的IPC物件,只能是新建
對於引數oflag,如上所述,它包含讀寫許可權、建立或開啟這兩方面資訊:
- 可以指定
IPC_CREAT
標誌,其含義和Posix IPC的O_CREAT一樣 - 還可以設定為下表所示的常值來指定讀寫許可權
ipcs和ipcrm命令
- 由於System V IPC的三種類型不是以檔案系統路徑名標識的,因此無法使用ls和rm命令檢視與刪除它們
- ipcs和ipcrm分別用於檢視與刪除系統中的System V IPC
usage : ipcs -asmq -tclup
ipcs [-s -m -q] -i id
ipcs -h for help.
usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
[-Q msgkey] [-M shmkey] [-S semkey] ... ]
2. System V訊號量
計數訊號量集
我們已經知道了Posix訊號量採用計數訊號量,System V訊號量在此基礎上增加了一級複雜度,它採用計數訊號量集,計數訊號量集是由一個或多個計數訊號量構成的集合。
對於系統中的每個訊號量集,核心都維護一個struct semid_ds
資訊結構,它定義在sys/sem.h
標頭檔案中。
struct semid_ds
{
struct ipc_perm sem_perm;
struct sem *sem_base; //指向訊號量集的指標
ushort sem_nsems; //訊號量集中的訊號量個數
time_t sem_otime; //上一次呼叫semop的時間
time_t sem_ctime; //建立時間或上一次以IPC_SET呼叫semctl的時間
};
其中,sem_base是指向訊號量集的指標,訊號量集中的每個成員都對應一個struct sem結構:
struct sem
{
ushort_t semval; //訊號量的值
short sempid; //上一次成功呼叫semop,或以SETVAL、SETALL呼叫semctl的程序ID
ushort_t semncnt; //等待semval變為大於當前值的執行緒數
ushort_t semzcnt; //等待semval變為0的執行緒數
};
semget
semget用於建立一個新的訊號量集或開啟一個已存在的訊號量集。
- nsems引數指定集合中的訊號量個數,如果是開啟一個已存在的訊號量集,就把該引數設為0
- oflag引數可設定為IPC_CREAT,以及SEM_R和SEM_A指定的訪問許可權,如果是開啟一個已存在的訊號量集,就把該引數設為0
- 成功時返回一個稱為訊號量識別符號的非負整數,semop和semctrl函式將使用它
//成功返回訊號量識別符號,失敗返回-1
int semget(key_t key, int nsems, int oflag);
當實際操作為建立一個新的訊號量集時,相應semid_ds結構中與集合中每個訊號量關聯的struct sem結構並不初始化,而是在以SETVAL或SETALL命令呼叫semctrl時初始化的。
也就是說,建立一個新的System V訊號量集(semget)並將它初始化(semctl)需要兩次函式呼叫,這是System V訊號量的一個致命缺陷。
semop
使用semget開啟一個訊號量集後,對其中一個或多個訊號量值的操作就使用semop函式。
//成功返回0,失敗返回-1
int semop(int semid, struct sembuf *ops, size_t nops);
- semid指定待操作的訊號量集
- nops為集合中的訊號量個數
- ops指向一個struct sembuf型別的結構陣列,該陣列中的每個元素給目標訊號量集中某個特定的訊號量指定sem_op操作,這個特定的訊號量由sem_num指定
我們只保證sembuf含有以下三個成員,不保證這三個成員的順序,也不保證還有其他成員,因此sembuf陣列元素必須採用如ops[0].sem_num = 0所示的方法進行初始化。
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_op指定的操作有三類:
- sem_op > 0:操作內容為semval += sem_op,這對應於釋放某個訊號量控制的資源
- sem_op = 0:呼叫者阻塞等待直到semval變為0
- sem_op < 0:呼叫者阻塞等待直到semval >= abs(sem_op),呼叫者阻塞等待直到semval>這對應於分配資源
sem_flg可設定的值有:0、IPC_NOWAIT、SEM_UNDO,一般使用0,對於其餘兩個值:
- IPC_NOWAIT用於給訊號量集中某個特定訊號量設定非阻塞標誌
- SEM_UNDO為System V訊號量特有的復舊機制
semctl
semctl函式對一個訊號量集執行各種控制操作。
//成功根據cmd返回相應的非負值,失敗返回-1
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */);
- semid指定待控制的訊號量集
- semnum指定訊號量集內的某個成員,僅在cmd為GETVAL、SETVAL、GETNCNT、GETZCNT和GETPID時使用
- cmd指定控制命令
- arg是可選的,取決於cmd的具體值
第四個引數arg是可選的,取決於cmd的值,當需要用到該引數時,應用程式必須按如下結構定義union semun:
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
定義如下術語用於後續cmd說明:
- semval:訊號量的當前值
- sempid:上一次成功呼叫semop,或以SETVAL、SETALL呼叫semctl的程序ID
- semncnt:等待semval變為大於當前值的執行緒數
- semzcnt:等待semval變為0的執行緒數
System V支援下列cmd值,除非特殊說明,否則成功時返回值均為0。
cmd | 說 明 |
---|---|
GETVAL | 把semval的當前值作為函式返回值返回,它是一個unsigned short型別的整數 |
SETVAL | 把semval的值設為arg.val |
GETPID | 把sempid的當前值作為函式返回值返回 |
GETNCNT | 把semncnt的當前值作為函式返回值返回 |
GETZCNT | 把semzcnt的當前值作為函式返回值返回 |
GETALL | 返回訊號量集內每個成員的semval值,這些值通過arg.array返回,arg.array需由呼叫者分配記憶體 |
SETALL | 設定訊號量集內每個成員的semval值,這些值通過arg.array指定,此時第二個引數semnum設為0即可 |
IPC_STAT | 返回訊號量集當前的semid_ds結構,該結構通過arg.buf返回,arg.buf需由呼叫者分配記憶體,可以用該命令獲得訊號量集中的訊號量個數 |
IPC_SET | 設定訊號量集對應semid_ds結構中的sem_perm.uid、sem_perm.gid和sem_perm.mode,設定的值來自arg.buf |
IPC_RMID | 刪除由semid指定的訊號量集,此時第二個引數semnum設為0即可 |
其中,前五個命令針對的都是訊號量集semid中由semnum指定的訊號量。
3. 測試程式
程式碼實現
semcreate.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
#define SEM_MODE 0666
/*
#define SEM_MODE_OWNER SEM_R | SEM_A
#define SEM_MODE_GROUP (SEM_R >> 3) | (SEM_A >> 3)
#define SEM_MODE_OTHER (SEM_R >> 6) | (SEM_A >> 6)
#define SEM_MODE (SEM_MODE_OWNER | SEM_MODE_GROUP | SEM_MODE_OTHER)
*/
int main()
{
int nsems = 3;
int oflag = IPC_CREAT | SEM_MODE;
key_t key = ftok(FTOK_FILE, FTOK_ID);
int semid = semget(key, nsems, oflag);
if (semid >= 0)
{
printf("semcreate success, semid = %d\n", semid);
}
return 0;
}
這裡遇到個問題,SEM_MODE一開始是按註釋部分定義的,但man semget給出的三個標頭檔案都已經包含了,編譯時卻報錯,提示SEM_R和SEM_A未定義,不知道為什麼,只能用0666定義了。
semrmid.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
int main()
{
key_t key = ftok(FTOK_FILE, FTOK_ID);
int semid = semget(key, 0, 0);
semctl(semid, 0, IPC_RMID);
return 0;
}
semsetvalues.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
unsigned short *semvals;
union semun arg;
struct semid_ds seminfo;
int i;
/* 開啟semcreate建立的訊號量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 獲得訊號量集中的訊號量個數 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 設定訊號量集中每個訊號量的值 */
semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));
for (i = 0; i < nsems; i++)
{
semvals[i] = atoi(argv[i + 1]); //通過命令列引數分別指定集合中每個訊號量的值
}
arg.array = semvals;
semctl(semid, 0, SETALL, arg);
return 0;
}
semgetvalues.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
unsigned short *semvals;
union semun arg;
struct semid_ds seminfo;
int i;
/* 開啟semcreate建立的訊號量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 獲得訊號量集中的訊號量個數 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 獲得訊號量集中每個訊號量的值 */
semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));
arg.array = semvals;
semctl(semid, 0, GETALL, arg);
for (i = 0; i < nsems; i++)
{
printf("semvals[%d] = %d\n", i, semvals[i]);
}
return 0;
}
semops.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
union semun arg;
struct semid_ds seminfo;
struct sembuf *semops;
int i;
/* 開啟semcreate建立的訊號量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 獲得訊號量集中的訊號量個數 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 對訊號量集中的所有訊號量進行相同的semop操作 */
semops = (struct sembuf *)calloc(nsems, sizeof(struct sembuf));
for (i = 0; i < nsems; i++)
{
semops[i].sem_num = i;
semops[i].sem_op = atoi(argv[1]); //操作型別由命令列引數指定,>0, 0, <0
semops[i].sem_flg = 0;
}
semop(semid, semops, nsems);
return 0;
}
執行測試
- 執行semcreate,通過執行前後的ipcs -s,可以確認訊號量集建立成功
- 執行semsetvalues,將三個訊號量的值分別設為1、2、3
- 然後執行semgetvalues,列印資訊和我們設定的值一致,符合預期
- 執行semops,並指定sem_op > 0
- 然後執行semgetvalues,列印資訊顯示每個訊號量的值都增加了1,符合預期
- 執行semrmid,通過執行前後的ipcs -s,可以確認訊號量集已經從系統刪除
相關推薦
Linux程序間通訊(IPC)程式設計實踐(十)System V訊號量---PV操作經典題目
//P原語 //P(semaphore *S) wait(semaphore *S) { -- S->value; if (S->value < 0) {
System V訊號量-semget()、semop()和semctl()
簡單介紹 這是我開這個欄目的第一篇文章,順序也不是按照《Unix網路程式設計》(以下簡稱網編)的章節順序往下寫的,可能文章會提及一些前面章節的概念,遇到的話,我會做一些引導,讀者也可以自己找找相關的內容或書籍檢視。 訊號量是一種用於提供不同程序間或一個給定程
System V 訊號量(三)之用訊號量解決哲學家進餐問題
一.哲學家就餐問題 5個哲學家,5個筷子。5個哲學家圍坐在一張桌子上,筷子放在分別放在每個哲學家的兩旁。如果所有哲學家在某個時刻同時拿起左邊的筷子,那麼右邊的筷子就都被其他的哲學家拿了,造成大家都無法吃飯。但是大家都不想放下左邊的筷子(規則是先拿起左邊
System V 訊號量
訊號量概念: 1.二值訊號量:值為0或者1。若互斥鎖就是這種 2.計數訊號量:值為0~N之間的訊號量 3.計數訊號量集:就是一個或多個訊號量構成一個集合 System V訊號量就是計數訊號量集,而Posix訊號量則是單個計數訊號量 ==================
System V訊號量
目錄 1. System V IPC 概述 IPC鍵和ftok函式 ipc_perm結構 建立與開啟IPC物件 ipcs和ipcrm命令 2.
Linux中的System V訊號量
> 在程序同步,併發執行時,保證按序地訪問共享資源是十分重要的。因此引入了臨界區的概念,一次只能有一個執行緒進入臨界區完成他的指令。而訊號量(semaphore)的作用,類似於一個交通訊號燈,它負責程序協作,因此訊號量又稱為訊號燈。 在Linux系統中,它提供兩種訊號量: - **核心訊號量**,由核心控
Linux程式設計學習筆記----System V程序間通訊(訊號量)
關於System V Unix System V,是Unix作業系統眾多版本中的一支。它最初由AT&T開發,在1983年第一次釋出,因此也被稱為AT&T System V。一共發行了4個System V的主要版本:版本1、2、3和4。System V Rel
【C語言】【unix c】訊號量集(system v ipc)
二、訊號量集(system v ipc) 訊號量集就是陣列,數組裡的每個元素都是訊號量的型別 1、獲取鍵值 ftok(3) 2、使用鍵值獲取訊號量集的id semget(2) #in
程序間通訊之——訊號量(一)(system V)
system v訊號量又被稱為system v訊號量集。它的本質就是一個計數器,用來為多個程序共享的資料結構提供受控訪問。訊號量支援的操作有:使用最廣泛的訊號量為二元訊號量,它控制單個資源,對於這種訊號量而言,它只有兩種合法值: 0 和 1 ,對應一個可用的資源。若當前有資源
System V 信號量使用相關函數
mode 可選參數 訪問 訪問權限 給定 素數 per 分配 絕對值 System V 信號量 在提到Posix 信號量時,指的是二值信號量或計數信號量,而System V信號量指的是入了計數信號量集 二值信號量:其值為0或1,類似於互斥鎖,資源被鎖住時為0,資源可用為1計
【作業系統】訊號量與P、V操作
知識點: 訊號量機制主要有整形訊號量、記錄性訊號量、訊號量集機制。 訊號量是一個整形變數,根據控制物件的不同賦不同的值。訊號量可分為公用訊號量和私用訊號量兩類。 公用訊號量:實現程序間的互斥,初值=1或資源的數目 私用訊號量:實現程序間的同步,初值=0或某個整數 訊
臨界資源、臨界區、訊號量、P,V操作
一、資源:Linux上有硬體資源和軟體資源之分。程式會受到資源限制的影響,可能在這幾方面的資源限制受到影響:1.硬體方面的物理性限制(記憶體);2.系統策略的限制(允許使用的CPU時間);3.具體實現的限制(整數的長度、檔名中所允許的最大字元數)。二、臨界資源:臨界資源是一
訊號量P,V操作
訊號量是最早出現的用來解決程序同步與互斥問題的機制(也可實現程序通訊),包括一個稱為信 號量的變數及對它進行的兩個原語操作。訊號量為一個整數,我們設這個訊號量為:sem。很顯然,我們規定在sem大於等於零的時候代表可供併發程序使用的 資源實體數,sem小於零的時候,表示正在等待使用臨界區的程序的個數。根據這個
IPC- Posix與system v
同步 linu 一個 tin 減少 ces 鏈接 emctl dia 一、功能上的區別 posix和system v有什麽區別/?現在在應用時應用那一標準浮雲484212 | 瀏覽 243 次 2014-11-06 10:362014-11-19 22:36 最佳答案
system v 共享內存區
include system #include<sys/shm.h> int shmget(key_t key,size_t size,int oflag); 返回:成功則為共享內存區對象,出錯為-1 key 的值可以是ftok的返回值,也可以是IPC_PRIVA
system v 共享內存
print usr ftok 新的 byte ipc 共享 err turn #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h
pause、jobs、setitimer(2)、system v ipc(day12)
ssa 周期 types.h ros system signed job 功能 chl 一、pause(2)的使用 #include <unistd.h> int pause(void); 功能:等待信號的到來 返回值: -1 錯誤 errno被設置 只
System V IPC 之共享內存
應用 創建者 CP 機構 ons 最大 基本概念 返回 time_t IPC 是進程間通信(Interprocess Communication)的縮寫,通常指允許用戶態進程執行系列操作的一組機制: 通過信號量與其他進程進行同步 向其他進程發送消息或者從其他進程接收消息
System V IPC 之消息隊列
指南 oid 消息 如果 bye time_t 信號 lag 一個 消息隊列和共享內存、信號量一樣,同屬 System V IPC 通信機制。消息隊列是一系列連續排列的消息,保存在內核中,通過消息隊列的引用標識符來訪問。使用消息隊列的好處是對每個消息指定了特定消息類型,接收
Systemd初始化進程/RHEL 6系統中System V init命令與RHEL 7系統中systemctl命令的對比
-i 公司 ystemd n-2 lis files merge ont -c Linux操作系統的開機過程是這樣的,即從BIOS開始,然後進入Boot Loader,再加載系統內核,然後內核進行初始化,最後啟動初始化進程。初始化進程作為Linux系統的第一個進程,它需要完