Linux驱动程序开发005 - 内核同步技术
来源:互联网 发布:linux du命令 编辑:程序博客网 时间:2024/06/07 02:54
就像我们在操作系统里学习的那样,如果多个程序(进程或线程)同时访问临界区数据就会发生竞争。存在竞争条件的程序会产生不可预料的结果。消除竞争的方法一般就是同步的访问临界区数据(原子访问)。Linux内核提供了多种技术用来实现内核同步操作。下面我们就分别介绍。
内核同步技术
Linux内核是多进程、多线程的操作系统,它提供了相当完整的内核同步方法。作为一个总结,我们先列出内核同步方法列表,这样我们可以从总体上对内核同步技术有个了解,然后我们这分别对每个同步技术做详细介绍。
- 自旋锁
需要强调的是,自旋锁别设计用于多处理器的同步机制,对于单处理器,内核在编译时不会引入自旋锁机制,对于可抢占的内核,它仅仅被用于设置内核的抢占机制是否开启的一个开关,也就是说加锁和解锁实际变成了禁止或开启内核抢占功能。如果内核不支持抢占,那么自旋锁根本就不会编译到内核中。
内核中使用spinlock_t类型来表示自旋锁,它定义在<linux/spinlock_types.h>:
typedef struct {
#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
#endif
} spinlock_t;
typedef struct {
} raw_spinlock_t;
break_lock表示当前是否由进程在等待自旋锁,显然,它只有在支持抢占的SMP内核上才起作用。
自旋锁的实现是一个复杂的过程,说它复杂不是因为需要多少代码或逻辑来实现它,其实它的实现代码很少。自旋锁的实现跟体系结构关系密切,核心代码基本也是由汇编语言写成,与体协结构相关的核心代码都放在相关的<asm/>目录下,比如<asm/spinlock.h>。对于我们驱动程序开发人员来说,我们没有必要了解这么spinlock的内部细节,如果你对它感兴趣,请参考阅读Linux内核源代码。对于我们驱动的spinlock接口,我们只需包括<linux/spinlock.h>头文件。在我们详细的介绍spinlock的API之前,我们先来看看自旋锁的一个基本使用格式:
#include <linux/spinlock.h>
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spin_lock(&lock);
....
spin_unlock(&lock);
#include <linux/spinlock.h>
SPIN_LOCK_UNLOCKED
DEFINE_SPINLOCK
spin_lock_init( spinlock_t *)
spin_lock(spinlock_t *)
spin_unlock(spinlock_t *)
spin_lock_irq(spinlock_t *)
spin_unlock_irq(spinlock_t *)
spin_lock_irqsace(spinlock_t *,unsigned long flags)
spin_unlock_irqsace(spinlock_t *, unsigned long flags)
spin_trylock(spinlock_t *)
spin_is_locked(spinlock_t *)
- 初始化
DEFINE_SPINLOCK (lock);
spinlock_t lock = SPIN_LOCK_UNLOCKED;
spinlock_t *lock
......
spin_lock_init(lock);
- 获取锁
spin_lock:获取指定的自旋锁。
spin_lock_irq:禁止本地中断并获取自旋锁。
spin_lock_irqsace:保存本地中断状态,禁止本地中断并获取自旋锁,返回本地中断状态。
自旋锁是可以使用在中断处理程序中的,这时需要使用具有关闭本地中断功能的函数,我们推荐使用 spin_lock_irqsave,因为它会保存加锁前的中断标志,这样就会正确恢复解锁时的中断标志。如果spin_lock_irq在加锁时中断是关闭的,那么在解锁时就会错误的开启中断。
另外两个同自旋锁获取相关的函数是:
spin_trylock():尝试获取自旋锁,如果获取失败则立即返回非0值,否则返回0。
spin_is_locked():判断指定的自旋锁是否已经被获取了。如果是则返回非0,否则,返回0。
- 释放锁
spin_unlock:释放指定的自旋锁。
spin_unlock_irq:释放自旋锁并激活本地中断。
spin_unlock_irqsave:释放自旋锁,并恢复保存的本地中断状态。
- 读写自旋锁
读写自旋锁的使用也普通自旋锁的使用很类似,首先要初始化读写自旋锁对象:
// 静态初始化
rwlock_t rwlock = RW_LOCK_UNLOCKED;
//动态初始化
rwlock_t *rwlock;
...
rw_lock_init(rwlock);
read_lock(&rwlock);
...
read_unlock(&rwlock);
write_lock(&rwlock);
...
write_unlock(&rwlock);
读写自旋锁的函数类似于普通自旋锁,这里就不一一介绍了,我们把它列在下面的表中。
RW_LOCK_UNLOCKED
rw_lock_init(rwlock_t *)
read_lock(rwlock_t *)
read_unlock(rwlock_t *)
read_lock_irq(rwlock_t *)
read_unlock_irq(rwlock_t *)
read_lock_irqsave(rwlock_t *, unsigned long)
read_unlock_irqsave(rwlock_t *, unsigned long)
write_lock(rwlock_t *)
write_unlock(rwlock_t *)
write_lock_irq(rwlock_t *)
write_unlock_irq(rwlock_t *)
write_lock_irqsave(rwlock_t *, unsigned long)
write_unlock_irqsave(rwlock_t *, unsigned long)
rw_is_locked(rwlock_t *)
- 信号量(semaphore)
P:如果信号量值大于0,则递减信号量的值,程序继续执行,否则,睡眠等待信号量大于0。
V:递增信号量的值,如果递增的信号量的值大于0,则唤醒等待的进程。
信号量的值确定了同时可以有多少个进程可以同时进入临界区,如果信号量的初始值始1,这信号量就是互斥信号量(MUTEX)。对于大于1的非0值信号量,也可称为计数信号量(counting semaphore)。对于一般的驱动程序使用的信号量都是互斥信号量。
类似于自旋锁,信号量的实现也与体系结构密切相关,具体的实现定义在<asm/semaphore.h>头文件中,对于x86_32系统来说,它的定义如下:
struct semaphore {
};
信号量的使用类似于自旋锁,包括创建、获取和释放。我们还是来先展示信号量的基本使用形式:
static DECLARE_MUTEX(my_sem);
......
if (down_interruptible(&my_sem))
{
}
......
up(&my_sem)
static DECLARE_SEMAPHORE_GENERIC(name, count);
static DECLARE_MUTEX(name);
seam_init(struct semaphore *, int);
init_MUTEX(struct semaphore *);
init_MUTEX_LOCKED(struct semaphore *)
down_interruptible(struct semaphore *);
down(struct semaphore *)
down_trylock(struct semaphore *)
up(struct semaphore *)
- 初始化信号量
static DECLARE_SEMAPHORE_GENERIC(name, count);
static DECLARE_MUTEX(name);
seam_init(sem, count);
init_MUTEX(sem);
init_MUTEX_LOCKED(struct semaphore *)
- 使用信号量
down_interruptible(struct semaphore *);
down(struct semaphore *)
down_trylock(struct semaphore *)
up(struct semaphore *)
down函数会尝试获取指定的信号量,如果信号量已经被使用了,则进程进入不可中断的睡眠状态。down_interruptible则会使进程进入可中断的睡眠状态。关于进程状态的详细细节,我们在内核的进程管理里在做详细介绍。
down_trylock尝试获取信号量, 如果获取成功则返回0,失败则会立即返回非0。
当退出临界区时使用up函数释放信号量,如果信号量上的睡眠队列不为空,则唤醒其中一个等待进程。
- 读写信号量
struct rw_semaphore {
};
在使用读写信号量前先要初始化,就像你所想到的,它在使用上几乎与读写自旋锁一致。先来看看读写信号量的创建和初始化:
// 静态初始化
static DECLARE_RWSEM(rwsem_name);
// 动态初始化
static struct rw_semaphore rw_sem;
init_rwsem(&rw_sem);
down_read(&rw_sem);
...
up_read(&rw_sem);
down_write(&rw_sem);
...
up_write(&rw_sem);
#include <linux/rwsem.h>
DECLARE_RWSET(name);
init_rwsem(struct
void down_read(struct rw_semaphore *sem);
void down_write(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
由于文本大小的限制,这里只能介绍这些技术了。自旋锁和信号量都是常用的内核技术,下面我们总结一下自旋锁和信号量的特点,然后说明什么时候使用自旋锁,什么时候使用信号量。
- SMP系统
- 睡眠
- 持锁时间
- Linux驱动程序开发005 - 内核同步技术
- Linux驱动程序开发 005- 内核同步技术
- Linux驱动程序开发 006- 内核同步技术
- Linux驱动程序开发009 - 使用内核内存
- Linux内核开发之将驱动程序添加到内核
- Linux内核开发之--将驱动程序添加到内核
- Linux内核开发之将驱动程序添加到内核
- Linux内核开发之将驱动程序添加到内核
- Linux内核开发之将驱动程序添加到内核
- Linux内核开发之将驱动程序添加到内核
- Linux内核:驱动程序
- linux 驱动程序 内核数据类型
- 编写Linux内核驱动程序
- 测试Linux内核驱动程序
- 《精通Linux设备驱动程序开发》——内核
- 《精通Linux设备驱动程序开发》——内核组件
- Ubantu下实现Linux驱动程序开发环境(内核树)
- 【Linux技术】linux驱动程序开发及环境搭建
- 利用Cookie显示商品浏览历史记录
- Rose与PowerDesigner:两款建模工具对比分析比较
- 同步在无线网状态下操作的数据
- HDU 3415 Max Sum of Max-K-sub-sequence(单调队列)
- Linux and Unix下修改时区的方法
- Linux驱动程序开发005 - 内核同步技术
- Android 中使用OpenGL ES进行2D开发(绘制第一个三角形番外篇)
- c++ 嵌套类的使用
- Ubuntu下增加Eclipse菜单图标
- Facebook10亿美元收购Instagram
- 关于SharePoint 2007的用户组和用户
- 用C#.NET实现生成PDF文档和将WORD转换为PDF
- Flex编译器的开源代码存放位置
- 控件之Radio Button:MFC中Radio Button使用方法