一、相关概念介绍
信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目。
获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0 时,代表没有资源可用,P 操作会阻塞。
释放资源时,需要对信号量的值进行原子加一,该操作被称为 V 操作。
信号量主要用来同步进程。信号量的值如果只取 0,1,将其称为二值信号量。如果信号量的值大于 1,则称之为计数信号量。
临界资源:同一时刻,只允许被一个进程或线程访问的资源
临界区:访问临界资源的代码段
二、操作信号量的接口介绍
//操作信号量的接口介绍:
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
int semget(key_t key, int nsems, int semflg);
/*
semget()创建或者获取已存在的信号量
semget()成功返回信号量的 ID, 失败返回-1
key:两个进程使用相同的 key 值,就可以使用同一个信号量
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号
量的个数
semflg 可选: IPC_CREAT IPC_EXCL
*/
int semop(int semid, struct sembuf *sops, unsigned nsops);
/*
semop()对信号量进行改变,做 P 操作或者 V 操作
semop()成功返回 0,失败返回-1
struct sembuf
{
unsigned short sem_num; //指定信号量集中的信号量下标
short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作
short sem_flg; //SEM_UNDO
};
*/
int semctl(int semid, int semnum, int cmd, ...);
/*
semctl()控制信号量
semctl()成功返回 0,失败返回-1
cmd 选项: SETVAL IPC_RMID
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
*/
三、信号量的使用
使用信号量步骤:
1.创建信号量,并初始化
2.对信号量进行pv操作
3销毁信号量
题目:
进程 a 和进程 b 模拟访问打印机,进程 a 输出第一个字符‘A’表示开始使用打印机,输出第二个字符‘A’表示结束使用,b 进程操作与 a 进程相同。(由于打印机同一时刻只能被一个进程使用,所以输出结果不应该出现 ABAB),如图所示:
创建sem.h头文件:
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/sem.h>
union semun
{
int val;
};
void sem_init();//创建初始化,获取已有的信号量 id
void sem_p(); //对信号量进行p操作
void sem_v();//对信号量进行v操作
void sem_destroy();//销毁信号量
~
头文件对应的sem.c代码:
#include"sem.h"
int semid = -1;
void sem_init()//创建初始化,获取已有的信号量 id
{
semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
if (semid == -1)
{
semid = semget((key_t)1234,1,0600);
if(semid == -1)
{
printf("semget error\n");
return;
}
}
else
{
union semun a;
a.val = 1;
if (semctl(semid,0,SETVAL,a) == -1)
{
printf("semctl error\n");
}
}
}
void sem_p()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = -1;//p
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
printf("semop p error\n");
}
}
void sem_v()
{
struct sembuf buf;
buf.sem_num = 0;
buf.sem_op = 1;//v
buf.sem_flg = SEM_UNDO;
if(semop(semid,&buf,1) == -1)
{
printf("semop v error\n");
}
}
void sem_destroy()
{
if(semctl(semid,0,IPC_RMID) == -1)
{
printf("semctl del error\n");
}
}
线程a:
#include"sem.h"
int main()
{
sem_init();
int i = 0;
for(;i < 5; i++)
{
sem_p();
printf("A");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("A");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
sleep(10);
sem_destroy();
}
~
线程b:
#include"sem.h"
int main()
{
sem_init();
int i = 0;
for(;i < 5; i++)
{
sem_p();
printf("B");
fflush(stdout);
int n = rand() % 3;
sleep(n);
printf("B");
fflush(stdout);
sem_v();
n = rand() % 3;
sleep(n);
}
}
~
~
运行结果:BBAABBBBAABBAABBAAAA