zoukankan      html  css  js  c++  java
  • linux设备驱动程序之并发和竞态(二)

            事实上这blog都是阅读ldd3时的一些总结,巩固自己的学习。也方便后期的使用。大家也能够直接阅读ldd3原文。


    锁陷阱

            所谓的锁陷阱就是防止死锁。
            不明白的规则:
            1、不论是信号量还是 自旋锁,都不同意锁拥有者第二次获得这个锁(会死锁)。
            2、系统直接调用的那些函数要获得信号量,保护要訪问的设备结构。而内部函数的訪问则能够依据须要上锁。

            锁顺序规则:
            1、假设都要获取一系列锁的话,那么能够依照一定顺序规则来获取锁,即:获取多个锁时,锁的顺序一直。
            2、假设要获取自己的局部锁和系统的中心锁,则先获取自己的局部锁,然后再去获取中心锁;
            3、假设要获取信号量和自旋锁,则须要先获取信号量,由于释放信号量的函数down有可能会休眠。

            免锁算法:
            有非常多的免锁算法,ldd3中提到一种循环缓存区,感觉非常实用(工作中也遇到过)。
            循环缓冲区:就是比方一个数组A[9]为临界区。能够从A[0]端往A[9]端方向读取数据,从A[9]端往A[0]端写入数据。

    就是一个互相追逐的算法,这样能够不用锁就能保证数据的一致性;

            工作中也遇到过这样的缓冲区,把请求结构体往一个循环队列中放。而内核在队列的另外一端读取请求结构体去运行。

    原子操作

            原子操作在内核源码中使用很频繁,事实上这个原子操作主要是对变量操作的。在本质上来说这也是一种锁。由于假设我们用一种锁机制去锁住一个变量,这样就有点浪费了。毕竟锁还是要耗费cpu的一些时间的,为了一个小小的变量,动用锁,不值当。

    所以原子操作就应运而生了。

            头文件 <asm/atomic.h>,数据结构为:atomic_t

            void  atomic_set(atomic_t  *v,  int  i); // 动态初始化,就是原子赋值:v = i
            atomic_t  v  = ATOMIC_INIT(o);// 静态初始化,在编译时初始化

            int  atomic_read(atomic_t  *v);// 返回V的当前值

            void  atomic_add(int i,  atomic_t  *v);// 将i 加到V指向的原子变量上,返回为void,是由于大多数情况下不须要这个返回值,所以不返回,减少函数成本;
            void  atomic_sub(in  i,  atomic_t  *v);// 和上面函数功能相反

            void  atomic_inc(atomic_t  *v);// 自增
            void  atomic_dec(atomic_t  *v);// 自减

           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);
           运行操作并測试结果:假设操作后,原子值为0。则返回true;否则返回false;

           int atomic_add_negative(int  i,  atomic_t  *v);// 将i加到v上,假设结果为负数。返回true。否则为false;

            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);       
            运行上面相应的操作,然后返回操作后的新值V

    位操作

            和原子操作类似,位操作其本质也算是一种锁机制。仅仅是相对来说粒度会小些。

    信号量和自旋锁是对一段代码,一块内存等进行保护的;原子操作是对变量进行保护的;而位操作是对变量中的各个位进行设置的,当然这也是原子操作。

    由于原子操作很快,能够用单个机器指令来运行,所以不用禁止中断。

            nr參数一般被定义为int,但也有些不同的系统定位为unsigned long等;        

            void  set_bit(nr,  void *addr);// 设置addr指向的数据项的第nr位
            void  clear_bit(nr,  void *addr);//清楚addr指向的数据项的第nr位

            void  change_bit(nr,  void *addr);// 切换指定位

            test_bit(nr,  void *addr);//返回当前值

            int   test_and_set_bit(nr,  void *addr);
            int   test_and_clear_bit(nr,  void *addr);
            int   test_and_change_bit(nr,  void *addr);
            运行相应的操作,而且返回  先前  的值

    seqlock

            seqlock可对共享资源的高速、免锁訪问。当要保护的资源非常小、非常easy、会频繁被訪问并且写入訪问非常少发生且必须高速时,就能够使用seqlock。seqlock同意读取者自由訪问。但要检查是否和写入者发生冲突,当发生冲突时,要又一次读取数据。seqlock不保护含有指针的数据结构。由于在写入 者改动该数据的时候,读取者可能会读取一个无效的指针;
            头文件 <linux/seqlock.h> 
            初始化:
            seqlock_t   lock1 = SEQLOCK_UNLOCKED;//静态初始化
            seqlock_t   lock2;
            seqlock_init(&lock2);// 动态初始化

            读取者在訪问临界区时。先获取一个无符号整数,然后完毕自己的操作,退出时再获取下该整数。对照下看是否相等;假设不相等,则须要又一次获取个整数==》完毕操作==》退出获取參数比較;代码例如以下:
            unsigned  int seq;
            do {
                   seq = read_seqbegin(&the_lock);
                   /* 完毕对应的工作 */
            }while (read_seqretry(&the_lock,  seq));
            这类锁仅仅是用来保护简单的计算,可是须要数据一致性,所以不一致就必须又一次读取数据;

            假设在中断处理例程中使用seqlock,则应该使用IRQ安全的版本号:
            unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned  long flags);
            int read_seqretry_irqrestore(seqlock_t *lock,unsigned int seq,  unsigned  long flags);

            写入者进入由seqlock保护的临界区时必须获得一个相互排斥量:
            void  write_seqlock(seqlock_t  *lock);
            void  write_sequnlock(seqlock_t  *lock);

            因为写入锁使用自旋锁实现,自旋锁控制写入訪问,所以自旋锁的常见变种都能够使用:
            void  write_seqlock_irqsave(seqlock_t  *lock, unsigned long  flags); 
            void  write_seqlock_irq(seqlock_t  *lock);
            void  write_seqlock_bh(seqlock_t  *lock);

            void  write_sequnlock_irqrestore(seqlock_t  *lock, unsigned long  flags);
            void  write_sequnlock_irq(seqlock_t  *lock,);
            void  write_sequnlock_bh(seqlock_t  *lock);



  • 相关阅读:
    python Database Poll for SQL SERVER
    SQLAlchemy表操作和增删改查
    flask动态url规则
    flask配置管理
    一个Flask运行分析
    Function Set in OPEN CASCADE
    Happy New Year 2016
    Apply Newton Method to Find Extrema in OPEN CASCADE
    OPEN CASCADE Multiple Variable Function
    OPEN CASCADE Gauss Least Square
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7381627.html
Copyright © 2011-2022 走看看