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()

阻止编译器跨屏障对载入或存储操作进行优化

 

 

posted @ 2020-02-28 16:44  my_flash  阅读(67)  评论(0)    收藏  举报