一、原子操作
原子整形操作API
函数 |
描述 |
ATOMIC_INIT(int i) |
定义原子变量的时候对其初始化。 |
int atomic_read(atomic_t *v) |
读取 v 的值,并且返回。 |
void atomic_set(atomic_t *v, int i) |
向 v 写入 i 值。 |
void atomic_add(int i, atomic_t *v) |
给 v 加上 i 值。 |
void atomic_sub(int i, atomic_t *v) |
从 v 减去 i 值。 |
void atomic_inc(atomic_t *v) |
给 v 加 1,也就是自增。 |
void atomic_dec(atomic_t *v) |
从 v 减 1,也就是自减 |
int atomic_dec_return(atomic_t *v) |
从 v 减 1,并且返回 v 的值。 |
int atomic_inc_return(atomic_t *v) |
给 v 加 1,并且返回 v 的值。 |
int atomic_sub_and_test(int i, atomic_t *v) |
从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomic_dec_and_test(atomic_t *v) |
从 v 减 1,如果结果为 0 就返回真,否则返回假 |
int atomic_inc_and_test(atomic_t *v) |
给 v 加 1,如果结果为 0 就返回真,否则返回假 |
int atomic_add_negative(int i, atomic_t *v) |
给 v 加 i,如果结果为负就返回真,否则返回假 |
原子位操作API
函数 |
描述 |
void set_bit(int nr, void *p) |
将 p 地址的第 nr 位置 1。 |
void clear_bit(int nr,void *p) |
将 p 地址的第 nr 位清零。 |
void change_bit(int nr, void *p) |
将 p 地址的第 nr 位进行翻转。 |
int test_bit(int nr, void *p) 获取 p |
地址的第 nr 位的值。 |
int test_and_set_bit(int nr, void *p) |
将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。 |
int test_and_clear_bit(int nr, void *p) |
将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。 |
int test_and_change_bit(int nr, void *p) |
将 p 地址的第 nr 位翻转,并且返回 nr 位原来的 |
二、自旋锁
使用注意如下:
- ①、因为在等待自旋锁的时候处于“自旋”状态,因此锁的持有时间不能太长,一定要短,否则的话会降低系统性能。如果临界区比较大,运行时间比较长的话要选择其他的并发处理方式,比如稍后要讲的信号量和互斥体。
- ②、自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数,否则的话可能导致死锁。
- ③、不能递归申请自旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就必须“自旋”,等待锁被释放,然而你正处于“自旋”状态,根本没法释放锁。结果就是自己把自己锁死了!
- ④、在编写驱动程序的时候我们必须考虑到驱动的可移植性,因此不管你用的是单核的还是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。
基本自旋锁API
函数 |
描述 |
DEFINE_SPINLOCK(spinlock_t lock) |
定义并初始化一个自选变量。 |
int spin_lock_init(spinlock_t *lock) |
初始化自旋锁。 |
void spin_lock(spinlock_t *lock) |
获取指定的自旋锁,也叫做加锁。 |
void spin_unlock(spinlock_t *lock) |
释放指定的自旋锁。 |
int spin_trylock(spinlock_t *lock) |
尝试获取指定的自旋锁,如果没有获取到就返回 0 |
int spin_is_locked(spinlock_t *lock) |
检查指定的自旋锁是否被获取,如果没有被获取就返回非 0,否则返回 0。 |
中断自旋锁API
函数 |
描述 |
void spin_lock_irq(spinlock_t *lock) |
禁止本地中断,并获取自旋锁。 |
void spin_unlock_irq(spinlock_t *lock) |
激活本地中断,并释放自旋锁。 |
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) |
保存中断状态,禁止本地中断,并获取自旋锁。 |
void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags) |
将中断状态恢复到以前的状态,并且激活本地中断,释放自旋锁。 |
下半部自旋锁API
函数 |
描述 |
void spin_lock_bh(spinlock_t *lock) |
关闭下半部,并获取自旋锁。 |
void spin_unlock_bh(spinlock_t *lock) |
打开下半部,并释放自旋锁。 |
三、信号量
使用信号量会提高处理器的使用效率,毕竟不用一直傻乎乎的在那里“自旋”等待。但是,信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。
使用注意如下:
- ①、因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合。
- ②、因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
- ③、如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。
信号量API
函数 |
描述 |
DEFINE_SEAMPHORE(name) |
定义一个信号量,并且设置信号量的值为 1。 |
void sema_init(struct semaphore *sem, int val) |
初始化信号量 sem,设置信号量值为 val。 |
void down(struct semaphore *sem) |
获取信号量,因为会导致休眠,因此不能在中断中使用。 |
int down_trylock(struct semaphore *sem); |
尝试获取信号量,如果能获取到信号量就获取,并且返回 0。如果不能就返回非 0,并且不会进入休眠。 |
int down_interruptible(struct semaphore *sem) |
获取信号量,和 down 类似,只是使用 down 进入休眠状态的线程不能被信号打断。而使用此函数进入休眠以后是可以被信号打断的。 |
void up(struct semaphore *sem) |
释放信号量 |
四、互斥体
使用注意如下:
- ①、 mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
- ②、和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数。
- ③、因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁。
互斥体API
函数 |
描述 |
DEFINE_MUTEX(name) |
定义并初始化一个 mutex 变量。 |
void mutex_init(mutex *lock) |
初始化 mutex。 |
void mutex_lock(struct mutex *lock) |
获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。 |
void mutex_unlock(struct mutex *lock) |
释放 mutex,也就给 mutex 解锁。 |
int mutex_trylock(struct mutex *lock) |
尝试获取 mutex,如果成功就返回 1,如果失败就返回 0。 |
int mutex_is_locked(struct mutex *lock) |
判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。 |
int mutex_lock_interruptible(struct mutex *lock) |
使用此函数获取信号量失败进入休眠以后可以被信号打断。 |