之前的一片博客介绍了用于Linux内核同步的自旋锁,即使用自旋锁来保护共享资源,今天介绍另外一种Linux内核同步机制——信号量。信号量在内核中的使用非常广泛,用于对各种共享资源的保护。信号量与自旋锁的实现机制是不一样的,用处也是不一样的。首先,自旋锁和信号量都使用了计数器来表示允许同时访问共享资源的最大进程数,但自旋锁的共享计数值是1,也就是说任意时刻只有一个进程在共享代码区运行;信号量却允许使用大于1的共享计数,即共享资源允许被多个不同的进程同时访问,当然,信号量的计数器也能设为1,这时信号量也称为互斥量。其次,自旋锁用于保护短时间能够完成操作的共享资源,使用期间不允许进程睡眠和进程切换;信号量常用于暂时无法获取的共享资源,如果获取失败则进程进入不可中断的睡眠状态,只能由释放资源的进程来唤醒。最后,自旋锁可以用于中断服务程序之中;信号量不能在中断服务程序中使用,因为中断服务程序是不允许进程睡眠的。关于信号量的基本知识已经讲解完毕,接下来看看信号量在内核里面的实现,本文讲解的内核版本是linux-2.6.24。
1 数据结构
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
};
信号量使用的数据结构是struct semaphore,包含三个数据成员:count是共享计数值、sleepers是等待当前信号量进入睡眠的进程个数、wait是当前信号量的等待队列。
2 信号量使用
使用信号量之前要进行初始化,其实只是简单的设置共享计数和等待队列,睡眠进程数一开始是0。本文重点讲解信号量的使用和实现。信号量操作的API:
static inline void down(struct semaphore * sem)//获取信号量,获取失败则进入睡眠状态
static inline void up(struct semaphore * sem)//释放信号量,并唤醒等待队列中的第一个进程
信号量的使用方式如下:
down(sem);
...临界区...
up(sem);
内核保证正在访问临界区的进程数小于或等于初始化的共享计数值,获取信号量失败的进程将进入不可中断的睡眠状态,在信号量的等待队列中进行等待。当进程释放信号量的时候就会唤醒等待队列中的第一个进程。
3 信号量的实现
3.1 down(sem)
首先看函数的定义:
static inline void down(struct semaphore * sem)