哪些是Linux内核的同步机制,Linux内核中的互斥与同步机制

共有这么几大类

1.自旋锁

2.信号量

3.互斥锁

4.RCU

5.原子变量

6.完成量

文章列举了各个互斥机制所要用的api以及在什么情况下用哪种互斥,并未对内核中的互斥和同步机制详细分析,

只供今后写代码时查阅,如果想了解详细机制可参考LKD或<>等书.

自旋锁

spin_lock/spin_unlock

因为只禁止抢占,并未对中断做处理,所以不能在中断上下文用,所以便有了以下变体

spin_lock_irq/spin_unlock_irq

禁止抢占,禁止中断,可以在中断上下文用

spin_lock_irqsave/spin_unlock_irqrestore

禁止抢占,禁止中断的同时保存中断前处理器FLAGS寄存器的状态,在ARM上是保存CPSR寄存器

spin_lock_bh/spin_unlock_bh

相对于spin_lock_irq来说,spin_lock_bh关闭的是softirq

非阻塞

spin_trylock

spin_trylock_irq

spin_trylock_irqsave

spin_trylock_bh

读写者自旋锁

如果系统中有大量对共享资源的读操作,但并不会改写其内容,那么用spin_lock就会大大降低系统性能

所以便有了读写者自旋锁rwlock.唯一与自旋锁不同的是可以允许多个读者同时访问,如果有写操作参与那么得互斥

读取者

read_lock/read_unlock

read_lock_irq/read_unlock_irq

read_lock_irqsave/read_unlock_irqrestore

写入者

write_lock/write_unlock

write_lock_irq/write_unlock_irq

write_lock_irqsave/write_unlock_irqrestore

顺序锁

typedef struct {

unsigned sequence;

spinlock_t lock;

} seqlock_t;

顺序锁seqlock的设计思想是写加锁,读不加

为了保证读取数据的过程中不会由写入者参与,便设置了一个sequence值,读取者在开始读取前读取该值,

读取操作完成后再读取该值,看两值是否一致,如果不一致,说明数据被更新,读取操作无效.因此写入者在

开始写入时要更新sequence值

同样,静态初始化

#define DEFINE_SEQLOCK(x) \

seqlock_t x = __SEQLOCK_UNLOCKED(x)

动态

seqlock_init

例子

//定义一个顺序锁变量demo_seqlock

DEFINE_SEQLOCK(demo_seqlock)

//写入者代码

write_seqlock(&demo_seqlock);    //实际写之前调用write_seqlock获取自旋锁,同时更新sequence的值

do_write();            //实际的写入操作

write_unseqlock(&demo_seqlock);    //写入结束,释放自旋锁

//读取者代码

unsigned start;

do {

//读取操作前先得到sequence的值赋给start,用以在读操作结束后判断是否发生更新

//注意读操作无需获取锁,但是如果有写操作在进行那么会一直循环读取sequence的值,直到写操作结束

//read_seqbegin是通过判断sequence的最低位实现的,写操作完成那么sequence&0返回0,否则返回1

start = read_seqbegin(&demo_seqlock);

do_read();    //实际的读操作

} while (read_seqretry(&demo_seqlock, start));    //如果有数据更新,再重新读取

如果考虑到中断安全问题,可以用

write_seqlock_irq/write_sequnlock_irq

write_seqlock_irqsave/write_sequnlock_irqrestore

write_seqlock_bh/write_sequnlock_bh

read_seqbegin_irqsave

read_seqretry_irqrestore

顺序锁seqlock和之前的读写者自旋锁rwlock其实是相同的,者是读写互斥,写写互斥,读读不互斥

信号量

struct semaphore {

raw_spinlock_t          lock;

unsigned int            count;

struct list_head        wait_list;

};

相对于自旋锁来讲,信号量最大的特点就是允许调用它的线程睡眠

定义信号量    struct semaphore

初始化信号量    sema_init(struct semaphore *sem, int val)

信号量主要是DOWN/UP操作,DOWN操作有,不过驱动使用最频繁的是down_interruptible

down

down_interruptible

down_killable

down_trylock

down_timeout

UP操作只有一个

up

即使不是信号量的拥有者也可以调用up函数来释放一个信号量,这一点是与mutex不同的

其实信号量常见的用途就是实现互斥机制,也就是信号量的count为1,也就是任意时刻只允许一个进程进入临界区,用法是

#define DECLARE_MUTEX(name)

以免与mutex产生混淆,Thomas Gleixner在2010年9月7号改为了

#define DEFINE_SEMAPHORE(name)  \

struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

所以现在我们用DEFINE_SEMAPHORE

读写者信号量

与读写自旋锁一个意思,者是为了提高系统性能

/*

* the rw-semaphore definition

* - if activity is 0 then there are no active readers or writers

* - if activity is +ve then that is the number of active readers

* - if activity is -1 then there is one active writer

* - if wait_list is not empty, then there are processes waiting for the semaphore

*/

struct rw_semaphore {

__s32                   activity;

raw_spinlock_t          wait_lock;

struct list_head        wait_list;

#ifdef CONFIG_DEBUG_LOCK_ALLOC

struct lockdep_map dep_map;

#endif

};

定义

#define DECLARE_RWSEM(name) \

struct rw_semaphore name = __RWSEM_INITIALIZER(name)

初始化

init_rwsem

DOWN操作

down_read

down_read_trylock

down_write

down_write_trylock

UP操作

up_read

up_write

互斥锁

用信号量实现互斥不是Linux中最经典的用法,于是便有了mutex.

struct mutex {

/* 1: unlocked, 0: locked, negative: locked, possible waiters */

atomic_t        count;

spinlock_t        wait_lock;

struct list_head    wait_list;

#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)

struct task_struct    *owner;

#endif

#ifdef CONFIG_DEBUG_MUTEXES

const char         *name;

void            *magic;

#endif

#ifdef CONFIG_DEBUG_LOCK_ALLOC

struct lockdep_map    dep_map;

#endif

};

定义并初始化,静态

#define DEFINE_MUTEX(mutexname) \

struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)

动态

struct mutex

mutex_init

DOWN/UP操作,得不到锁就去等待队列睡眠

mutex_lock/mutex_unlock

RCU

Read-Copy_Update,即读/写-复制-更新.Linux提供了很多互斥机制,RCU与其他不同的是它是免锁的.

RCU的应用场景也是读取者/写入者,不同的是RCU不用考虑读/写的互斥问题.

简单原理是,将读取者和写入者要访问的共享数据放在指针p指向的区域,读取者通过p来访问数据,而写入者通过

修改这个区域来更新数据.在具体实现上读取者没有太多的事要做,大量的工作都在写入者一方.免锁的实现双方

必须同时遵守一定的规则.

读取者所做的工作是

禁止抢占,对p指针的引用只能在临界区中

写入者做做的工作是

1.分配新的空间ptr

2.更新数据

3.用新指针ptr替换老指针p

4.调用call_rcu释放老指针所指向的空间

注意第4步释放空间时必须确保没有读取者对老指针的引用,内核是通过判断处理器是否发生进程切换来实现的.因为

读取者是禁止抢占的,所以在临界区不会发生进程切换(就单核而言),如果发生进程切换那么就说明不在临界区了

例子

//假设struct shared_data是读者和写者要共同访问的共享数据

struct shared_data {

int a;

int b;

struct rcu_head rcu;

};

//读取者代码

//读取者调用rcu_read_lock/rcu_read_unlock构建它的读取临界区,所有对指向被保护资源指针的引用都应该只出现在临界区中,

//而且临界区中的代码不能睡眠

static void demo_reader(struct shared_data *ptr)

{

struct shared_data *p = NULL;

rcu_read_lock();

p = rcu_dereference(ptr);    //调用rcu_dereference获得指向共享数据的指针

if (p)

do_something...

rcu_read_unlock();

}

//写入者代码

//写入者提供的回调函数,用于释放老指针

static void demo_del_oldptr(struct rcu_head *rh)

{

struct shared_data *p = container_of(rh, struct shared_data, rcu);

kfree(p);

}

static void demo_writer(struct shared_data *ptr)

{

struct shared_data *new_ptr = kmalloc(...);

...

new_ptr->a = 30;

new_ptr->b = 40;

rcu_assign_pointer(ptr, new_ptr);    //用新指针更新老指针

call_rcu(ptr->rcu, demo_del_oldptr);    //调用call_rcu让内核在确保所有对老指针ptr的引用都结束后回调demo_del_oldptr释放老指针所指向的区域

}

和call_rcu类似的还有一个synchronize_rcu,不过后者会阻塞,它会等待所有对老指针的引用都消失后才执行,所以在中断上下文要用call_rcu

原子变量

如果需要保护的数据只是一个简单的整型变量,那么可以用原子变量

typedef struct {

int counter;

} atomic_t;

例子

atomic_t flag = ATOMIC_INIT(0);

//Task A

void add_flag()

{

atomic_inc(&flag);

}

//Task B

void add_flag()

{

atomic_inc(&flag);

}

完成量

struct completion {

unsigned int done;

wait_queue_head_t wait;

};

静态初始化

#define DECLARE_COMPLETION(work) \

struct completion work = COMPLETION_INITIALIZER(work)

动态

init_completion

等待完成所调用的API

wait_for_completion

wait_for_completion_interruptible

wait_for_completion_timeout

wait_for_completion_interruptible_timeout

完成

complete

complete_all

阅读(6095) | 评论(0) | 转发(3) |

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值