關于信号量和共享記憶體的相關描述已經在前幾篇提到過;
- 信号量:即核心維護的一個正整數,可以使用核心提供的p/v接口進行該正整數的+/-操作,它主要用來表示系統中可用資源的個數,協調各個程序有序通路資源,防止發生沖突
- 共享記憶體:作業系統從運作中的程序擁有的3G使用者空間中提供一段使用者态可以直接通路的記憶體塊,并且該記憶體區域可以被其他程序共享
信号量在共享記憶體中的使用基本流程如下圖:
兩個信号量,一個用于控制讀端程序對共享記憶體的通路,一個用于控制寫端程序對共享記憶體的通路
當寫端程序寫入時,寫信号量sem1 程序p操作,讀信号量sem0進行v操作
當讀端程序寫入時,讀信号量sem0進行p操作,寫信号量sem1程序v操作
以下代碼通過信号量實作了每次僅有一個程序操作共享記憶體
寫端
sem_write.c
:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
union semnum {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int sem_id;
//初始化信号量
void sem_init(int sem_id, int nsignum, int sem_value) {
union semnum sem_union;
sem_union.val = sem_value;
if (semctl(sem_id,nsignum,SETVAL,sem_union) == -1) {
printf("semctl failed\n");
_exit(-1);
}
}
//對信号量進行p操作 -1
void sem_p(int sem_id, int nsignum) {
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
if (semop(sem_id, &sops,1) == -1) {
printf("semop P failed \n");
_exit(-1);
}
}
//對信号量進行v操作 +1
void sem_v(int sem_id, int nsignum) {
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop(sem_id, &sops,1) == -1) {
printf("semop P failed \n");
_exit(-1);
}
}
//列印信号量
void sem_print(int sem_id, int nsignum) {
int sem_value;
sem_value = semctl(sem_id, nsignum, GETVAL);
printf("sem[%d] = %d \n",nsignum, sem_value);
}
int main() {
int shm_id;
key_t shm_key = ftok("./",111);
key_t sem_key = ftok("./",112);
//建立共享記憶體,并将共享記憶體進行映射,擷取映射後的共享記憶體位址
shm_id = shmget(shm_key , 1028 , IPC_CREAT | 0666);
char *shm_addr = shmat(shm_id, NULL , 0);
sem_id = semget(sem_key,2,IPC_CREAT | 0666);//建立兩個信号量
if(sem_id == -1) {
sem_id = semget(sem_key, 2 ,0666);
}
else {
sem_init(sem_id,0,0); //初始化讀端信号量
sem_init(sem_id,1,1); //初始化寫端信号量
}
while(1) {
sem_p(sem_id,1); //由于接下來要寫入共享記憶體,則對寫端信号量進行p操作,-1
fgets(shm_addr,1020,stdin);
sem_v(sem_id,0);//由于讀程序在阻塞讀,則寫完之後立即将讀信号量進行v操作
}
return 0;
}
讀端
sem_read.c
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
union semnum {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int sem_id;
void sem_init(int sem_id, int nsignum, int sem_value) {
union semnum sem_union;
sem_union.val = sem_value;
if (semctl(sem_id,nsignum,SETVAL,sem_union) == -1) {
printf("semctl failed\n");
_exit(-1);
}
}
void sem_p(int sem_id, int nsignum) {
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
if (semop(sem_id, &sops,1) == -1) {
printf("semop P failed \n");
_exit(-1);
}
}
void sem_v(int sem_id, int nsignum) {
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop(sem_id, &sops,1) == -1) {
printf("semop P failed \n");
_exit(-1);
}
}
void sem_print(int sem_id, int nsignum) {
int sem_value;
sem_value = semctl(sem_id, nsignum, GETVAL);
printf("sem[%d] = %d \n",nsignum, sem_value);
}
int main() {
int shm_id;
key_t shm_key = ftok("./",111);
key_t sem_key = ftok("./",112);
shm_id = shmget(shm_key , 1028 , IPC_CREAT | 0666);
char *shm_addr = shmat(shm_id, NULL , 0);
sem_id = semget(sem_key,2,IPC_CREAT | 0666);
if(sem_id == -1) {
printf("semget failed\n");
_exit(-1);
}
else {
sem_init(sem_id,0,0); //初始化讀端信号量
sem_init(sem_id,1,1); //初始化寫端信号量
}
while(1) {
//對讀端信号量進行p操作,-1,接下來要從共享記憶體讀
sem_p(sem_id,0);
printf("from shm:%s\n",shm_addr);
//讀完之後将寫端信号量進行v操作,+1,因為還有寫端程序需要寫入(p操作),防止信号量變為-1
sem_v(sem_id,1);
}
return 0;
}