• linux设备驱动中的并发控制


    并发指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源的访问则很容易导致竞态

    linux内核中主要竞态
    1.多对称处理器的多个CPU  2.单CPU内进程与抢占它的进程 3.中断(硬中断、软中断、Tasklet、下半部)与进程之间
    访问共享内存资源的代码区称为“临界区”,临界区需要被以某种互斥机制加以保护,中断屏蔽、原子操作、自旋锁和信号量等
    是linux设备驱动中可采用的互斥途径。

    这几个互斥的介绍:

    1.中断屏蔽,这个主要用于单CPU,中断屏蔽将使得中断和进程之间的并发不再发生。使用方法:

    local_irq_disable();//屏蔽中断
    ...
    ...
    临界区
    ...
    local_irq_enable();//开中断
    由于linux的异步IO、进程调度等很多重要的操作都依赖于中断,中断对于内核的运行非常重要,在屏蔽中断期间所有的中断都无法处理,
    因此长时间的屏蔽中断很危险,有可能导致数据丢失甚至系统崩溃。所以这个不作为重点讨论。

    **********************************************************************************************************************************************************

    2.原子操作,原子操作是一系列的不能被打断的操作。linux内核提供了一系列的函数来实现内核中的原子操作,这些函数分为2类,分别针对位和整型变量
    进行原子操作。
    实现原子操作的步骤:

    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.操作并测试:对原子变量执行自增、自减后(没有加)测试其是否为0,如果为0返回true,否则返回false。
    int atomic_inc_and_test(atomic_t *v);
    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_t * v);
    int atomic_dec_return(atomic_t * v);

    **********************************************************************************************************************************************************
    3.自旋锁
    自旋锁是一个忙锁,它在一个小的循环内不断的重复测试并设置的操作。
    自旋锁保护临界区的特点:临界区要小,并且临界区内不能有导致睡眠的操作,否则可能引起系统崩溃。自旋锁可能导致系统死锁,
    引发这个问题最常见的情况是递归使用一个自旋锁。

    自旋锁的操作步骤:

    1.定义自旋锁
    spinlock_t lock;
    2.初始化自旋锁
    spin_lock_init(lock);这是个宏,它用于动态初始化自旋锁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);//释放自旋锁

    自旋锁不关心锁定的临界区究竟是如何执行的。不管是读操作还是写操作,实际上,对共享资源进行读取的时候是应该可以允许多个执行单元同时
    访问的,那么这样的话,自旋锁就有了弊端。于是便衍生出来一个读写锁。

    它保留了自旋的特性,但在对操作上面可以允许有多个单元进程同时操作。当然,读和写的时候不能同时进行。

    现在又有问题了,如果我第一个进程写共享资源,第二个进程读的话,一旦写了,那么就读不到了,可能写的东西比较多,但是第二个进程读很小,那么
    能不能第一个进程写的同时,我第二个进程读呢?
    当然可以,那么引出了顺序锁的概念。都是一样的操作。


    **********************************************************************************************************************************************************
    4.信号量
    是用于保护临界区的一种常用的方法,它的使用与自旋锁差不多,但是它不在原地打转,当获取不到信号量时候,进程会进入休眠等待状态。

    主要操作:

    1.定义sem信号量
    struct semaphore sem;
    2.初始化信号量
    void sema_init(struct semaphore *sem, int val);
    初始化信号量,并设置sem的值为val
    初始化的时候还可以这样用,init_MUTEX(sem),这是个宏 #define init_MUTEX(sem) sema_init(sem , 1)
           init_MUTEX_LOCKED(sem),这是个宏 #define init_MUTEX_LOCKED(sem) sema_init(sem , 0)

    3.获得信号量
    void down(struct semaphore * sem);
    该函数用于获得信号量sem,他会导致睡眠,所以不能在中断中使用。
    int down_interruptible(struct semaphore* sem);
    与上面功能类似,因为down进入睡眠状态的进程不能被信号打断,而它能被信号打断,信号也会导致该函数返回。
    int down_trylock(struct semaphore * sem);

    4.释放信号量
    void up(struct semaphore * sem);
    该函数用于释放信号量,同时唤醒等待者。
    信号量一般这样使用:

    DECLARE_MUTEX(sem);

    down(&sem);

    .....临界区

    up(&sem);

    linux自旋锁和信号量采用的“获取锁-访问临界区-释放锁”的方式。


    *************************************************************************************************************************

    5.互斥体


    互斥体和信号量基本上差不多。不介绍了。


    总结:并发和竞态广泛存在,这几个机制是解决问题的好方法,中断屏蔽很少单独使用,原子操作只能针对整数进行,因此,自旋锁和信号量应用最为广泛。
    自旋锁会导致死循环,锁定期间不允许阻塞,因此要求锁定的临界区要小。信号量允许临界区阻塞,可以适用于临界区较大的情况。读写自旋锁和读写信号量是
    放宽了条件的自旋锁和信号量,他们允许多个进程并发的读取共享空间。

  • 相关阅读:
    cocos2dx android java调用C++
    cocos2dx android c++调用java
    cocos2dx android collection
    cocos2dx学习之windows android环境搭建
    windows phone 8.0 与 windows phone7.1区别
    C#文件同步工具教程
    Web2.0时代,RSS你会用了吗?(技术实现总结)
    介绍一篇关于session的好文章,写的很详细(jspservlet 技术)
    利用HttpModuler实现WEB程序同一时间只让一个用户实例登陆
    css
  • 原文地址:https://www.cnblogs.com/chd-zhangbo/p/5267658.html
Copyright © 2020-2023  润新知