管理信号量、自旋锁、原子变量函数接口>>Linux 设备驱动程序

接下来是这个,没想到内核的知识还是挺紧凑,已经不再有上个月的对代码恐慌的状态了;
继续实现我的愿望,结果要努力过了才知道;

[0x100] 进程竞态特征

  • 简述定义:同一时间,不同进程对共享资源的访问,由于次序不同导致结果与预期不一致的情况;
  • 共享资源:全局变量、内核空间数据、堆分配 等;被称为临界区
  • 面临问题:当某进程操作的共享资源时,减少或者阻止其他进程对相同资源访问或者修改;

[0x200] 信号量

  1. 进程信号量 :基于进入临界区访问数量控制,每次进入减1,当信号量为0 时,即为锁定状态;
  2. 读写信号量 :基于进程对临界区的执行的操作,确定是否需要阻塞等待上一个进程完成操作;

[0x210] 进程信号量函数接口[struct semaphore]

[0x211] 初始化信号量

#include <asm/semaphore.h>
void sema_init(struct semaphore * sem,int val);
//函数功能     :初始化 信号量结构体
//args 1      : 定义的信号量结构体指针;
//args 2      : 信号量的初始数值 如何数值为1 则为互斥信号量;
DECLARE_MUTEX(struct semaphore *);
DECLARE_MUTEX_LOCK(struct semaphore*);  

[0x212] 获取与释放信号量

#include <asm/semaphore.h>
/*不达目的不罢休的获取信号量*/
void down(struct semaphore*);
/*如果没有被中断就一直等待获取信号量,如果进程获取信号量时,被中断则返回 非零,处理结果-ERESTARTSYS 或者 -EINTR*/
int down_interruptible(struct semaphore *);
/*试一下不行就放弃的获取信号量,如果没有获取信号量,则返回非零值*/
int down_trylock(struct semaphore *);
/*释放信号量*/
void up(struct semaphore *);

[0x213] 使用信号量注意事项

  • 使用信号量之前必须初始化,且初始化必须位于设备注册之前;
  • 如果使用 特殊的 加锁 需要检查返回值,例如 down_interruptible(), 并对应适当的处理;
  • 如果需要执行过程结束前,必须执行 up()解除锁定,否则信号量状态将永远无法找回;

[0x220] 读写信号量函数接口[struct rw_semaphore]

  • 只读临界区可以并发, 只写临界区不可并发;;
  • 获取临界区资源时,写入者优先级高于读取者的优先级

[0x221] 配置读信号量

#include <linux/rwsem.h>
//函数功能     :初始化读写信号量结构体
void init_rwsem(struct rw_semaphore *);

/*定义读锁定状态与释放状态*/
void down_read(struct rw_semaphore *);
/*不同于其它trylock 函数,当获取权限时返回非零值,无法获取时返回0*/
int  down_read_trylock(struct rw_semaphore *);
/*读信号量解除锁定的必须是由 down_read_* 锁定的临界区*/
void up_read(struct rw_semaphore *);

[0x222] 配置写信号量

#include <linux/rwsem.h>
//函数功能     :初始化读写信号量结构体
void init_rwsem(struct rw_semaphore *);

/*定义写锁定状态与释放状态*/
void down_write(struct rw_semaphore *);
/*不同于其它trylock 函数,当获取权限时返回非零值,无法获取时返回0*/
int  down_write_trylock(struct rw_semaphore *);
/*写信号量解除锁定的必须是由 down_write_* 锁定的临界区*/
void up_write(struct rw_semaphore *);
/*写入完成且仍处于写信号量状态锁定时,调用该函数可以允许只读进程进入临界区访问*/
void downgrade_write(struct rw_semaphore *);

[0x300] 自旋锁

  1. 锁定状态,将使未获取锁的进程一直处于自旋的运行状态,直到获取锁成功;
  2. 非抢占单核CPU的自旋锁操作为空,是为了防止进程陷入永远自旋状态;
  3. 自旋锁必须用于原子上下文,且尽量避免锁定耗时操作;
  4. 自动禁止 CPU的抢占,防止当前造成死锁;

[0x310]自旋锁注意事项

[0x311] 可能造成死锁的情况

  • 进程切换中断前获取的自旋锁,之后中断上下文需要获取这个锁时;
  • 进程上下文中,执行过程中互相锁定了对方需要的临界区资源;

[0x312] 减少可能死锁的情况

  1. 严格规定锁定与解锁的顺序;
  2. 固定上层函数执行加锁解锁动作,减少内层嵌套函数执行锁定的可能;
  3. 减少实现大量精细颗粒的锁定过程;
  4. 实现循环栈缓冲 #include <linux/kfifo.h>;

[0x320] 自旋锁函数接口[struct spinlock]

[0x321] 初始化自旋锁

#include <linux/spinlock.h>
/*方法1*/
spinlock_t pym_lock = SPIN_LOCKP_UNLOCKED;
/*方法2*/
void spin_lock_init(spinlock_t *lock);

[0x322] 获取自旋锁

#include <linux/spinlock.h>
/*不做任何操作获取自旋锁*/
void spin_lock(spinlock_t *lock);
/*获取自旋锁之前禁止IRQ中断,并保存之前的中断状态到flag中,如果自旋处于中断上下文 必须用此函数*/
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags);
/*获取自旋锁之前禁止IRQ中断,不保存之前的中断状态,如果自旋处于中断上下文 必须用此函数*/
void spin_lock_irq(spinlock_t *lock);
/*获取自旋锁之前禁止软中断*/
void spin_lock_bh(spinlock_t *lock);

[0x323] 释放自旋锁

/*不做任何操作释放自旋锁*/
#include <linux/spinlock.h>
void spin_unlock(spinlock_t *lock);
/*释放自旋锁之前恢复IRQ中断,并恢复之前的中断状态从flag中,其中flags 必须是从spin_lock_irqsave中的返回参数*/
void spin_unlock_irqrestore(spinlock_t *lock,unsigned long flags);
/*释放自旋锁之前恢复IRQ中断,不恢复之前的中断状态*/
void spin_unlock_irq(spinlock_t *lock);
/*释放自旋锁之前禁止软中断*/
void spin_unlock_bh(spinlock_t *lock);

[0x330] 读写自旋锁函数接口[rwlock_t]

[0x331] 配置读自旋锁

#include <linux/spinlock.h>
/*初始化读写自旋锁*/
void rwlock_init(rwlock_t *lock);

/*获取读自旋锁*/
void read_lock(rwlock_t *lock);
/*获取读自旋锁之前禁止IRQ中断,并保存之前的中断状态到flag中,如果自旋处于中断上下文 必须用此函数*/
void read_lock_irqsave(rwlock_t *lock,unsigned long flags);
/*获取读自旋锁之前禁止IRQ中断,不保存之前的中断状态,如果自旋处于中断上下文 必须用此函数*/
void read_lock_irq(rwlock_t *lock);
/*获取读自旋锁之前禁止软中断*/
void read_lock_bh(rwlock_t *lock);

/*释放读自旋锁*/
void read_unlock(rwlock_t *lock);
/*释放读自旋锁之前恢复IRQ中断,并恢复之前的中断状态从flag中,其中flags 必须是从read_lock_irqsave中的返回参数*/
void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags);
/*释放读自旋锁之前恢复IRQ中断,不恢复之前的中断状态*/
void read_unlock_irq(rwlock_t *lock);
/*释放读自旋锁之前禁止软中断*/
void read_unlock_bh(rwlock_t *lock);

[0x332] 配置写自旋锁

#include <linux/spinlock.h>
/*初始化读写自旋锁*/
void rwlock_init(rwlock_t *lock);

/*获取写自旋锁*/
void write_lock(rwlock_t *lock);
/*获取写自旋锁之前禁止IRQ中断,并保存之前的中断状态到flag中,如果自旋处于中断上下文 必须用此函数*/
void write_lock_irqsave(rwlock_t *lock,unsigned long flags);
/*获取写自旋锁之前禁止IRQ中断,不保存之前的中断状态,如果自旋处于中断上下文 必须用此函数*/
void write_lock_irq(rwlock_t *lock);
/*获取写自旋锁之前禁止软中断*/
void write_lock_bh(rwlock_t *lock);

/*释放写自旋锁*/
void write_unlock(rwlock_t *lock);
/*释放写自旋锁之前恢复IRQ中断,并恢复之前的中断状态从flag中,其中flags 必须是从read_lock_irqsave中的返回参数*/
void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags);
/*释放写自旋锁之前恢复IRQ中断,不恢复之前的中断状态*/
void write_unlock_irq(rwlock_t *lock);
/*释放写自旋锁之前禁止软中断*/
void write_unlock_bh(rwlock_t *lock);

[0x400] 原子变量

  1. 用于对较小范围的资源的操作单入,是一种替补锁结构的策略,但并不是一种锁;
  2. 通常是对atomic_t 这种小于 24位的int 值的操作;
  3. 由于操作过程可能只需要一个指令即可完成,不可分割 所以称为原子变量;
  4. 原子变量不能作为整形数值进行传递;

[0x410] 原子变量函数接口

[0x411] 创建与获取、增加、减少

#include <asm/atomic.h>
/*1. 初始化 共两种方法*/
/*method 1 :宏初始化原子变量结构 其中 val是需要初始化的数值*/
atomic_t i_val = ATOMIC_INIT(val)/*method 2 :函数初始化原子变量 其中 i_ptr是原子变量结构,val是需要初始化的数值*/
void atomic_set(atomic_t *i_ptr,int val);

/*2.操作 包括 获取、增加、减少*/
/*获取结构中的原子数值*/
int atomic_read(atomic_t *i_ptr);
/*从i_ptr 中增加指定数值inc_val */
void atomic_add(int inc_val,atomic_t *i_ptr);
/*从i_ptr 中减少指定数值dec_val */
void atomic_sub(int dec_val,atomic_t *i_ptr);
/*从i_ptr 中+1 */
void atomic_inc(atomic_t *i_ptr);
/*从i_ptr 中-1 */
void atomic_dec(atomic_t *i_ptr);

[0x412] 增加与减少后返回原子值正负结果

#include <asm/atomic.h>
/*先执行相应的操作后判断原子变量,如果是=0则返回真,如果>0则返回假,黏连步骤还要恢复的原子变量数值*/
int atomic_sub_and_test(int dec_val,atomic_t *i_ptr);
int atomic_inc_and_test(atomic_t *i_ptr);
int atomic_dec_and_test(atomic_t *i_ptr);
/*例外的add 如果 inc_val 添加到 i_ptr后 如果原子变量 <0为真,如果原子变量 >=0为假;*/
int atomic_add_negative(int inc_val,atomic_t *i_ptr);

[0x412] 增加与减少后返回当前原子值

/*先执行相应操作后将原子变量的最终数值返回给调用者*/
int atomic_add_return(int inc_val,atomic_t *i_ptr);
int atomic_sub_return(int dec_val,atomic_t *i_ptr);
int atomic_inc_return(atomic_t *i_ptr);
int atomic_dec_return(atomic_t *i_ptr);

猜你喜欢

转载自blog.csdn.net/god1992/article/details/85160412