linux 并发控制

一、 什么是并发,为何要使用并发机制
并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源
(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions)
 并发即是 多件事情同时在执行  只有使这些设备都并发的执行才能满足性能要求。      
  例:
      如果系统上挂10个设备,每个设备都请求,如果是串行顺序执行,
      可能一个设备要等较长时间,系统才能响应它    

二、静态产生的几种情况
1、对称多处理器(SMP)的多个CPU
2、单CPU内进程与抢占它的进程
      一个进程在内核执行的时候可能被另一个高优先级进程打断
3、中断(硬中断,软中断,Tasklet,底半部)与进程之间
     中断可以打断正在执行的进程,如果中断处理程序访问进程正在访问的资源,或者出现更高级的中断

三、 如何实现并发
    CPU是顺序的读入指令执行的, 如何来实现并发呢?    
    例:
       当你A先生的业务时,B女士来个电话,你接完电话后继续做A的事。
       如何你切换足够快,即在A,B的忍耐时间内。 感觉就像在并发执行一样。

    使用中断的方式可实现并发    //中断: 类似于接电话,打断当前执行,等处理完后,返回再接着执行。

四、 为什么要进行并发控制
    并发会导致竞态的发生。 //竞态: 一个资源被并发执行的任务同时访问时,就会出现竞争。此时的状态就是竞态。
    需保证一个资源在一个时间只能被一个任务访问,才能避免混乱。即并发控制    

五、 并发机制使用场合
    三个并发:    进程    中断    CPU

    1. 中断屏蔽的使用场合  //用于 中断和中断间。进程 和 中断 竞争时
        当有中断处理程序访问共享资源的时候。
    2. 原子操作的使用场合  //用于 进程间 中断间 综合竞争时  (信号量 中断屏蔽等都是基于原子操作来实现的)
       只使用于共享资源为一个变量的操作的情况
    3. 自旋锁的使用场合   //用于 多CPU 竞争时, (尽量不用,因有CPU死掉的风险)
       多CPU的情况下 
       在临界区代码运行时间比较短的情况。
    4. 信号量互斥体的使用场合   //用于 进程间竞争时  (它不能防止中断的打断,故要防止中断打断,虚与中断屏蔽混合使用)
       临界区代码运行时间比较长的情况。  
       当锁持有的时间较长的时候,优先使用信号量。
       1注意: 中断里不能使用信号量。 因为中断不能睡眠。

六、 如何进行并发控制
 采用互斥机制对并发进行控制  //互斥: 共享资源被某任务访问时,别的任务禁止访问。
1、 中断屏蔽     
          使用禁止中断方式,避免中断的影响 

          local_irq_disable       //屏蔽中断
          临界区代码              //临界区: 访问共享资源的代码区域
          local_irq_enable        //开中断
          以上函数只能禁止和使能CPU内的中断,并不能解决SMP多CPU引发的竟态
          注: 
             1。屏蔽中断的时间尽量短,否则会造成别的中断延时过久,甚至丢失,最好能采有屏蔽指定中断的方式
                disable_irq(int irq); //屏蔽指定中断号的中断
                enable_irq(int irq);  //使能某中断号的中断   
              2。常需保存当前的中断状态,便于恢复  ,与 local_irq_disable,local_irq_enable 相比除了禁止中断操作外,还保存中断信息,
                用local_irq_save(flags);  代替local_irq_disable
                用local_irq_restore(flags) 代替 local_irq_enable
            3、如果只想禁止中断的底半部
                 用  local_bh_disable ()      
                 用  local_bh_enable ()     

2、 原子操作
          并发中不被打断的最小单元,与CPU架构密切相关

(1)整型原子操作
定义于#include<asm/atomic.h>
分为 定义,获取,加减,测试,返回。

void atomic_set(atomic_t *v,int i);    //设置原子变量v的值为i
atomic_t v = ATOMIC_INIT(0);     //定义原子变量v,并初始化为0;

atomic_read(atomic_t* v);     //返回原子变量v的值;

void atomic_add(int i, atomic_t* v);     //原子变量v增加i;
void atomic_sub(int i, atomic_t* v);    

void atomic_inc(atomic_t* v);     //原子变量增加1;
void atomic_dec(atomic_t* v);    

int atomic_inc_and_test(atomic_t* v);        //先自增1,然后测试其值是否为0,若为0,则返回true,否则返回false;
int atomic_dec_and_test(atomic_t* v);        
int atomic_sub_and_test(int i, atomic_t* v);     //先减i,然后测试其值是否为0,若为0,则返回true,否则返回false;
注意:只有自加,没有加操作

int atomic_add_return(int i, atomic_t* v);   //v的值加i后返回新的值;
int atomic_sub_return(int i, atomic_t* v);  
int atomic_inc_return(atomic_t* v);     //v的值自增1后返回新的值;
int atomic_dec_return(atomic_t* v);    

(2)位原子操作
定义于#include<asm/bitops.h>
分为 设置,清除,改变,测试

void set_bit(int nr, volatile void* addr);        //设置地址addr的第nr位,所谓设置位,就是把位写为1;
void clear_bit(int nr, volatile void* addr);      //清除地址addr的第nr位,所谓清除位,就是把位写为0;

void change_bit(int nr, volatile void* addr);     //把地址addr的第nr位反转;

int test_bit(int nr, volatile void* addr);    //返回地址addr的第nr位;

int test_and_set_bit(int nr, volatile void* addr);    //测试并设置位;若addr的第nr位非0,则返回true; 若addr的第nr位为0,则返回false;
int test_and_clear_bit(int nr, volatile void* addr);    //测试并清除位;
int test_and_change_bit(int nr, volatile void* addr);    //测试并反转位;
上述操作等同于先执行test_bit(nr,voidaddr)然后在执行xxx_bit(nr,voidaddr)

(3)示例
      {//---hello_atomic/hello.c         实现设备只被一个进程打开
          #include <asm/atomic.h>

          static atomic_t hello_atomic = ATOMIC_INIT(1);    //定义原子变量hello_atomic ,并初始化为1 
        static int hello_open(struct inode *inode,struct file *file)
        {
          if (!atomic_dec_and_test(&hello_atomic))  {  
               //  atomic_dec_and_test表示原子变量自减一,并测试是否为了零,如果为零返回真
               //当已open过,还未release时,再次open , hello_atomic为0, 再减一不为零,!atomic_dec_and_test 成立  
             atomic_inc(&hello_atomic);       //原子变量加一,恢复自减前状态
             return  - EBUSY;   //已经被打开
          }
          //当第一次被open时, hello_atomic为1  , !atomic_dec_and_test 不成立, 正常打开
          printk("hello open \n");
          return 0;
        } 
        static int hello_release(struct inode *inode,struct file *file)
        { 
          atomic_inc(&hello_atomic);   //释放设备    
          printk("hello release\n");
          return 0;
        }
    }    

3、 自旋锁    spinlock  (cpu自旋 类似while循环)
          cpu自旋死循环空转CPU 等待释放锁,  耗资源, 适用于锁持有时间小于睡眠唤醒时间场合    
          进程递归获得锁时,会导致死锁

          {//---hello_spinlock/hello.c
             static DEFINE_SPINLOCK(hello_spinlock); 
             static int hello_resource = 1;

             static int hello_open(struct inode *inode,struct file *file)
             {
               spin_lock(&hello_spinlock);  //还可和中断屏蔽合用 spin_lock_irq    
               if(hello_resource == 0)        //为避免死机, spin_try_lock
               {
                  spin_unlock(&hello_spinlock); 
                  return  - EBUSY; 
               }
               hello_resource--;
               spin_unlock(&hello_spinlock); 


               printk("hello open \n");
               return 0;
             } 
             static int hello_release(struct inode *inode,struct file *file)
             {
               spin_lock(&hello_spinlock);
               hello_resource++; 
               spin_unlock(&hello_spinlock);

               printk("hello release\n");
               return 0;
             }
     }

4、 信号量   
       进程当中用于并发互斥和,资源的计数。
       相对于自旋锁,信号量会睡眠,仅能用于进程中 
    //---hello_semaphore/hello.c
      #include <linux/semaphore.h>
    static DECLARE_MUTEX(hello_semlock);   //定义一个初始值为一的信号量

    static int hello_open(struct inode *inode,struct file *file)
    {            
      if (down(&hello_semlock))  /* 获得打开锁*/
      {
         return  - EBUSY;  /* 设备忙*/
      }

      printk("hello open \n");
      return 0;
    }
    static int hello_release(struct inode *inode,struct file *file)
    {
      up(&hello_semlock);  /* 释放打开锁*/
      printk("hello release\n");
      return 0;
    }      

5、 互斥体  用得最多
       互斥体是专门用来做互斥的, 和二元的信号量类似, 

        struct mutex my_mutex;  /* 定义mutex */
        mutex_init(&my_mutex);  /* 初始化mutex */

        mutex_lock(&my_mutex);  /* 获取mutex */
        .../* 临界资源*/
        mutex_unlock(&my_mutex); /* 释放mutex */
   }    











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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值