LDD读书笔记第五章-并发和竞态(一)

linux信号量的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <asm/semaphore.h>  /*信号量相关头文件*/
struct semaphore		/*信号量相关结构体*/
/*初始化相关函数*/
void  sema_init(struct semaphore *sem,int val); 
/*初始化信号量函数*/
DECLARE_MUTEX(name);	/*创建互斥体并初始化为1*/
DECLARE_MUTEX_LOCKED(name);	/*创建互斥体并初始化为0*/
void init_MUTEX(struct semaphore *sem); /*初始化互斥体为1*/
void init_MUTEX_LOCKED(struct semaphore *sem); /*初始化互斥体为0*/
/*down&up 相关函数*/
void down(struct semaphore *sem); /*不可中断*/
int down_interruptible(struct semaphore *sem); /*可中断*/
int down_trylock(struct semaphore *sem); /*立即退出*/
void up(struct semaphore *sem); /*释放信号量*/

down_trylock函数在获取信号量失败时立即退出而不会等待。成功返回0,失败返回非零值。

读取者/写入者信号量

读取者/写入者信号量允许有一个写入者和无穷多个读取者。
当只需要读取数据时使用读取者信号量,仅当需要写入数据时获得写入者信号量。

当有线程获得写入者信号量的时候读取者信号量会被阻塞。
而多个读取者可以并发的读取数据。
并且写入者拥有比读取者更高的优先级。
如果大量线程竞争写入者信号量会导致读取者“饿死”。

1
2
3
4
5
6
7
8
9
10
#include <linux/rwsem.h> 	/*相关头文件*/
struct rw_semaphore		/*相关结构体*/
void init_rwsem(struct rw_semaphore *sem);	/*初始化信号量*/
void down_read(struct rw_semaphore *sem);	/*获取读取者信号量*/
int down_read_trylock(struct rw_semaphore *sem);	/*获取读取者信号量失败立即退出*/
void up_read(struct rw_semaphore *sem);	/*释放读取者信号量*/
void down_write(struct rw_semaphore *sem);	/*获取写入者信号量*/
int down_write_trylock(struct rw_semaphore *sem);	/*获取写入者信号量失败立即退出*/
void up_write(struct rw_semaphore *sem);	/*释放写入者信号量*/
void downgrade_write(struct rw_semaphore *sem);/*释放写入者权限*/

down_read_tryloc和down_write_trylock函数会在获取信号量失败时不会等待而立即退出。
并且和其他一般内核函数不同在成功时返回非零值失败时返回零。

completion

1
2
3
4
5
6
#include <linux/completion.h>
DECLARE_COMPLETION(my_completion);	 /*创建completion*/
struct completion my_completion;	/*结构体*/
void wait_for_completion(struct completion *c); /*等待完成*/
void complete(struct completion *c);	/*触发一个等待*/
void complete_all(struct completion *c);	/*出发所有等待*/

用代码来说明completeion的使用

1
2
3
4
5
....
DECLARE_COMPLETION(my_completion);
start_external_task(&my_completion);
void wait_for_completion(&my_completion);
....

线程在wait_for_completion处开始等待 当start_external_task完成外部任务时调用complete线程开始继续执行

自旋锁

自旋锁核心规则

1、任何拥有自旋锁的代码必须是原子的。
2、不能休眠。
3、自旋时禁用中断。
4、不能被抢占
5、自旋锁必须在可能的最短时间内拥有。

相关函数和宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*需要的头文件和两种初始化自旋锁的方法。
使用宏在编译时直接初始化。
而使用函数是在运行时初始化*/
#include <linux/spinlock.h>
spinlock_t lock = SPIN_LOCK_UNLOCKED;
void spin_lock_init(spinlock_t *lock);
/*分别为:
获得锁。
获得锁并禁用中断,中断状态保存你在flags中。
获得锁并禁用中断,不保存状态,必须保证没有其他代码禁用中断
获得锁并禁用软中断,硬中断工作正常。
*/
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock);
void spin_lock_bh(spinlock_t *lock);
/*下面四个函数和上面的函数一一对应用于释放锁。*/
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);
/*
*/
int spin_trylock(spinlock_t *lock);
int spin_trylock_bh(spinlock_t *lock);

读取/写入自旋锁

整体概念和读取者/写入者信号量差不多。
函数跟自旋锁汉俗话一一对应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rwlock_t lock = RW_LOCK_UNLOCKED
rwlock_init(rwlock_t *lock);
void read_lock(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_lock_irq(rwlock_t *lock);
void read_lock_bh(rwlock_t *lock);
void read_unlock(rwlock_t *lock);
void read_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_bh(rwlock_t *lock);
void write_lock(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_lock_irq(rwlock_t *lock);
void write_lock_bh(rwlock_t *lock);
void write_unlock(rwlock_t *lock);
void write_unlock_irqrestore(rwlock_t *lock, unsigned long flags);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_bh(rwlock_t *lock);
----------------- 全文结束 -----------------
© 该日志作者:Senghoo, 于2010年07月15日发表于分类"Linux Kernel"下;
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第五章-并发和竞态(一) | 转自: Senghoo's Blog
© Tag: , , ,

你可以继续围观

  • LDD读书笔记第五章-并发和竞态(二)
    锁陷阱 不明确的规则:每个线程不能第二次尝试获得该锁。 锁的顺序规则:保证每个线程都以相同的顺序获得锁。 锁的粒度对比:粗粒度锁会导致竞争严重,细粒度锁会导致额外的开销和给维造成副作用 除了锁之外的方法 免锁算法 大量的读取者,只有一个写入者可以使用免锁算法。 免锁算法类似于循环队列(...
  • LDD读书笔记第七章-时间、延迟及延缓操作
    度量时间差 在linux/param.h中定义HZ值,表示每秒发生时钟中断的频率。 内核时钟计数器:jiffies_64。 内核使用jiffies变量是unsigned log类型。 可能是jiffies_64的值也可能是其底32位。 使用jiffies计数器 头文件:linux/jif...

发表评论