信號量 IPC 原理 信號量通信機制主要用來實現進程間同步,避免併發訪問共用資源。信號量可以標識系統可用資源的個數。最簡單的信號量為二元信號量 下圖為 Linux 信號量通信機制的概念圖。在實際應用中,兩個進程通信可能會使用多個信號量,因此,Linux 在管理時以信號量集合的概念來管理。 通常所說的 ...
信號量 IPC 原理
信號量通信機制主要用來實現進程間同步,避免併發訪問共用資源。信號量可以標識系統可用資源的個數。最簡單的信號量為二元信號量
下圖為 Linux 信號量通信機制的概念圖。在實際應用中,兩個進程通信可能會使用多個信號量,因此,Linux 在管理時以信號量集合的概念來管理。
通常所說的創建一個信號量實際上是創建了一個信號量集合,在這個信號量集合中,可能有多個信號量。整個信號量集合由以下部分組成。
1.信號量集合數據結構:在此數據結構中定義了整個信號量集合的基本屬性,如訪問許可權。
2.信號量:信號量集合使用指針指向一個由數組構成的信號量單元,在此信號量單元中存儲了各信號量的值。
信號量集合的數據結構定義如下:
from /usr/include/linux/sem.h
struct semid_ds {
struct ipc_perm sem_perm; /* permissions .. see ipc.h 許可權 */
__kernel_time_t sem_otime; /* last semop time 最近semop時間 */
__kernel_time_t sem_ctime; /* last change time 最近修改時間 */
struct sem *sem_base; /* ptr to first semaphore in array 第一個信號量 */
struct sem_queue *sem_pending; /* pending operations to be processed 阻塞信號量 */
struct sem_queue **sem_pending_last; /* last pending operation 最後一個阻塞信號量 */
struct sem_undo *undo; /* undo requests on this array undo隊列 */
unsigned short sem_nsems; /* no. of semaphores in array 信號量數 */
};
信號量的數據結構定義如下:
from /usr/src/kernels/xxx/include/linux/sem.h
xxx 為 uname -r 命令所得
struct sem {
int semval; /* current value 信號量的值 */
int sempid; /* pid of last operation 最近一個操作的進程號PID */
};
Linux 信號量管理操作
1.創建信號量集合
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/*
* 第一個參數為 key 值,一般由 ftok() 函數產生
* 第二個參數為創建的信號量個數,以數組的方式存儲
* 第三個參數用來標識信號量集合的許可權
*/
int semget(key_t key, int nsems, int semflg);
2.控制信號量集合、信號量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/*
* 第一個參數為要操作的信號量標識符
* 第二個參數,如果要操作的是信號量則它是信號量的下標;如果操作集合,此參數無意義
* 第三個參數為要執行的操作
* 第四個參數則需根據第三個參數進行設置,其類型為 senum 的共用體
*/
int semctl(int semid, int semnum, int cmd, ...);
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) */
};
3.信號量的操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/*
* 第一個參數為要操作的信號量的標識符
* 第二個參數為 sembuf 結構體
* 第三個參數為 sops 個數
*/
int semop(int semid, struct sembuf *sops, unsigned nsops);
sembuf 結構體如下:
struct sembuf {
unsigned short sem_num; /* semaphore index in array 信號量下標 */
short sem_op; /* semaphore operation 信號量操作 */
short sem_flg; /* operation flags 操作標識 */
};
sem_flg 為操作標識。可選為以下各值:
IPC_NOWAIT:在對信號量集合的操作不能執行的情況下,調用立即返回。
SEM_UNDO:當進程退出後,該進程對 sem 進行的操作將被撤銷。
程式實例
下麵用一個程式來演示 SEM_UNDO 的效果:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
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 */
};
static void sem_init(int id)
{
union semun sem;
sem.val = 10;
/* 初始化信號量的值 */
semctl(id, 0, SETVAL, sem);
}
static void sem_v(int id)
{
struct sembuf buf = {
.sem_num = 0,
.sem_op = -1,
/* 可改為 SEM_UNDO 查看結果 */
// .sem_flg = 0,
.sem_flg = SEM_UNDO,
};
/* 操作信號量 */
semop(id, &buf, 1);
}
static int get_val(int id)
{
/* 獲取信號量的值 */
return semctl(id, 0, GETVAL);
}
int main()
{
int sem_id, pid;
/* 創建信號量集合 */
sem_id = semget((key_t)1004, 1, IPC_CREAT | 0600);
sem_init(sem_id);
if((pid = fork()) == -1){
perror("fork Err");
exit(0);
}
else if(!pid){
sem_v(sem_id);
printf("child : %d \n", get_val(sem_id));
}
else{
sleep(1);
printf("parent : %d \n", get_val(sem_id));
}
return 0;
}
如果未採用 SEM_UNDO 標識,子進程輸出 9 ,父進程輸出 9;
如果採用了 SEM_UNDO 標識,子進程輸出 9,父進程輸出 10。