目录
1.信号量的作用
信号量也是进程间的通信方式之一,更多的应用于实现进程间的同步与互斥。(进程/线程安全概念),保证进程间对临界资源的安全有序访问,同步保证的是有序,互斥保证的是安全。
同步:保证对临界资源访问的时序可控性
互斥:对临界资源同一时间的唯一访问性
多个进程同时操作一个临界资源的时候就需要通过同步与互斥机制来实现临界资源的安全访问。
2.信号量的本质
信号量的本质是具有一个等待队列的计数器(代表现在是否有资源使用)
3.信号量如何实现同步与互斥
当信号量没有资源可用时,这时候需要阻塞等待(当有资源的时候会打断这个阻塞)
a.对于同步来说:
只有信号量资源计数从0转变为1的时候,会同期其他进程打断阻塞等待,会操作临界资源,也就是说我们释放了资源(即计数器+1操作后)才能获取资源(即计数器-1操作)然后进行操作。
b.对于互斥来说:
信号量如果想要实现互斥,那么它的计数器只能是0或1,(一元信号量),即就是一个进程获取的计数器的资源,其他简称就没法获取了)也就代表了同一时间只有一个进程能够获得信号量,保证访问的安全。
进程在操作临界资源之前先获取信号量,判断是否可以对临界资源进行操作,如果没有信号量资源(计数器为0),则需要等待,当别人释放信号量资源后,信号量计数器变为1,则会唤醒等待的进程去重新获取信号量资源。【信号量计数器大于0,代表有资源可以获取,信号量计数器等于0,代表信号量没有资源,需要等待】
信号量作为进程间的通信方式,意味着大家都能访问到信号量,信号量实际上也是一个临界资源,当然信号量这个临界资源的操作是不会出问题的(因为信号量的操作是以个原子操作,是不可被打断的)
4.信号量的操作步骤
(1)semget:创建信号量
(2)semctl: SETVAL SETALL:设置信号量的初值、
对临界资源操作之前先获取信号量(计数器减1操作)
对临界资源操作完毕之后需要西方的资源(计数器加1操作)
代码演示:
//这是一个基于信号量的互斥实现代码
//让一个进程打印A睡1000ms
//让另一个进程打印B睡1000ms然后在打印一个B
//查看结果是否有序
//通过同步与互斥实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define IPC_KEY 0x12345678
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;
};
void sem_P(int id)
{
struct sembuf buf;
buf.sem_num =0;
buf.sem_op= -1;
buf.sem_flg=SEM_UNDO;
semop(id, &buf,1);
}
void sem_V(int id)
{
struct sembuf buf;
buf.sem_num =0;
buf.sem_op= 1;
buf.sem_flg=SEM_UNDO;
semop(id, &buf,1);
}
int main()
{
int pid = -1;
//1.创建或打开一个信号量
int semid=semget(IPC_KEY,1,IPC_CREAT | 0664);
if(semid < 0){
perror("semget error");
return -1;
}
//2.设置信号量初值,只能设置一次,不能重复设置
//int semctl(int semid, int semnum, int cmd, ...);
//semid 操作句柄
//senum 指定要操作第几个信号量
// cmd 具体的操作
// SETVAL 设置单个信号量的初值
// SETALL 设置所有信号量的初值,semnum将被忽略
// IPC_RMID
// ... 是一个不定参数
// 比如要获取信号量的信息,那么第四个参数就是结构体
// 比如要设置信号量的值,那么放的就是指的联合体
union semun val;
val.val =1;
semctl(semid ,0,SETVAL,val);
pid = fork();
if(pid < 0){
perror("fork error");
}else if(pid ==0){
//打印A
while(1){
//获取信号量
sem_P(semid);
printf("A");
fflush(stdout);
usleep(1000);
printf("A ");
fflush(stdout);
//释放信号量
sem_V(semid);
}
}else{
//打印B
while(1){
sem_P(semid);
printf("B");
fflush(stdout);
usleep(100);
printf("B ");
fflush(stdout);
sem_V(semid);
}
}
return 0;
}