文章目錄
- 概念描述
- 通信原理
- 程式設計接口
- 使用流程
- 程式設計案例
概念描述
- 英文:
簡稱SEM,主要用來進行程序間同步semaphore
- 本質:核心維護的一個正整數,可對其進行各種+/-操作
- 分類:systemV 信号量、POSIX 有名信号量、POSIX 無名信号量
- 用途:用來标示系統中可用資源的的個數,協調各個程序有序的通路資源,防止發生沖突
- P操作:程式在進入臨界區之前要對資源進行申請
- V操作:程式離開臨界區後要釋放相應的資源
通信原理
- 類似于房卡,不是單個值,而是一組(實際上是數組)信号量元素構成
- 将信号量初始設定成一個絕對值
- 在信号量目前值的基礎上加一個數量
- 在信号量目前值的基礎上減去一個數量,降到0以下再去通路會引起阻塞
- 阻塞程序一直等待其他程序修改該信号量的值,直到恢複正常運作
- 信号量本身無意義,通常會與一塊臨界資源(共享記憶體)關聯使用
程式設計接口
-
擷取信号量ID
a. 頭檔案
b. <sys/ipc.h> <sys/sem.h>
c. 函數參數:int semget(key_t key,int nsem, int semflg)
-
用來表示信号量的鍵,通常使用值IPC_PRIVATE或者由ftok建立key:
-
: 信号的數量,所有的信号量放在一個數組裡nsem
-
semflg
:位掩碼,用來設定信号量的權限或者檢查一個已有信号量的權限
IPC_CREAT: 如果找不到指定的key相關聯的信号量,建立一個信号量集合
IPC_EXCL: 若指定了IPC_CREAT且指定key相關聯的信号量存在,報EXIST錯誤
d. 傳回值
- 成功:傳回操作信号量描述符
- 失敗:傳回-1
-
設定信号量
a. 頭檔案
b. <sys/ipc.h> <sys/sem.h>
c. 函數參數:int semctl(int semid, int semnum, int cmd ..)
-
: 信号量的描述符,辨別符;用于操作信号量semid
-
: 信号量的數量,所有的信号量放在一個數組内semnum
-
cmd
:
IPC_RMID :删除信号量及相關聯的核心smid_ds資料結構
IPC_STAT: 擷取semid_ds的副本
IPC_SET: 設定semid_ds的資料結構
GETVAL: 擷取信号集中地 semnum個信号量的值
GETALL: 擷取所有信号量的值
SETVAL: 設定信号集中的第semnum個信号量的值
d. 函數傳回值
- 成功:根據cmd指令,傳回不同的值
- 失敗:-1
-
信号量P/V操作
a. 頭檔案
b. <sys/ipc.h> <sys/sem.h>
c. 函數參數:int semop(int semid,struct sembuf *sops, size_t nsops);
-
:信号的IPC辨別符semid
-
: 指向數組的指針,數組中包含了需要執行的操作sops
struct sembuf {
unsigned short sem_num; // 辨別要操作的信号集中的信号量
/*
sem_op如下資訊:
a.若大于0 将sem_op的值加到信号量值上
b. 若等于0 ,則對信号量進行檢查,确定其目前值是否為0,若為0則操作結束;若不為0,則一直阻塞,直到為0結束
c. 若小于0,則将信号量值減去sem_op,最後結果大于或等于0,操作立即結束;若最後結果小于0,則目前程序會阻塞
*/
short sem_op;
/*信号操作對标記,SEM_UNDO,IPC_NOWAIT*/
short sem_flag;
}
-
:數組的大小nsops
d. 傳回值
- 成功:根據cmd指令,傳回不同的值
- 失敗:-1,并設定errno全局變量
使用流程
- 使用semget 建立或打開一個信号量集
- 使用semctl SETVAL 或 SETALL 操作初始化集合中的信号量(其中一個程序操作即可,核心中維護,對其它程序是全局可見的)
- 使用semop操作信号量的值,多個程序通過多信号量值的操作來表示一些臨界資源的擷取和釋放
- 當所有程序不再需要信号量集時,使用semctl IPC_RMID 操作删除這個信号量集(其中一個程序操作即可)
程式設計案例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
union semnum {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int main ()
{
int sem_id;
key_t key;
if ((key =ftok(".",111)) == -1) {
printf("create key failed \n");
_exit(-1);
}
//使用semget 建立或打開一個信号量集
if ((sem_id = semget(key,3,IPC_CREAT | 0770)) == -1) {
printf("create shm_id failed \n");
_exit(-1);
}
printf("sem_id is :%d\n",sem_id);
//擷取信号量中的值
int sem_value;
sem_value = semctl(sem_id , 0, GETVAL);
printf("sem value is %d\n",sem_value);
//使用semctl SETVAL 或 SETALL 操作初始化集合中的信号量。通過sem_union設定信号量的值
union semnum sem_union;
sem_union.val = 2;
semctl(sem_id, 0, SETVAL, sem_union);
sem_value = semctl(sem_id ,0, GETVAL);
printf("sem value after reset is %d\n",sem_value);
//使用semop操作信号量,對信号量進行pv操作,這裡是-1
struct sembuf sops,sops2,sops3;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
/*sops.sem_num = 1;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
sops.sem_num = 2;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;*/
if( -1 == (semop(sem_id,&sops, 1)) ) {
printf("semop set failed \n");
_exit(-1);
}
sem_value = semctl(sem_id, 0, GETVAL);
printf("sem value after semop is %d\n",sem_value);
sleep(30);
semctl(sem_id, 0 ,IPC_RMID);
return 0;
}
sem_id is :524314
sem value is 0
sem value after reset is 2
sem value after semop is 1