1.信號量
- 信號量和P,V原語由Dijkstra提出
- 信號量
(1)互斥問題: P,V在同一個進程中
(2)同步問題:P,V在不同進程中 - 信號量值含義,S表示計數值
(1)S>0:S表示可用資源的個數
(2)S=0:表示無可用資源,無等待進程
(3)S<0:|S|表示等待隊列中進程個數,即:若當前資源計數值S=0,此時來一個進程申請資源,計數值減去1爲-1,|-1|=1表示等待隊列中的進程個數爲1 - 信號量數據結構
struct semaphore
{
int value;//信號量計數值S
pointer_PCB queue;//將等待進程的控制塊指針放到等待隊列中
}
- P原語:進程申請資源
原語:這塊代碼是原子性的,不會被其它信號所中斷,在硬件上,實際是通過關閉中斷的方式來實現的。
P(s)
{
s.value=s.value--;
if (s.value<0)
{
該進程狀態置爲等待狀態;
將該進程的PCB插入相應的等待隊列s.queue末尾
}
}
- V原語:進程歸還資源
若之前s.value<0說明有進程在等待資源,此時s.value++歸還一個資源,意味着要喚醒一個進程
V(s)
{
s.value=s.value++;
if (s.vaue <=0)
{
喚醒相應等待隊列s.queue中等待的一個進程,
改變其狀態爲就緒態;
並將其插入就緒隊列中;
}
}
2.信號量集結構
- 信號量集結構struct semid_ds
struct semid_ds {
//IPC對象都有的
struct ipc_perm sem_perm; /* Ownership and permissions */
//信號量集所特有
time_t sem_otime; /* Last semop time *///最後一次執行PV操作的時間
time_t sem_ctime; /* Last change time *///信號量集最後一個改變的時間
unsigned long sem_nsems; /* No. of semaphores in set *///信號量集中的信號量的個數
};
3.信號量集函數
- 消息隊列和共享內存有4個,而信號量集只有三個,因爲semop涵蓋了P和V操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//創建或者打開一個信號量集
int semget(key_t key, int nsems, int semflg);
//控制一個信號量集
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, size_t nsops);
- semget函數
功能:用來創建和訪問一個信號量集
原型:
int semget(key_t key, int nsems, int semflg);
參數:
key:信號集的名字
nsems:信號集中信號量的個數
semflg:由9個權限標誌構成,他們的用法和創建文件時使用的mode模式標誌是一樣的
返回值:
成功返回一個非負整數,即:該信號集的標識碼;
失敗返回-1;
- shmctl函數
功能:用於控制信號量集
原型:
int semctl(int semid, int semnum, int cmd, ...);
參數:
semid:由semget返回的信號集標識碼
semnum:信號集中信號量的序號
cmd:將要採取的動作(有3個)
最後一個參數根據命令不同而不同
返回值:
成功返回0;
失敗返回-1
- cmd
- eg:權限perms是666,信號量集中信號量的個數nsems爲1
刪除信號量集:
ipcrm -S key,eg:ipcrm -S 0x4d2
ipcs -Q semid
- eg:NetworkProgramming-master (1)\NetworkProgramming-master\P30sem.cpp
/
// Created by wangji on 19-8-13.
//
// p30 system v信號量(一)
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
// https://blog.csdn.net/a1414345/article/details/64513946
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);
//來自man semctl
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 sem_creat(key_t key)
{
int semid;
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);//IPC_EXCL表示只能創建1次
if (semid == -1)
{
ERR_EXIT("semget");
}
return semid;//返回信號量集的id
}
//僅僅打開一個創建的信號量集
int sem_open(key_t key)
{
int semid;
semid = semget(key, 0, 0);//由於信號量集已經存在了,可以不指定信號量的個數;打開選項也可以不必關心
if (semid == -1)
{
ERR_EXIT("semget");
}
return semid;
}
// 設置信號量中信號量的值
int sem_setval(int semid, int val)
{
union semun su;
su.val = val;
int ret;
ret = semctl(semid, 0, SETVAL, su);
if (ret == -1)
{
ERR_EXIT("setval")
}
return 0;
}
// 獲取信號量中信號量的值
int sem_getval(int semid)
{
int ret;
ret = semctl(semid, 0, GETVAL,0);//第4個參數忽略了,填寫0
if (ret == -1)
{
ERR_EXIT("getval")
}
printf("sem.val = %d\n", ret);
return ret;
}
//只能刪除信號量集,不能刪除信號量集中的某個信號量
int sem_d(int semid)
{
int ret;
ret = semctl(semid, 0, IPC_RMID, NULL);//信號量個數不知道的話填寫0
if (ret == -1)
{
ERR_EXIT("rm_sem")
}
return 0;
}
int main(int argc, char** argv)
{
int opt;
int semid;
semid=sem_creat(1234);
sleep(5);//過5s刪除該信號量集
sem_d(semid);
}
- 測試:執行:./sem,過5s後,執行ipcs發現信號量集會被刪除
2902