并发控制——“锁”

 

 

多个执行单元同时、并行被执行时,而并发的执行单元对共享资源(硬件资源和软件资源上的全局变量、静态变量等)的访问则很容易导致竞态。访问共享资源的代码区成为临界区,临界区需要以某种互斥机制加以保护。中断屏蔽、原子操作、自旋锁和信号量等是linux设备驱动中可采用的互斥途径。 

中断屏蔽:可以保证正在执行的内核执行路径不被中断处理程序所抢占。

使用方法:

local_irq_disable()//屏蔽中断

....

critical section//临界区

....

local_irq_enable()//开启中断 

 

 

我们来看下local_irq_disable()的宏定义:(见include/linux/Irqflags.h):

 

 

 local_irq_disable()的作用是关闭中断,可以从宏中看出,local_irq_disable()调用raw_local_irq_disable()函数,而后者最终调用native_irq_disable()。

 

 

以native_irq_disable()为例,native_irq_enable同理:(见arch/x86/include/asm/irqflags.h

 

 

 温馨提示:汇编语言中,CLI是清除中断标志位,STI是设置中断标志位。“asm”表示后面的代码为内嵌汇编,“volatile”表示编译器不要优化代码,后面的指令保留原样。__asm__(汇编语句模板: 输出部分: 输入部分: 破坏描述部分),memory表示在内存中进行操作 。

 

local_irq_disable()与local_irq_save(flags)除了进行禁止中断的操作以外,还保存目前CPU的中断位信息,local_irq_restore(flags)进行的是与local_irq_save(flags)相反的操作。仅想禁止中断底部,应使用local_bh_disable(),使能被local_bh_disable禁止的底半步调用local_bh_enable()。

 

信号量 :用于保护临界区的一种常用方法,它的使用方式和自旋锁类似。与自旋锁相同,只有得到信号量的进程才能执行临界区代码。但是与自旋锁不同的是,进程不会原地打转而是进入休眠状态。两者区别详见http://blog.csdn.net/mia_go/archive/2010/10/24/5961878.aspx

内核对信号量 semaphore的描述:

等待队列wait_list定义为list_head类型,双向链表。spinlock_t是如何定义?见linux/include/linux/spinlock_types.h。

  

信号量使用如下:

    1.定义一个名为sem的信号量: struct semaphore sem;

    2.初始化信号量:void sema_init (struct semaphore  *sem,int val); //该函数初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。

       void init_MUTEX(struct semaphore *sem); //该函数用于初始化一个用于互斥的信号量,它把信号量sem的值设置成1或0,等同于sema_init(struct semaphore *sem,1或0)。 

     此外,两个宏定义并初始化信号量的“快捷方式”。

       DECLEAR_MUTEX(name)  //定义一个名为name的信号量并初始化为1

       DECLEAR_MUTEX_LOCKED(name)  //定义一个名为name的信号量并初始化为0

     3.获得信号量:

        void down(struct semaphore *sem); //该函数获得sem会导致失眠,因此不能再中断上下文使用。类似的,int down_interruptible(struct semaphore *sem)/int down_trylock(struct semaphore *sem),这里不再作解释。

     4.释放信号量:

       void up(struct semaphore *sem); //释放信号量sem,唤醒等待者。

信号量一般使用如下:

      DECLEAR_MUTEX(mount_sem); //定义信号量

      down(&mount_sem); //获取信号量,保护临界区

      ... ...

      critical section //临界区

       ... ...

      up(&mount_sem); //释放信号量       

具体看过来:      

 

对于down()进一步到底层来看:

在这个函数中,%0 对应于sem->count,如果sem->count 减1 后,结果不小于0,则说明down 操作成功完成。否则调用__down_failed,它最终将调用__down()把当前进程设置为等待状态,并把当前进程加入到该信号量的等待队列,调度其他进程来运行。当某个进程释放相应的资源时调用up()。

回过头来继续spin_lock_irqsave:

 

 

由于中断许可位位于CPU 的标志寄存器中,因此spin_lock_irqsave()在获取自旋锁之前,把标志寄存器的值保存到flags 中,而spin_unlock_irqrestore()在释放自旋锁之后,占,由于中断或系统调用之后,可能会调度其他的进程运行(根据flags 恢复标志寄存器。这是通过下面的两个函数完成的。preempt_disable()的作用是关闭进程抢例如当前进程时间片用完,或者有一个拥有更高优先级的进程已经进入了就绪状态),preempt_disable()关闭调度器的这个功能,从而保证当前进程在执行临界区代码的过程中不会被其他进程干扰。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值