linux内核设计与实现-第10章 内核同步方法
-
10.1.1 原子整数操作
#include <linux/types.h>
typedef struct{
volatile int counter;
}atomic_t;
原子整形操作定义
#include <asm/atomic.h>
atomic_t v; /*定义v*/
atomic_t u = AtOMIC_INIT(0); /*定义u 并把它初始化为0*/
| 原子整数操作 | 描述 |
| ATOMIC_INIT(int i) | 在声明一个atomic_t变量时,将它初始化为i |
| int atomic_read (atomic_t *v) |
|
| void atomic_set(atomic_t *v,int i) |
|
| void atomic_add(int i, atomic_t *v) |
|
| void atomic_sub(int i, atomic_t *v) |
|
| void atomic_inc(atomic_t *v) |
|
| void atomic_dec(atomic_t *v) |
|
| int atomic_sub_and_test(int i, atomic_t *v) |
|
| int atomic_add_negative(int i, atomic_t *v) |
|
| int atomic_add_return(int i, atomic_t *v) |
|
| int atomic_sub_return(int i, atomic_t *v) |
|
| int atomic_inc_return(int i, atomic_t *v) |
|
| int atomic_dec_return(int i, atomic_t *v) |
|
| int atomic_dec_and_test(atomic_t *v) |
|
| int atomic_inc_and_test(atomic_t *v) |
|
-
10.1.2 64位原子操作
atomic64_t 类型定义
#include <linux/types.h>
typedef struct{
volatile long counter;
}atomic_t;
| 原子整数操作 | 描述 |
| ATOMIC64_INIT(int i) | 在声明一个atomic_t变量时,将它初始化为i |
| int atomic64_read (atomic_t *v) |
|
| void atomic64_set(atomic_t *v,int i) |
|
| void atomic64_add(int i, atomic_t *v) |
|
| void atomic64_sub(int i, atomic_t *v) |
|
| void atomic64_inc(atomic_t *v) |
|
| void atomic64_dec(atomic_t *v) |
|
| int atomic64_sub_and_test(int i, atomic_t *v) |
|
| int atomic64_add_negative(int i, atomic_t *v) |
|
| int atomic64_add_return(int i, atomic_t *v) |
|
| int atomic64_sub_return(int i, atomic_t *v) |
|
| int atomic64_inc_return(int i, atomic_t *v) |
|
| int atomic64_dec_return(int i, atomic_t *v) |
|
| int atomic64_dec_and_test(atomic_t *v) |
|
| int atomic64_inc_and_test(atomic_t *v) |
|
-
10.1.3 原子位操作
#include <asm/bitops.h>
内核提供了两个历程用来从指定的地址开始搜索第一个被设置(或未被设置)的位
int find_first_bit(unsigned long *addr,unsigned int size)
int find_fist_zero_bit(unsigned long *addr,unsigned int size)
_ffs() ffz() //搜索的范围仅限一个字
| 原子位操作 | 描述 |
| void set_bit(int nr,void *addr) | 原子地设置addr所指对象的第nr位 |
| void clear_bit(int nr,void *addr) | 原子地清空addr所指对象的第nr位 |
| void change_bit(int nr,void *addr) | 原子地翻转addr所指对象的第nr位 |
| int test_and_set_bit(int nr,void *addr) | 原子地设置addr所指对象的第nr位,并返回原先的值 |
| int test_and_clear_bit(int nr,void *addr) | 原子地清空addr所指对象的第nr位,并返回原先的值 |
| int test_and_change_bit(int nr,void *addr) | 原子地翻转addr所指对象的第nr位,并返回原先的值 |
| int test_bit(int nr,void *addr) | 原子地返回addr所指对象的第nr位,并返回原先的值 |
-
10.2 自旋锁 spin lock
#include <asm/spinlock.h>
#include <linux/spinlock.h>
DEFINE_SPINLOCK(mr_lock);
spin_lock(&mr_lock);
/* 临界区... */
spin_unlock(&mr_lock);
Linux内核实现的自旋锁是不可递归的。
自旋锁可以使用在中断处理程序中,在是获取锁之前,首先禁止本地中断
内核提供的禁止中断同时请求锁的接口
DEFINE_SPINLOCK(mr_lock);
unsigned long flags;
spin_lock_irqsave(&mr_lock,flags);
/* 临界区 ...*/
spin_unlock_irqestore(&mr_lock,flags);
如果能确定中断在加锁前是激活的,就不需要在解锁后恢复中断以前的状态了,
可以无条件的解锁时,激活中断,使用下面函数会更好一些
DEFINE_SPINLOCK(mr_lock);
spin_lock_irq(&mr_lock);
/*关键节*/
spin_unlock_irq(&mr_lock);
调试自旋锁
打开配置选项CONFIG_DEBUG_SPINLOCK,内核会检查是否使用了未初始化的锁。
打开配置选项CONFIG_DEBUG_LOCK_ALLOC,进一步全程调试锁。
-
10.2.2 自旋锁方法列表
| 方法 | 描述 |
| spin_lock() | 获取指定的自旋锁 |
| spin_lock_irq() | 禁止本地中断并获取指定的自旋锁 |
| spin_unlock_irqsave() | 保存本地中断的当前状态,禁止本地中断并获取指定的自旋锁 |
| spin_unlock() | 释放指定的锁 |
| spin_unlock_irq() | 释放指定的锁并激活本地中断 |
| spin_unlock_irqresotre() | 释放指定的锁,并让本地中断恢复到以前状态 |
| spin_lock_init() | 动态初始化指定的spinlock_t |
| spin_trylock() | 试图获取指定的锁,如果未获取,则返回非0 |
| spin_is_locked() | 如果指定的锁当前正在被获取,则返回非0,否则返回0 |
-
10.2.3 自旋锁和下半部
spin_lock_bh()
spin_unlock_bh()
-
10.2 读写自旋锁(读写自旋锁方法列表)
DEFINE_RWLOCK(mr_rwlock);
read_lock(&mr_rwlock);
/* 临界区 (只读)...*/
read_unlock(&mr_rwlock);
write_lock(&mr_rwlock);
/* 临界区 (只读)...*/
wirte_unlock(&mr_rwlock):
注意:不能把一个读锁升级为写锁
多个读者可以安全地获取同一个读锁,一个线程递归的获得同一个读锁也是安全的
中断处理只有读没有写,可以使用 read_lock()
中断处理有写,需要使用write_lock_irqsave()
-
10.4.1 计数信号量 和 二值信号量
-
10.4.2 创建和初始化信号量
#include <asm/semaphore.h>
struct semaphore name;//信号量变量名
sema_init(&name,count);//信号量使用数量
sema_init(sem,count); //信号量初始化,sem是指针
init_MUTEX(sem); //初始化动态创建的互斥信号量
static DECLARE_MUTEX(name); //创建普通的互斥信号量
if (down_interruptible(&mr_sem)){
/**信号被接收,信号量还未获取*/
}
/**临界区...*/
/**释放给定的信号量*/
up(&mr_sem);
信号量方法列表
| 方法 | 描述 |
| sema_init(struct semaphore *,count) | 以指定的计数值初始化动态创建的信号量 |
| init_MUTEX(struct semaphore *) | 以计数值1初始化动态创建的信号量 |
| init_MUTEX_LOCKED(struct semaphore *) | 以计数值0初始化动态创建的信号量(初始为加锁状态) |
| down_interruptibel(struct semaphore *) | 以试图获得指定的信号量,如果信号量已被争用,则进入可中断睡眠状态 |
| down(struct semaphore *) | 以试图获得指定的信号量,如果信号量已被争用,则进入不可中断睡眠状态 |
| down_trylock(struct semaphore *) | 以试图获得指定的信号量,如果信号量已被争用,则立即返回非0值 |
| up(struct semaphore *) | 以释放指定的信号量,如果睡眠队列不空,则唤醒其中一个任务 |
-
10.5 读写信号量
#include <linux/rwsem.h>
init_rwsem(struct rw_semaphore *sem) //动态创建的读-写信号量 初始化
static DECLARE_RWSEM(name); //创建声明的读-写信号量
down_read(&mr_rwsem); /** 试图获取信号量用于读... */
/** 临界区(只读)*/
up_read(&mr_rwsem); /**释放信号量*/
down_write(&mr_rwsem); /** 试图获取信号量用于写*/
/** 临界区(读和写)*/
up_write(&mr_rwsem); /**释放信号量*/
-
10.6 互斥体
DEFINE_MUTEX(name) //静态的定义mutex
mutex_init(&mutex) //动态初始化mutex
mutex_lock(&mutex)
/** 临界区*/
mutex_un_lock(&mutex);
Mutex 方法
| 方法 | 描述 |
| mutex_lock(struct mutex *) | 为指定的mutex 上锁,如果锁不可用则睡眠 |
| mutex_unlock(struct mutex *) | 为指定的mutex解锁 |
| mutex_trylock(struct mutex *) | 试图获取指定的mutex,如果成功则返回1,否则锁被获取,返回值是0 |
| mutex_is_lock(struct mutex *) | 如果锁已被争用,则返回1,否则返回0 |
-
10.7 完成变量
#include <linux/completion.h>
DECLARE_COMPLETION(mr_comp); //通过宏静态的创建完成变量并初始化它
完成变量方法
| 方法 | 描述 |
| init_completion(struct completion *) | 初始化指定的动态创建的完成量 |
| wait_for_completion(struct completion *) | 等待指定的完成变量接收信号 |
| complete(struct completion *) | 发信号唤醒任何等待任务 |
-
10.8 BLK: 大内核锁 -- 不推荐使用
#include <linux/smp_lock.h>
kernel_locked() //如果锁被持有返回非0值,否则返回0(UP总是返回非0)
lock_kernel();//获得BKL锁
/*
* 临界区,对所有的其他的BLK用户进行同步
* 注意,你可以安全的在此睡眠,锁会悄无声息的被释放
* 当你的任务被重新调度时,锁又会被悄无声息的获取
* 这意味着你不会处于死锁状态,但是,如果你需要锁保护这里的数据
* 你还是不需要睡眠
*/
unlock_kernel()//释放BKL锁
-
10.9 顺序锁
seqlock_t mr_seq_lock = DEFINE_SEQLOCK(mr_seq_lock);// 定义一个seq锁
write_seqlock(&mr_seq_lock); //写锁
/* 写锁被获取.... */
write_sequnlock(&mr_seq_lock);
unsigned long seq; //读
do {
seq = read_seqbegin(&mr_seq_lock);
/*读这里的数据...*/
}while(read_seqretry(&mr_seq_lock,seq));
-
10.10 内核抢占
内核抢占的相关函数
| 函数 | 描述 |
| preempt_disable() | 增加抢占计数值,从而禁止内核抢占 |
| preempt_enable() | 减少抢占计数,并当该值降为0时检查和执行被挂起的需调度的任务 |
| preempt_enable_no_resched() | 激活内核抢占但不再检查任何被挂起的需调度任务 |
| preempt_count() | 返回抢占计数 |
int cpu;
/* 禁止内核抢占,并将cpu设置为当前处理器 */
cpu = get_cpu();
/* 对每个处理器的数据进行操作 ... */
/* 再给与内核抢占行,CPU 可改变故它不再有效 */
put_cup();
-
10.11 顺序和屏障
内存和编译器屏障方法
| 屏障 | 描述 |
| rmb() | 阻止跨越屏障的载入动作发生重排 |
| read_barrier_depends | 阻止跨越屏障的具有数据依赖关系的载入动作重排序 |
| wmb() | 阻止跨越屏障的存储动作发生重排序 |
| mb() | 阻止跨越屏障的载入和存储动作重新排序 |
| smp_rmb() | 在SMP上提供rmb()功能,在UP上提供barrier功能 |
| smp_read_barrier_depends() | 在SMP上提供read_barrier_depends()功能,在UP上提供barrier()功能 |
| smp_wmb() | 在SMP上提供wmb()功能,在UP上提供barrier()功能 |
| smp_mb() | 在SMP上提供mb()功能,在UP上提供barrier()功能 |
| barrier() | 阻止编译器跨屏障对载入或存储操作进行优化 |

浙公网安备 33010602011771号