|簡體中文

比思論壇

 找回密碼
 按這成為會員
搜索



查看: 647|回復: 0
打印 上一主題 下一主題

Linux设备中的并发控制

[複製鏈接]

989

主題

1

好友

5288

積分

教授

Rank: 8Rank: 8

  • TA的每日心情

    2024-9-18 07:17
  • 簽到天數: 1252 天

    [LV.10]以壇為家III

    推廣值
    2
    貢獻值
    378
    金錢
    1714
    威望
    5288
    主題
    989

    回文勇士 文明人 中學生 高中生 簽到勳章 簽到達人 男生勳章 大學生 文章勇士 附件高人 附件達人 文章達人 教授

    樓主
    發表於 2012-9-21 00:20:32
    Linux设备中的并发控制



    并发执行的唯一原因是:硬件中断服务。

    内核代码是可抢占的。

    什么是竞争?:

    当 2 个执行的线路[有机会操作同一个数据结构(或者硬件资源), 混合的可能性就一直存在。



    避免竞争方法:

    在你设计驱动时在任何可能的时候记住避免共享的资源. 如果没有并发存取, 就没有竞争情况. 因此小心编写的内核代码应当有最小的共享. 这个想法的最明显应用是避免使用全局变量. 如果你将一个资源放在多个执行线路能够找到它的地方, 应当有一个很强的理由这样做。
      

    全局变量不是共享资源的唯一方法,传递一个指针给内核的其他代码,潜在的创造了一个新的共享形式。

    存取管理的常用技术是加锁或者互斥,确保在任何时间内只有一个执行线程可以操作一个共享资源。


    当内核代码创建一个会被内核其他部分共享的对象时, 这个对象必须一直存在(并且功能正常)到它知道没有对它的外部引用存在为止。

    临界区:在任何给定的时间内只有一个线程可以执行代码。
      

    旗标(信号量):是一个整型值,结合一对函数,一个像进入临界区的进程将调用相关旗标的P,如果旗标的值大于0,这个值将减一并且继续进程。相反,如果旗标的值是0(或者更小),进程必须等待别的进程释放旗标,解锁旗标通过调用V完成,这个函数会增加旗标的值,并且。如果需要,可以唤醒等待的进程。



    当旗标初始化为1时,就成了互斥锁。

    初始化:void sema_init(struct semaphore *sem, int val)    /* val 表示初始化值 */

    互斥锁初始化: DECLARE_MUTEX(name);              /* 1 name为信号量变量*/

                   DECLARE_MUTEX_LOCKED(name);       /* 0 同上*/

    动态初始化(运行时):

    void init_MUTEX(struct semaphore *sem);

    void init_MUTEX_LOCKED(struct semaphore *sem);

    P函数成为down—或者这个名字的变体:作用是是信号量减一,并且可能使调用者睡眠一会,等到旗标变为可用之后,再对共享资源进行存取。



    void down(struct semaphore * sem);          /* 递减信号量,并等待需要的时间 */

    int down_interruptible(struct semaphore *sem); /* 可被中断的:允许一个在等待用户空间的进程被用户中断 */

    int down_trylock(struct semaphore *sem);     /* 从不睡眠 */

    V函数:void up(struct semaphore *sem);  /* 当持有一个旗标时遇到一个错误时,旗标必须在返回错误状态之前释放旗标。

    注意:旗标必须在设备对系统的其他部分可用前初始化。



    read and write semaphore:

    down_read(struct rm_semaphore *sem);  --------  down_write(struct rm_semaphore *sem)

    int down_read_trylock(struct rw_semaphore *sem);  -----  down_write_trylock(struct rw_semaphore *sem);

    void up_read(struct rw_semaphore *sem);  ---- void up_write(struct rw_semaphore *sem);  

    void downgrade_write(struct rw_semaphore *sem); //一旦完成改变后允许其他read进入。

    what is the meaning of Completions?

    允许一个线程告诉另一个线程工作已经完成。



    一个completion的创建:

    <linux/completion.h>

    DDECLARE_COMLETION(my_completion);

    动态创建和初始化:

    struct completion my_completion;

    init_completion( &my_completion);



    简单的调用:

    void wait_for_completion(struct completion *c)  //如果没有人完成这个任务,结果会是一个不可杀死的进程。

    一般调用:

    void complete(struct completion *c);   /* 一个等待进程 */

    void complete_all(struct completion *c); /* 多个等待进程 */


    what is the meaning of spinlock?



    background: 大部分加锁是有一种自旋锁机制来实现;

    special case:自旋锁可用在不能睡眠的代码中,例如中断处理;
      

    concept:互斥设备,上锁和解锁;如果锁是可用的, 这个"上锁"位被置位并且代码继续进入临界区. 相反, 如果这个锁已经被别人获得, 代码进入一个紧凑的循环中反复检查这个锁, 直到它变为可用. 这个循环就是自旋锁的"自旋"部分。(简单的形式并不是好的形势)

    要求:原子操作;在超线程上避免死锁。



    #include<linux/spinlock.h>

    初始化定义:spinlock_t my_lock = SPIN_LOCK_UNLOCKED;

    或者在运行时使用:void spin_lock_init(spinlock_t *lock);

    在进入一个临界区时,代码必须获得需要的lock:

    void spin_lock(spinlock_t *lock);  /* 由于不可中断 自旋直到锁变为可用*/



    释放锁:

    void spin_unlock(spinlock_t *lock);

    原则上:不能睡眠;不能因为任何原因放弃处理器,除了服务中断(并且有时即便如此也不行);自旋锁必须一直是尽可能短时间得持有。
      

    原因:内核抢占;睡眠可能发生在从用户空间拷贝数据,分配内存的任何操作等等;中断在获得自旋操作之后,但是中断操作中的代码又需要获得这把锁,这就会发生永久的自旋。(避免这个陷阱需要在持有自旋锁时禁止中断)



    void spin_lock(spinlock_t *lock);

    void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);

    void spin_lock_irq(spinlock_t *lock);

    void spin_lock_bh(spinlock_t *lock)


    void spin_unlock(spinlock_t *lock);

    void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);

    void spin_unlock_irq(spinlock_t *lock);

    void spin_unlock_bh(spinlock_t *lock);



    陷阱锁:

    模糊的规则

    加锁顺序规则

    细 – 粗 – 粒度加锁


    原子变量:<asm/atomic.h>

    void atomic_set(atomic_t *v, int i);

    atomic_t v = ATOMIC_INIT(0);
      

    设置原子变量 v 为整数值 i. 你也可在编译时使用宏定义 ATOMIC_INIT 初始化原子值.

    int atomic_read(atomic_t *v);

    返回 v 的当前值.



    void atomic_add(int i, atomic_t *v);

    由 v 指向的原子变量加 i. 返回值是 void, 因为有一个额外的开销来返回新值, 并且大部分时间不需要知道它.

    void atomic_sub(int i, atomic_t *v);

    从 *v 减去 i.

    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, 那么返回值是真; 否则, 它是假. 注意没有 atomic_add_and_test.

    int atomic_add_negative(int i, atomic_t *v);



    加整数变量 i 到 v. 如果结果是负值返回值是真, 否则为假.
      

    位操作:<asm/bitops.h>

    void set_bit(nr, void *addr);

    设置第 nr 位在 addr 指向的数据项中.

    void clear_bit(nr, void *addr);
      

    清除指定位在 addr 处的无符号长型数据. 它的语义与 set_bit 的相反.

    void change_bit(nr, void *addr);

    翻转这个位.

    test_bit(nr, void *addr);

    这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.
    重要聲明:本論壇是以即時上載留言的方式運作,比思論壇對所有留言的真實性、完整性及立場等,不負任何法律責任。而一切留言之言論只代表留言者個人意見,並非本網站之立場,讀者及用戶不應信賴內容,並應自行判斷內容之真實性。於有關情形下,讀者及用戶應尋求專業意見(如涉及醫療、法律或投資等問題)。 由於本論壇受到「即時上載留言」運作方式所規限,故不能完全監察所有留言,若讀者及用戶發現有留言出現問題,請聯絡我們比思論壇有權刪除任何留言及拒絕任何人士上載留言 (刪除前或不會作事先警告及通知 ),同時亦有不刪除留言的權利,如有任何爭議,管理員擁有最終的詮釋權。用戶切勿撰寫粗言穢語、誹謗、渲染色情暴力或人身攻擊的言論,敬請自律。本網站保留一切法律權利。

    手機版| 廣告聯繫

    GMT+8, 2024-9-28 01:18 , Processed in 0.017433 second(s), 27 queries , Gzip On.

    Powered by Discuz! X2.5

    © 2001-2012 Comsenz Inc.

    回頂部