zoukankan      html  css  js  c++  java
  • linux内核情景分析之内核中的互斥操作

    信号量机制:
    struct sempahore是其结构,定义如下
    1. struct semaphore {
    2. atomic_t count;//资源数目
    3. int sleepers;//等待进程数目
    4. wait_queue_head_t wait;//等待队列
    5. #if WAITQUEUE_DEBUG
    6. long __magic;
    7. #endif
    8. };
    down操作成功(减后结果非负数)那就在标号1处结束down操作,转到临界区.
    如果减为负数,跳转到2标号,并且调用call_down_failed,进入睡眠,一直要到唤醒并拿到资源才返回跳转到1标号,结束down操作进入临界区
    1. /*
    2. * This is ugly, but we want the default case to fall through.
    3. * "__down_failed" is a special asm handler that calls the C
    4. * routine that actually waits. See arch/i386/kernel/semaphore.c
    5. */
    6. static inline void down(struct semaphore * sem)
    7. {
    8. #if WAITQUEUE_DEBUG
    9. CHECK_MAGIC(sem->__magic);
    10. #endif
    11. __asm__ __volatile__(
    12. "# atomic down operation "
    13. LOCK "decl %0 " /* --sem->count lock字段把总线锁住,防止其他cpu干扰*/
    14. "js 2f " /*如果小于0,那就跳转到2号*/
    15. "1: " /*成功拿到,从此处进入临界区*/
    16. ".section .text.lock,"ax" "
    17. "2: call __down_failed " /*count--后为负值,休眠*/
    18. "jmp 1b "/*失败睡眠,但经过一段时间被唤醒,并且进入临界区,就跳转到1*/
    19. ".previous"
    20. :"=m" (sem->count)
    21. :"c" (sem)
    22. :"memory");
    23. }

    __down_failed源码,这里的目的只是为了调用__down函数
    1. asm(
    2. ".align 4 "
    3. ".globl __down_failed "
    4. "__down_failed: "
    5. "pushl %eax "
    6. "pushl %edx "
    7. "pushl %ecx "
    8. "call __down "
    9. "popl %ecx "
    10. "popl %edx "
    11. "popl %eax "
    12. "ret"
    13. );

    __down将判断资源是否存在,不存在睡眠,如果被唤醒那就从等待队列删除,并且唤醒其他等待队列进程
    1. void __down(struct semaphore * sem)
    2. {
    3. struct task_struct *tsk = current;
    4. DECLARE_WAITQUEUE(wait, tsk);//wait代表tsk
    5. tsk->state = TASK_UNINTERRUPTIBLE;//设置为睡眠状态
    6. add_wait_queue_exclusive(&sem->wait, &wait);//把当前进程的等待队列元素wait加入到sem->wait等待队列队尾
    7. spin_lock_irq(&semaphore_lock);
    8. sem->sleepers++;//等待进程数目+1
    9. for (;;) {
    10. int sleepers = sem->sleepers;//禁止本地中断并获取指定的锁
    11. /*返回非0,表示进程需要等待
    12. * 假设有2个进程,进程资源已经被占用,当前进程执行down失败,跳转到这里,等待调度
    13. 结郭前一个进程归还了资源,count变为0(之前down2次为-1),sleeper-1也为0,相加等于0,于是可以进入临界区
    14. */
    15. if (!atomic_add_negative(sleepers - 1, &sem->count)) {//返回0表示可以进入临界区
    16. sem->sleepers = 0;//睡眠的进程为0,因为要唤醒这进程了
    17. break;
    18. }
    19. sem->sleepers = 1; /* 没法到临界区,那就需要阻塞,执行到这,设置为1,那就只有us - see -1 above */
    20. spin_unlock_irq(&semaphore_lock);
    21. schedule();//调度
    22. tsk->state = TASK_UNINTERRUPTIBLE;//将当前进程设置为睡眠状态
    23. spin_lock_irq(&semaphore_lock);
    24. }
    25. spin_unlock_irq(&semaphore_lock);//解锁
    26. remove_wait_queue(&sem->wait, &wait);//当前进程可以进入临界区后,从wait队列移除
    27. tsk->state = TASK_RUNNING;//设置为可执行状态
    28. wake_up(&sem->wait);//唤醒等待队列(然而等待队列很多进程依旧无法进入临界区)
    29. }
    缺陷:优先级倒转,优先级高进程在外等待,在临界区的进程优先级很低,一旦优先级低的进程在临界区受阻睡眠,也得不到及时调度,优先级高进程会被拖累,解决办法:把高优先级借给进入临界区的进程

    接下来分析up函数
    1. /*
    2. * Note! This is subtle. We jump to wake people up only if
    3. * the semaphore was negative (== somebody was waiting on it).
    4. * The default case (no contention) will result in NO
    5. * jumps for both down() and up().
    6. */
    7. static inline void up(struct semaphore * sem)
    8. {
    9. #if WAITQUEUE_DEBUG
    10. CHECK_MAGIC(sem->__magic);
    11. #endif
    12. __asm__ __volatile__(
    13. "# atomic up operation "
    14. LOCK "incl %0 " /* ++sem->count */
    15. "jle 2f "//如果资源充足(也就是递增结果为正数,直接从1:跳出临界区,不用唤醒阻塞进程(应该说没有阻塞临界区的进程)
    16. "1: "
    17. ".section .text.lock,"ax" "
    18. "2: call __up_wakeup " /*递增结果为负数或者非0值,就唤醒阻塞进程*/
    19. "jmp 1b "
    20. ".previous"
    21. :"=m" (sem->count)
    22. :"c" (sem)
    23. :"memory");
    24. }

    __up_wakup的目的也是调用__up
    1. asm(
    2. ".align 4 "
    3. ".globl __up_wakeup "
    4. "__up_wakeup: "
    5. "pushl %eax "
    6. "pushl %edx "
    7. "pushl %ecx "
    8. "call __up "
    9. "popl %ecx "
    10. "popl %edx "
    11. "popl %eax "
    12. "ret"
    13. );

    1. /*
    2. * Semaphores are implemented using a two-way counter:
    3. * The "count" variable is decremented for each process
    4. * that tries to acquire the semaphore, while the "sleeping"
    5. * variable is a count of such acquires.
    6. *
    7. * Notably, the inline "up()" and "down()" functions can
    8. * efficiently test if they need to do any extra work (up
    9. * needs to do something only if count was negative before
    10. * the increment operation.
    11. *
    12. * "sleeping" and the contention routine ordering is
    13. * protected by the semaphore spinlock.
    14. *
    15. * Note that these functions are only called when there is
    16. * contention on the lock, and as such all this is the
    17. * "non-critical" part of the whole semaphore business. The
    18. * critical part is the inline stuff in <asm/semaphore.h>
    19. * where we want to avoid any extra jumps and calls.
    20. */
    21. /*
    22. * Logic:
    23. * - only on a boundary condition do we need to care. When we go
    24. * from a negative count to a non-negative, we wake people up.
    25. * - when we go from a non-negative count to a negative do we
    26. * (a) synchronize with the "sleeper" count and (b) make sure
    27. * that we're on the wakeup list before we synchronize so that
    28. * we cannot lose wakeup events.
    29. */
    30. void __up(struct semaphore *sem)
    31. {
    32. wake_up(&sem->wait);//唤醒等待队列中的进程
    33. }
    1. #define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,WQ_FLAG_EXCLUSIVE)

    1. void __wake_up(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode)
    2. {
    3.     __wake_up_common(q, mode, wq_mode, 0);
    4. }

    posix信号量与内核信号量概念基本一样,不过posix信号量可以用于位于内核外临界区的不同进程.而内核信号量只可以用于临界区位于内核
    互斥锁(也就是mutex.一般用于线程互斥),不过可以通过设置线程锁属性用于不同进程通信,为了达到多进程共享的需要,互斥锁对象需要创建在共享内存中
    文件锁用于2个进程访问一个文件
    自旋锁,读写锁只可用于线程互斥
    信号量分为匿名信号量与有名信号量,前一个用于线程互斥,后一个用于进程同步
    大内核锁也是用来保护临界区资源,避免出现多个处理器上的进程同时访问同一区域的





  • 相关阅读:
    JAVA中线程同步的方法(4种)汇总
    java
    指定的元素已经是另一个元素的逻辑子元素。请先将其断开连接。(解决问题总结)
    无法将类型为“System.Windows.Controls.SelectedItemCollection”的对象强制转换为类型“System.Collections.Generic.IList`1
    foreach---集合已修改;可能无法执行枚举操作。
    WPF_View中控件使用单例ViewModel
    判断s2是否能够被通过s1做循环移位(rotate)得到的字符串是否包含
    多列转1列 SqlServer 实现oracle10g的 wmsys.wm_concat()--for xml path('')
    异步对象(XMLHttpRequest)的帮助脚本
    在vs2010使用EF出现CS0012: 类型“System.Data.Objects.DataClasses.EntityObject”在未被引用的程序集中定义
  • 原文地址:https://www.cnblogs.com/zengyiwen/p/03e3584939cc3340396fe58f016e46dc.html
Copyright © 2011-2022 走看看