信号量:用于管理对资源的访问;
共享内存:用于在程序之间高效的共享数据;
消息队列:在程序之间传递数据的一种简单方法;
一、信号量
临界代码:需要确保只有一个进程或者一个执行线程可以进入这个临界代码并拥有对资源独占式的访问权。
临界区:真正执行数据更新的代码需要独占式的执行,它们被称为临界区域;它们通常只在一个大型程序中占据一小段的代码。
信号量是一个特殊的变量,它只取正整数值,并且程序对其访问都是原子操作;只允许对它进行等待和发送信号这两种操作。
在Linux编程中,等待和发送信号都已具有特殊含义,所以用原先定义的符号来表示这种操作:
P:信号量变量:用于等待;
V:信号量变量:用于发送信号;
1、信号量的定义:
最简单的信号量是只能取值0和1的变量,即二进制的信号量,这也是信号量最常见的一种形式。可以取多个正整数值的信号量被称为通用信号量。
PV操作的定义非常简单,假设有一个信号量变量sv
P(sv) | 如果sv的值大于0,就给她减1;如果它的值等于0,就挂起该进程的执行 |
V(sv) | 如果有其他进程因等待sv而被挂起,就让它恢复运行;如果没有进程因等待sv而被挂起,就给它加1 |
即:当临界区域可用时,信号量变量sv的值是true,然后P(sv)操作将它减1使它变为false以表示临界区正在被使用;当进程离开临界区域时,使用V(sv)操作将它加1,使临界区域再次变为可用;
如果两个进程共享信号量变量sv:
伪代码对两个进程都是相同的:
semaphore sv = 1;
loop forever
{
P(sv);
critical code section;
V(sv);
noncritical code section;
}
二、Linux的信号量机制
信号量函数:
#include <sys/sem.h>
int semctl(int sem_id,int sem_num,int command,.......);
int semget(key_t key,int num_sems,int sem_flags);
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);
参数key的作用很像一个文件名,它代表程序可能要使用的某个资源,如果多个程序使用key值,它负责协调工作;
由semget函数返回并用在其他共享内存函数中的标识符也与fopen返回的FILE*文件流很相似,进程需要通过它来返回共享文件;
不同的进程可以用不同的信号量标识符来指向同一个信号量。
1、int semget(key_t key,int num_sems,int sem_flags)
作用:
创建一个新信号量或取得一个已有信号量的键。
参数:
第一个参数key:整数值,不相关的进程可以通过它访问同一个信号量;程序对所有信号量的访问都是间接的,它先提供一个键,再由系统生成一个相应的信号量标识符。只有semget函数才直接使用信号量键,所有其他的信号量函数都是使用由semget函数返回的信号量标识符。
在创建新的信号量时,你需要给键提供一个唯一的非零整数。
num_sems:指定需要的信号量数目,它总是取值为1;
sem_flags:一组标志;它低端的9个比特是该信号量的权限,其作用类似于文件的访问权限;
返回值:
成功返回一个非零正数值,就是其他信号量函数将用到的信号量标识符。如果失败,则返回-1;
2、int semop(int sem_id,sruct sembuf *sem_ops,size_t num_sem_ops)
作用:
用于改变一个信号量的值
参数:
sem_id是由semget()函数返回的信号量标识符。
sem_ops是指向一个结构数组的指针,每个数组元素至少包含以下几个成员:
struct sembuf
{
short sem_num;信号量编号,除非你要使用一组信号量,否则它一般取值为0
short sem_op;信号量在一次操作需要增加或者减少的值,通常分为+1和-1,+1:p操作,表示等待信号量变为可用;-1:v操作,表示信号量已可用
short sem_flg;通常被设置为SEM_UNDO:操作系统跟踪当前信号量,如果进程退出前没有释放信号量,操作系统负责释放该信号量
};
num_sem_ops:指出将要进行操作的信号的个数。
返回值:
成功返回0;失败返回-1