Linux内核开发之并发控制(一)

来源:互联网 发布:淘宝装修图 编辑:程序博客网 时间:2024/05/01 22:44

“小涛,你说十一黄金周,火车站,飞机场那些售票系统咋没一个宕掉的呢。你不宕掉也没关系,来两个卖错票的,说不定哥就去上海看世博,去北京看青梅竹马的表妹了…”小王抱怨道。

“晕死..哥鄙视你,你说都老大不小的人了,怎么脑子里天天都是MM之类的事了,能不能有点男子气概啊..”。

“靠,能跟你比啊,你是饱汉不知饿汉饥,要是像你一样十一和…”

  "嗯,啊,哼哼.."没等他说完,我赶忙塞了双臭袜子(哪天的也记不住了)。“得得,I 服了 you,ok”。

“不过话说回来,小王,你说的还真是个问题,想想这样的问题,你和GF两个要去西湖看白娘子,偏偏遇到老天跟你过不去,就只剩下一张票了,你和GF两个谁去…“

"不是吧,我这命苦,好不容易有个GF,应该不会出现的,呵呵,如果出现…那好办,我和她商量好,咱们一起在晚上12:00一起买票,这样我们两边的售票员怎么看都各自有一张票,我们两个就可以一起了"小王狡黠的笑着。

“笨,我一口一个盐水喷死你,分明一张票,你们两个同一个时间去两个不同的售票点去买,它还是一张票,怎么可能说去两个不同地方,两个售票员都看到有一张票,然后就把这唯一的一张同时卖给了你们两个人”我打断到。"算了,看在室友兼我的最忠实狗仔队员的身份,哥就传授一招只传MM的绝学----Linux设备驱动程序之并发控制"。

听说过并发没,那你肯定听说过竞争,比如竞争上岗,还有你最熟悉的追MM,这也是竞争。那么

并发(concurrency)就是说多个执行单元同时,并行被执行,这多个单元却不巧要同时访问一些资源。这其中要分三种情况:

     竞态

正所谓:道高一尺,魔高一丈,你孙悟空有72变,人家二郎神还有73变不是。有问题,没关系,找小涛哥不是..呵呵。现在就教你几招以备不时只需:

大家不是要竞争吗,那好,总体原则就是不让你竞争:保证一个执行单元在访问共享资源的时候,其他的执行单元被禁止访问,将竞争扼杀在萌芽状态。这就是传说中的对共享资源的互斥访问。

出招表一:中断屏蔽(可以保证正在执行的内核执行路径不被中断处理程序抢占,由于Linux内核的进程调度都依赖中断来实现,内核抢占进程之间的竞态就不存在了)

    使用方法: local_irq_disable()  //屏蔽中断                                   说明:local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU内的中断

                 ….                                                                               并不能解决SMP多CPU引发的竞争。

                 critical section  //临界区

                 ….

                 local_irq_enable()  //开中断 

    与local_irq_disable()不同,local_irq_save(flags)除了进行禁止中断操作以外,还保证目前CPU的中断位信息,local_irq_save(flags)进行相反的操作。

    致命弱点: 由于Linux系统的异步I/O,进程调度等很多重要操作都依赖于中断,在屏蔽中断期间所有的中断都无法处理,因此长时间屏蔽中断是很危险的,有可能造

                成数据丢失甚至系统奔溃。 

出招表二:原子操作(忘了是物理还是化学老师拉着我的手说:原子是最小的,不能再分的东西.看多形象,执行过程不能被别的代码路径中断的操作就是原子操作,还

             想跟我竞争,门都没有)。 分为整形原子和位原子操作。 

    使用方法一:整形原子操作

                    1)设置原子变量的值

              void atomic_set(atomic_t *v, int i);//设置原子变量的值为i            atomic_t v = ATOMIC_INIT(0);//定义原子变量v并初始化为0
             2)获取原子变量的值
              atomic_read(atomic_t *v);//返回原子变量的值
                    3)原子变量加/减
              void atomic_add(int i,atomic_t *v);  //原子变量增加i            void atomic_sub(int i,atomic_t *v);  //原子变量减少i
            4)原子变量自增/自减
              void atomic_inc(atomic_t *v);  //原子变量加1            void atomic_dec(atomic_t *v);  //原子变量减1
            5)操作并测试
              int atomic_inc_and_test(atomic_t *v);//这些操作对原子变量执行自增,自减,减操作后测试是否为0,是返回true,否则返回false            int atomic_dec_and_test(atomic_t *v);            int atomic_sub_and_test(int i, atomic_t *v);
                   6)操作并返回
              int atomic_add_return(int i,atomic_t *v);  //这些操作对原子变量进行对应操作,并返回新的值。              int atomic_sub_return(int i, atomic_t *v);            int atomic_inc_return(atomic *v);            int atomic_dec_return(atomic_t *v);
   使用方法二:位原子操作。
             1)设置位
              void set_bit(nr, void *addr);  //设置addr地址的第nr位,所谓设置位即将位写为1           2)清除位
              void clear_bit(nr,void *addr);  //清除addr地址的第nr位,所谓清除位即将位写为0           3)改变位
              void change_bit(nr,void *addr);  //对addr地址的第nr位反置             4)测试位
              void test_bit(nr, void  *addr);  //返回addr地址的第nr位                      5)测试并操作位
              int test_and_set_bit(nr, void *addr);            int test_and_clear_bit(nr, void *addr);            int test_and_change_bit(nr, void *addr);
                 光说不练,不是好汉。这谁说的呢,咋就是记不得呢,看段代码:
              static atomic_t ato_avi = ATOMIC_INIT(1); //定义原子变量              static int ato_open(struct inode *inode, struct file *filp)              {              ...              if (!atomic_dec_and_test(&ato_avi))              {                atomic_inc(&ato_avi);                return = - EBUSY;  //已经打开                }              ..              return 0;  //已经打开              }            static int ato_release(struct inode *inode, struct file *filp)            {              atomic_inc(&ato_avi);              return 0;            }
 出招表三:自旋锁。正如其名,CPU上将要执行的代码将会执行一个测试并设置某个内存变量的原子操作,若测试结果表明锁已经空闲,则程序获得这个自旋
         锁继续运行;若仍被占用,则程序将在一个小的循环内重复测试这个"测试并设置"的操作.这就是自旋。
使用方法:1)spinlock_t spin; //定义自旋锁
             2)spin_lock_init(lock);  //初始化自旋锁
             3)spin_lock(lock);  //成功获得自旋锁立即返回,否则自旋在那里直到该自旋锁的保持者释放
               spin_trylock(lock); //成功获得自旋锁立即返回真,否则返回假,而不是像上一个那样"在原地打转"
           4)spin_unlock(lock);//释放自旋锁
    自旋锁一般像下边这样使用:
      spinlock_t lock;     spin_lock_init(&lock);     spin_lock (&lock);     ....//临界区      spin_unlock(&lock);
     还记的前边说的第一招:中断屏蔽中致命的弱点步,自旋锁就是针对SMP或单个CPU但内核可抢占的情况,对于但CPU和内核不可抢占的系统,自旋锁退化为空操作。还有就是自旋锁解决了临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候还可能受到中断和底半部的影响。
     那咋办呢,天要下雨,娘要嫁人,可二郎神的就是比你孙悟空多了一变,你能咋办,打一架?打不过..所以说嘛,Linux社区的开发者们早想到了办法:在自旋锁的基础上进行衍生,具体是怎么回事,且听下回分解(每次说这句话是感觉好爽,这难道就是高手耍酷的乐趣..)..

 

                     
 

                   

 
原创粉丝点击