1.原理
跟进程间通信的信号量原理一模一样,只是接口函数不同,同样也有PV操作
POSIX:操作系统的标准名字,是一个行业标准的称呼
linux中总共有三种信号量
第一种:system-v IPC信号量
用于进程间
sem_get();
sem_ctl( SETVAL/GETVAL)
sem_op();
第二种: POSIX无名信号量
多用于线程间
第三种:POSIX有名信号量
用于进程间
2.POSIX无名信号量
第一个:初始化无名信号量
sem_t类型的变量就表示无名信号量
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:sem --》表示信号量
pshared --》0表示信号量在线程间使用 非零表示信号量在进程间使用
value --》信号量的值
第二个:PV操作
int sem_wait(sem_t *sem); //P操作
int sem_post(sem_t *sem); //V操作
第三个:销毁无名信号量
int sem_destroy(sem_t *sem);
3.POSIX有名信号量
第一个:创建有名信号量
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
返回值:成功 返回有名信号量指针
失败 NULL
参数:name --》有名信号量的名字,只需要写名字,不需要写路径
oflag --》O_CREAT 和 O_EXCL
mode --》0777权限
value --》有名信号量的值
第二个:PV操作
int sem_wait(sem_t *sem); //P操作
int sem_post(sem_t *sem); //V操作
第三个:删除有名信号量
int sem_unlink(const char *name);
参数:name --》有名信号量的名字
有名信号量和无名信号量的区别
区别一:有名信号量创建成功以后会在系统的/dev/shm目录下自动生成有名信号量文件
无名信号量创建成功以后,不会生产信号量文件
用无名信号量来协调生产和销售
#include "myhead.h"
//定义一个全局变量表示目前库存数量
int num=0;
//定义两个无名信号量
sem_t sem1,sem2;
//用代码来模拟生产和销售
//生产者
void *product(void *arg)
{
while(1)
{
//对第一个信号量进行p操作,不会阻塞当前线程
sem_wait(&sem1);
num++;
printf("生产了一件产品,目前产品数量是:%d\n",num);
sleep(1);
//对第二个信号量进行v操作,解救销售者
sem_post(&sem2);
}
}
//销售者
void *sale(void *arg)
{
while(1)
{
//对第二个信号量进行p操作,会阻塞当前线程
sem_wait(&sem2);
num--;
printf("销售了一件产品,目前产品数量是:%d\n",num);
sleep(1);
//对第一个信号量进行v操作,解救生产者
sem_post(&sem1);
}
}
int main()
{
pthread_t id1,id2;
//初始化两个无名信号量
sem_init(&sem1,0,1); //第一个无名信号量,值为1
sem_init(&sem2,0,0); //第二个无名信号量,值为0
//创建两个线程--》分别表示生产部门和销售部门
pthread_create(&id1,NULL,product,NULL); //生产部门
//sleep(1); //北鼻手段
pthread_create(&id2,NULL,sale,NULL); //销售部门
//阻塞
pthread_join(id1,NULL);
pthread_join(id2,NULL);
//销毁无名信号量
sem_destroy(&sem1);
sem_destroy(&sem2);
return 0;
}
创建有名信号量
#include "myhead.h"
int main()
{
//创建一个有名信号量
sem_t *sem1=sem_open("mysem1",O_CREAT,0777,1);
if(sem1==NULL)
{
perror("创建有名信号量失败!\n");
return -1;
}
}
有名信号量用于协调两个进程之间对于共享内存的访问
#include "myhead.h"
int main()
{
int shmid;
//申请2个有名信号量
sem_t *sem1=sem_open("mysem1",O_CREAT,0777,1);
if(sem1==NULL)
{
perror("新建信号量失败!\n");
return -1;
}
sem_t *sem2=sem_open("mysem2",O_CREAT,0777,0);
if(sem2==NULL)
{
perror("新建信号量失败!\n");
return -1;
}
//申请共享内存
shmid=shmget(54552,1024,IPC_CREAT|IPC_EXCL|0777);
if(shmid==-1) //发生错误
{
//依据刚才讲解的错误码原理,进一步分析产生错误的原因
if(errno==EEXIST) //说明错误的原因是共享内存已经存在
{
shmid=shmget(54552,1024,0777); //打开共享内存
}
else //说明错误是其他类型的错误
{
perror("申请共享内存失败了!\n");
return -1;
}
}
//映射得到共享内存的首地址
char *p=shmat(shmid,NULL,0);
//p指向的就是你刚才申请得到的1024字节的内存首地址
if(p==NULL)
{
perror("映射共享内存失败!\n");
return -1;
}
//p1通过共享内存发送信息给p2
while(1)
{
//对第一个信号量进行p操作,不会阻塞当前进程
sem_wait(sem1);
printf("请输入要发送给p2的信息!\n");
scanf("%s",p);
//对第二个信号量进行v操作,拯救p2让p2不会永远阻塞
sem_post(sem2);
}
}
#include "myhead.h"
// p2的代码
int main()
{
int shmid;
//申请2个有名信号量
sem_t *sem1=sem_open("mysem1",O_CREAT,0777,1);
if(sem1==NULL)
{
perror("新建信号量失败!\n");
return -1;
}
sem_t *sem2=sem_open("mysem2",O_CREAT,0777,0);
if(sem2==NULL)
{
perror("新建信号量失败!\n");
return -1;
}
//申请共享内存
shmid=shmget(54552,1024,IPC_CREAT|IPC_EXCL|0777);
if(shmid==-1) //发生错误
{
//依据刚才讲解的错误码原理,进一步分析产生错误的原因
if(errno==EEXIST) //说明错误的原因是共享内存已经存在
{
shmid=shmget(54552,1024,0777); //打开共享内存
}
else //说明错误是其他类型的错误
{
perror("申请共享内存失败了!\n");
return -1;
}
}
//映射得到共享内存的首地址
char *p=shmat(shmid,NULL,0);
//p指向的就是你刚才申请得到的1024字节的内存首地址
if(p==NULL)
{
perror("映射共享内存失败!\n");
return -1;
}
//从共享内存去访问p1存放的信息
while(1)
{
//对第二个信号量进行P操作
sem_wait(sem2);
printf("p1发送的信息是:%s\n",p);
//对第一个信号量进行v操作,拯救p1让p1不会永远阻塞
sem_post(sem1);
}
}