在上篇中我們講到了共享內存的操作,沒有看的童鞋可以點擊鏈接:
Linux共享內存的使用(一)
信號量
信號量是一個內存變量,可以被系統中的任何進程所訪問。
爲什麼引入信號量
大家可以考慮這個問題...
在使用共享內存的時候,如果客戶端在讀取數據時,恰好服務器端也在寫數據,那麼就會出現問題。
如何解決呢?
出現讀寫衝突的問題,可通過信號量機制,保證讀寫共享內存段互斥進行加以解決。
linux中的信號量
Linux中信號量是以集合的形式存在的,一個集合中存在着多個信號量。
信號量的操作函數
功能 創建一個信號量集合
頭文件 #include<sys/sem.h>
函數原型
int semget(key_t key, int nsems, int flag);
key 創建信號量集的鍵值
nsems 集合中信號量的個數
flag 信號量集的權限
返回值 >0 信號量集的ID
-1 失敗
功能 操作信號量集合
頭文件 #include<sys/sem.h>
函數原型
int semctl(int semid, int semnum, int cmd, union semun arg);
semid 信號量集ID
semnum 要操作的信號量序號
cmd 要執行的命令
IPC_STAT 取信號量集的屬性,存放在arg.buf所指單元
IPC_SET 按arg.buf所指單元的數據設置信號量集合中的
sem_perm.uid、sem_perm.gid、sem_perm.mode
三個屬性。
IPC_RMID 刪除該信號量集合
GETVAL 返回第semnum個信號量的semval值
SETVAL 用arg.val設置第semnum個信號量的semval值
GETALL 取信號量集所有信號量的值,存放在arg.array指向的
數組中
SETALL 按照arg.array所指數組的值設置信號量集中所有信號
量的值
arg 修改信號量時所需的數據或獲取信號量數據時所需的變
量(可選)
返回值 與cmd相關
功能 完成信號量集的一組操作
頭文件 #include<sys/sem.h>
函數原型
int semop(int semid, struct sembuf semoparr[], size_t
nops);
semid 信號量集ID
semoparr 存放一組信號量操作的數組
nops 數組中操作的數量
返回值 0 成功
-1 失敗
我們直接在上篇的基礎上,對服務器的代碼進行一些修改,客戶端代碼不變
//服務器端代碼
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#define SHM_KEY 99
#define SEM_KEY 88
int init_sem(int semid,int semnum,int val){
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}initval;
initval.val=val;
if(semctl(semid,semnum,SETVAL,initval)==-1) {
perror("semctl");
exit(1);
}
}
void lockforwrite(int semid){
struct sembuf action[2];
//等待所有的讀進程都完成(進行寫操作前要保證沒有讀進程執行)
action[0].sem_num=0;
action[0].sem_flg=SEM_UNDO;
action[0].sem_op=0;
//進行寫操作(申請資源)
action[1].sem_num=1;
action[1].sem_flg=SEM_UNDO;
action[1].sem_op=-1;
if(semop(semid,action,2)==-1) {
perror("semop");
exit(1);
}
}
void unlockafterwrite(int semid){
struct sembuf action[1];
//釋放寫操作資源
action[0].sem_num=1;
action[0].sem_flg=SEM_UNDO;
action[0].sem_op=+1;
if(semop(semid,action,1)==-1) {
perror("semop");
exit(1);
}
}
int main()
{
int seg_id,sem_id;
char *mem_ptr;
time_t now;
int i=30;
seg_id=shmget(SHM_KEY,100,IPC_CREAT|0777);
if(seg_id==-1)
{
perror("shmget");
exit(1);
}
mem_ptr=(char *)shmat(seg_id,0,0);
if(mem_ptr==NULL)
{
perror("shmat");
exit(1);
}
//(1)創建信號量集
if((sem_id=semget(SEM_KEY,2,IPC_CREAT|0770))==-1){
perror("semget");
exit(1);
}
//(2)對信號量進行初始化
init_sem(sem_id, 0, 0);
init_sem(sem_id, 1, +1);
while(i>0)
{
time(&now);
puts(ctime(&now));
//(3)對寫操作加鎖
lockforwrite(sem_id);
strcpy(mem_ptr,ctime(&now));
//(4)解鎖
unlockafterwrite(sem_id);
sleep(1);
i--;
}
shmctl(seg_id,IPC_RMID,NULL);
return 0;
}