SYSTEM-V IPC信号量 详解
(1)概述
1.消息队列的作用是进程之间传递消息。
2.信号量的作用是为了同步多个进程的操作
3.内核会负责维护信号量的值,并确保其值不小于 0 。
4.信号量上支持的操作
· 将信号量的值设置成一个绝对值。
· 在信号量当前值的基础上加上一个数量。
· 在信号量当前值的基础上减去一个数量。
· 等待信号量的值等于 0 。
5.二值信号量
1.它只有两种合法值: 0 和 1 ,对应一个可用的资源。
2.若当前有资源可用,则与之对应的二值信号量的值为 1 ;
3.若资源已被占用,则与之对应的二值信号量的值为 0 。
4.当进程申请资源时,如果当前信号量的那么进程会陷入阻塞,直到有其他进程释放资源,将信号量的值加 1 才能被唤醒。
6.互斥量( mutex )是用来保护临界区的,所谓临界区,是指同一时间只能容许一个进程进入。而信号量( semaphore )是用来管理资源的,资源的个数不一定是 1 ,可能同时存在多个一模一样的资源,因此容许多个进程同时使用资源。
7.信号量是互斥量的一个扩展,由于资源数目增多,增强了并行度。但是这仅仅是一个方面。更重要的区别是,互斥量和信号量解决的问题是不同的。
8.互斥量的关键在于互斥、排它,同一时间只允许一个线程访问临界区。决定了解铃还须系铃人,即加锁进程必然也是解锁进程,
9.而信号量的关键在于资源的多少和有无。申请资源的进程不一定要释放资源,信号量同样可以用于生产者 - 消费者的场景。在这种场景下,
生产者进程只负责增加信号量的值,而消费者进程只负责减少信号量的值。彼此之间通过信号量的值来同步。
10.和二值信号量相比, System V 信号量在两个维度上都做了扩展。
第一,资源的数目可以是多个。资源个数超过 1 个的信号量称为计数信号量
第二,允许同时管理多种资源,由多个计数信号量组成的一个集合称为计数信号量集,每个计数信号量管理一种资源。
11. fork前创建的信号量,父子进程的信号量是实时自动同步的
(2)创建或打开信号量
int semget(key_t key, int nsems, int semflg);
第二个参数 nsems 表示信号量集中信号量的个数。换句话说,就是要控制几种资源。大部分情况下只控制一种。
semflg 支持多种标志位。目前支持 IPC_CREAT 和 IPC_EXCL 标志位,其含义不再赘述。
(3)操作信号量
int semop(int semid, struct sembuf *sops, unsigned nsops);
第二个参数是 sembuf 类型的指针, sem_num 解决的是操作哪个信号量的问题
第三个参数是表示要同时操作几个信号量
通常的方法:
1.抢占资源型
同一个资源互斥访问:
[资源]
setctl 1 // 初始化为1
p1 p2
semop-1 semop-1 // 使用资源前,必须先占有资源
使用临界资源资源
sleep(1) sleep(1) // 将当前进程挂起1秒, 给其他进程抢占资源的机会
sem+1 sem+1 // 释放资源
2.生产者消费者类型 //(共享内存和信号量的搭配使用就是)
[资源]
setctl0 // 初始化为0
生产者 消费者
semop+1 semop-1 // 二者完全独立, 当资源消耗完后, 消费者陷入阻塞
(4)控制信号量
int semctl(int semid, int semnum, int cmd,/* union semun arg*/);
(5)通常的封装
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; /* Buffer for IPC_INFO (Linux-specific) */
};
// 获取信号量集中指定信号量的值
int sema_getval(int semid, int index)
{ union semun ignore_arg;
return semctl(semid, index, GETVAL, ignore_arg);
}
// 设置信号量集中指定信号量为value
int sema_setval(int semid, int index, int val)
{ union semun arg;
arg.val = val;
return semctl(semid, index, SETVAL, arg);
}
// 立即删除信号量集
int sema_rmid(int semid)
{ union semun ignore_arg;
int ignore_index;
return semctl(semid, ignore_index, IPC_RMID, ignore_arg);
}
/* 操作信号量 */
// 申请资源(操作一个信号量)
int semaphore_wait (int semid, int index)
{
struct sembuf operations[1];
operations[0].sem_num = index; // 操作一组信号量中的哪一个信号量
operations[0].sem_op = -1; // 信号量0的值 -1
operations[0].sem_flg |= SEM_UNDO; // 进程退出后恢复信号量(成对出现)
return semop(semid, operations, 1); // 信号量集合操作
}
// 释放资源(操作一个信号量)
int semaphore_post(int semid, int index)
{
struct sembuf operations[1];
operations[0].sem_num = index; // 操作一组信号量中的哪一个信号量
operations[0].sem_op = 1; // 信号量0的值 + 1
operations[0].sem_flg |= SEM_UNDO; // 进程退出后恢复信号量(成对出现)
return semop(semid, operations, 1);
}