本文基于mstar801平台Linux2.6.35.11内核版本。
对于内核临界区域临界资源的保护,也是Linux设备驱动的关键部分。因为设备驱动牵扯到会同时被许多用户态进程使用情况。
本章我们分析Linux内核态的信号量机制。
一、首先说明操作系统PV操作
PV操作与信号灯的处理相关,P表示通过的意思(其实就是减1,当为0时不能通过),V表示释放的意思(其实就是加1)。
二、Linux内核信号量相关定义及实现
1.先看看头文件定义
kernel2.6.35.11/include/linux/semaphore.h
...... struct semaphore { spinlock_t lock; //应该是这个信号量的自旋锁 unsigned int count; //表示的是这个信号量的计数器 struct list_head wait_list; //顾名思义应该是等待链表了 }; ...... #define DECLARE_MUTEX(name) \ struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1) //定义一个信号量 extern void down(struct semaphore *sem); //获得信号量,对应P操作、即减1 extern void up(struct semaphore *sem); //释放信号量,对应V操作、即加1 ......
2.看看down和up函数的实现
kernel2.6.35.11/kernel/semaphore.c...... void down(struct semaphore *sem){ unsigned long flags; spin_lock_irqsave(&sem->lock, flags); //原子操作,加锁 if (likely(sem->count > 0)) sem->count--; //如果count>0,表示可以使用;直接减减 else __down(sem); //否则,将调用__down()函数将进程放进等待队列里边;等待后边唤醒。 spin_unlock_irqrestore(&sem->lock, flags); //释放锁 } EXPORT_SYMBOL(down); //符号导出,这是Linux内核通信手段。这样此变量在内核态全局都可用 ...... void up(struct semaphore *sem){ unsigned long flags; spin_lock_irqsave(&sem->lock, flags); if (likely(list_empty(&sem->wait_list))) sem->count++; //如果等待链表为空,表示没有正在等待此信号量的进程,count++就行 else __up(sem); //如果等待链表不为空,也就是还有进程在等待此信号量;我们还必须调用__up()函数唤醒之前进程 spin_unlock_irqrestore(&sem->lock, flags); } EXPORT_SYMBOL(up); //符号导出,这是Linux内核通信手段。这样此变量在内核态全局都可用 ......
三、如何在内核模块中使用信号量进行并发控制
eg:相关代码片段#include <linux/semaphore.h> ...... DECLARE_MUTEX(my_sem); //定义一个信号量,并置my_sem.count = 1 down(&my_sem); ...... //临界区代码 up(&my_sem); ......