Linux下進程通訊之信號量集
1.簡介
?信號量集,就是由多個信號量組成的一個數組。 作為一個整體, 信號量集中所有的信號量使用同一個等待隊列。 Linux 的信號量集為進程請求多個資源創造了條件。 Linux 規定, 當進程的一個操作需要多個共享資源時, 如果只成功獲得了其中部分資源, 那么這個請求即告失敗, 進程必須立即釋放所有已獲得資源, 已防止形成死鎖。
信號量本質是一個計數器(不設置全局變量是因為進程間是相互獨立的), 用于多進程對共享數據對象的讀取, 它和管道有所不同, 它不以傳送數據為主要目 錄, 主要是用來保護共享資源(信號量也屬于臨界資源), 使得資源在同一時刻只能由一個進程獨享。
2.工作原理
信號量只能進行等待和發送信號兩種操作, 即 P(申請)和 V(釋放)。
(1) P(申請):如果 SV 的值大于 0, 就給他減 1, 如果它的值為 0, 就掛起進程。
(2) V(釋放):如果有其他進程等待信號量而被掛起, 就讓他恢復運行, 如果沒有進程因等待信號量而掛起, 就給他加 1。
在信號量進程 PV 操作時都是為了原子操作(原子操作:單指令的操作, 單條指令的執行時不會被打斷的)。
3. 二值信號量
?? 二元信號量(Binary Semaphore)是最簡單的一種鎖(互斥鎖), 它只有兩種狀態:占用與非占用。 所以它的引 用計數為 1。
4. 查看系統信號量命令
??1.查看信號量組:ipcs -s
[wbyq@wbyq 0414work]$ ipcs -s
--------- 信號量數組 -----------
鍵 semid 擁有者 權限 nsems
0xd2350092 3 wbyq 666 1
??2.查看信號量限制信息:ipcs -ls
[wbyq@wbyq 0414work]$ ipcs -ls
--------- 信號量限制 -----------
最大數組數量 = 32000
每個數組的最大信號量數目 = 32000
系統最大信號量數 = 1024000000
每次信號量調用最大操作數 = 500
信號量最大值=32767
??3.查看信號量詳細信息:ipcs -s -i
[wbyq@wbyq 0414work]$ ipcs -s -i 3
信號量數組 semid=3
uid=1000 gid=1000 cuid=1000 cgid=1000
模式=0666,訪問權限=0666
nsems = 1
otime = Fri Apr 29 10:27:21 2022
ctime = Fri Apr 29 10:25:28 2022
semnum 值 ncount zcount pid
0 1 0 0 13747
??4.創建信號量:ipcmk -S <信號量個數>
//創建信號量,信號量個數為5個
[wbyq@wbyq 0414work]$ ipcmk -S 5
信號量 id:6
//查看創建的信號量
[wbyq@wbyq 0414work]$ ipcs -s -i 6
信號量數組 semid=6
uid=1000 gid=1000 cuid=1000 cgid=1000
模式=0644,訪問權限=0644
nsems = 5
otime = 未設置
ctime = Fri Apr 29 14:17:16 2022
semnum 值 ncount zcount pid
0 0 0 0 0
1 0 0 0 0
2 0 0 0 0
3 0 0 0 0
4 0 0 0 0
??5.刪除信號量:ipcrm -s
//刪除信號量6
[wbyq@wbyq 0414work]$ ipcs -s 6
--------- 信號量數組 -----------
鍵 semid 擁有者 權限 nsems
0xd2350092 3 wbyq 666 1
0xa8132e52 4 wbyq 644 1
0x9416e553 6 wbyq 644 5
[wbyq@wbyq 0414work]$ ipcrm -s 6
//刪除后結果
[wbyq@wbyq 0414work]$ ipcs -s
--------- 信號量數組 -----------
鍵 semid 擁有者 權限 nsems
0xd2350092 3 wbyq 666 1
0xa8132e52 4 wbyq 644 1
5.信號量相關函數
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
函數功能:創建信號量
形參:key ?鍵值,ftok產生
???nsems?信號量個數,幾乎總是為1
? ? ?semflg?標志 IPC_CREAT|0666
返回值:失敗返回-1,成功返回semid
int semctl(int semid, int semnum, int cmd, …);
函數功能:信號量控制函數
形參:semid?semget函數返回值
???semnum?信號量數組下標,semnum=0表示第一個信號量
???cmd?控制命令:SETVAL 初始化信號量值
???????????IPC_RMID 刪除信號量,刪除只需要前三個參數即可
???????????GETVAL 獲取信號量值
???可變參數共用體類型(需要自己定義):
??????union semun {
?????????int val; /* 要設置的初始值 */
?????????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) */
?????????};
返回值:失敗返回-1,成功根據cmd值返回
int semop(int semid, struct sembuf *sops, size_t nsops);
函數功能: 使用或者釋放信號量pv操作
形參:semid?semget函數返回值
???sops?信號量pv操作結構體
????struct sembuf sops
????{
?????unsigned short sem_num; /* 信號量集下標,0表示第一個信號量 */
?????short sem_op; /*>0則v操作,釋放信號量
???????????<0則p操作,使用信號量*/
?????short sem_flg; / 0 表示默認操作,沒有信號量可用就等待。IPC_NOWAIT 表示不等待。 */
????}
返回值:成功返回信號量標志符,失敗返回-1;
6.創建一個信號量示例
??(1)創建信號量
#include
#include
#include
#include
#include
#include
#include
union semun
{
int val; /* 信號量值 */
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[])
{
if(argc!=2)
{
printf("格式:./a.out <信號量值>\n");
return 0;
}
key_t key=ftok("semget.c",1234);//產生鍵值
if(key==-1)
{
printf("產生鍵值失敗res=%s\n",strerror(errno));
return 0;
}
printf("key=%#x\n",key);
int semid=semget(key,1,IPC_CREAT|0666);//創建1個信號量
if(semid==-1)
{
printf("創建信號量集失敗res=%s\n",strerror(errno));
return 0;
}
printf("semid=%d\n",semid);
union semun sem;
sem.val=atoi(argv[1]);
/*初始化信號量*/
if(semctl(semid,0,SETVAL,sem))
{
printf("初始化信號量值失敗err=%s\n",strerror(errno));
return 0;
}
/*獲取信號量*/
int val=semctl(semid,0,GETVAL,NULL);
printf("信號量值:%d\n",val);
/*通過系統命令查看信號量詳細信息*/
char buff[20];
snprintf(buff,sizeof(buff),"ipcs -s -i %d",semid);
system(buff);
return 0;
}
??(2)使用信號量
#include
#include
#include
#include
#include
#include
#include
union semun
{
int val; /* 信號量值 */
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[])
{
if(argc!=2)
{
printf("格式:./a.out <使用信號值>\n");
return 0;
}
key_t key=ftok("semget.c",1234);//產生鍵值
if(key==-1)
{
printf("產生鍵值失敗res=%s\n",strerror(errno));
return 0;
}
printf("key=%#x\n",key);
int semid=semget(key,1,IPC_CREAT|0666);//創建1個信號量
if(semid==-1)
{
printf("創建信號量集失敗res=%s\n",strerror(errno));
return 0;
}
printf("semid=%d\n",semid);
/*獲取信號量*/
int val=semctl(semid,0,GETVAL,NULL);
printf("信號量值:%d\n",val);
/*pv操作*/
struct sembuf sops=
{
.sem_num=0,//信號量下標
.sem_op=atoi(argv[1]),
.sem_flg=0//阻塞等待
};
int ret=semop(semid,&sops,1);
printf("ret=%d\n",ret);
/*獲取信號量*/
val=semctl(semid,0,GETVAL,NULL);
printf("信號量值:%d\n",val);
return 0;
}
7.創建多個信號量
??(1)創建多個信號量
#include
#include
#include
#include
#include
#include
#include
union semun
{
int val; /* 信號量值 */
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[])
{
if(argc!=4)
{
printf("格式:./a.out <信號量值1> <信號量值2> <信號量值3>\n");
return 0;
}
key_t key=ftok("semget.c",1234);//產生鍵值
if(key==-1)
{
printf("產生鍵值失敗res=%s\n",strerror(errno));
return 0;
}
printf("key=%#x\n",key);
int semid=semget(key,3,IPC_CREAT|0666);//創建3個信號量
if(semid==-1)
{
printf("創建信號量集失敗res=%s\n",strerror(errno));
return 0;
}
printf("semid=%d\n",semid);
union semun sem[3];
sem[0].val=atoi(argv[1]);//初始化第一個信號量
sem[1].val=atoi(argv[2]);//初始化第二個信號量
sem[2].val=atoi(argv[3]);//初始化第三個信號量
int i=0;
for(i=0;i<3;i++)
{
/*初始化信號量*/
if(semctl(semid,i,SETVAL,sem[i]))
{
printf("初始化信號量值失敗err=%s\n",strerror(errno));
return 0;
}
/*獲取信號量*/
int val=semctl(semid,i,GETVAL,NULL);
printf("第%d個信號量值:%d\n",i,val);
}
/*通過系統命令查看信號量詳細信息*/
system("ipcs -s");
return 0;
}
??(2)使用多個信號量
#include
#include
#include
#include
#include
#include
#include
union semun
{
int val; /* 信號量值 */
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[])
{
if(argc!=2)
{
printf("格式:./a.out <使用信號值>\n");
return 0;
}
key_t key=ftok("semget.c",1234);//產生鍵值
if(key==-1)
{
printf("產生鍵值失敗res=%s\n",strerror(errno));
return 0;
}
printf("key=%#x\n",key);
int semid=semget(key,3,IPC_CREAT|0666);//創建3個信號量
if(semid==-1)
{
printf("創建信號量集失敗res=%s\n",strerror(errno));
return 0;
}
printf("semid=%d\n",semid);
int i=0;
/*獲取信號量*/
for(i=0;i<3;i++)
{
/*獲取信號量*/
int val=semctl(semid,i,GETVAL,NULL);
printf("第%d個信號量值:%d\n",i,val);
}
/*pv操作*/
struct sembuf sops[3]=
{
{
.sem_num=0,//信號量下標
.sem_op=atoi(argv[1]),
.sem_flg=0//阻塞等待
},
{
.sem_num=1,//信號量下標
.sem_op=atoi(argv[1]),
.sem_flg=0//阻塞等待
},
{
.sem_num=2,//信號量下標
.sem_op=atoi(argv[1]),
.sem_flg=0//阻塞等待
}
};
int ret=semop(semid,sops,3);//一次使用3個信號量
printf("ret=%d\n",ret);
/*獲取信號量*/
/*獲取信號量*/
for(i=0;i<3;i++)
{
/*獲取信號量*/
int val=semctl(semid,i,GETVAL,NULL);
printf("第%d個信號量值:%d\n",i,val);
}
return 0;
}
??(3)運行效果
-
Linux
+關注
關注
87文章
11292瀏覽量
209323 -
IPC
+關注
關注
3文章
346瀏覽量
51902 -
進程
+關注
關注
0文章
203瀏覽量
13960
發布評論請先 登錄
相關推薦
評論