linux内核设计与实现 LINUX内核源代码情景分析( 六 )

  • 禁止和激活当前处理器中断:local_irq_disable,local_irq_enable
  • 禁止(屏蔽)指定中断线: disable_irq,disable_irq_nosync,enable_irq,synchronize_irq
  • 获取中断系统状态:asm/system.h中的irqs_disable
  • 判断是否处于中断上下文中:asm/hardirq.h中的in_interrupt
  • 六. 内核同步1. 基本概念
    • 临界区:访问和操作共享数据的代码段
    • 竞争条件:多个执行线程处于同一个临界区
    • 同步:避免并发和防止竞争条件
    • 为什么需要同步:用户程序会被调度程序抢占和重新调度
    • 造成并发的原因有: 中断 内核抢占 睡眠及用户空间的同步 多处理器
    • 同步问题的解决:加锁
    • 什么数据需要加锁 内核全局变量 共享数据
    • 死锁;所有线程都在等待对方释放锁,任何线程都无法继续进行
    2. 内核同步方法2.1 原子操作
    • 原子操作保证指令以原子方式执行
    • 原子操作通常是内联函数,通过内嵌汇编指令完成
    • 原子操作比其他同步方法给系统的开销小
    • linux内核提供对整数和单独对位进行的原子操作
    • 整数原子操作相关函数为asm/atomic.h文件中
    • 位原子操作相关函数为asm/bitops.h
    2.2 自旋锁
    • 自旋锁(spin lock)最多只能被一个可执行线程持有 。如果锁未被占用,线程立刻可以得到 。如果被占用,会一直循环等待锁可用 。
    • 自旋锁可用于防止多个线程同时进入临界区
    • 自旋时特别浪费cpu,所以不应该被长时间持有
    • 接口定义在,具体与体系结构相关的实现在
    • linux中自旋锁不可用递归
    • 自旋锁的使用
    spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
    //普通请求锁
    spin_lock(&mr_lock);
    //禁止中断请求锁
    spin_lock_irqsave(&mr_lock);
    //确保中断是激活的情况可用的方法
    spin_lock_irq(&mr_lock)
    /**临界区**/
    spin_unlock_irq(&mr_lock)
    spin_unlock_irqrestore(&mr_lock);
    spin_unlock(&mr_lock)
    • 自旋锁可以用在中断处理程序中(信号量不行,会导致休眠),在使用锁之前,要禁止本地中断,否则会导致死锁
    2.3 读写自旋锁
    • 锁用途可以明确分为读锁和写锁 。
    • 一个或多个读任务可以并发的持有读者锁
    • 用于写的锁只能被一个写任务持有,且此时不能并发读
    • 读写锁使用
    rwlock_t mr_rwlock = RW_LOCK_UNLOCKED;
    read_lock(&mr_rwlock);
    /**只读临界区**/
    read_unlock(&mr_rwlock)
    write_lock(&mr_rwlock)
    /**读写临界区**/
    write_unlock(&mr_rwlock)
    • 不能把读锁升级为写锁,会导致死锁
    2.4 信号量
    • 信号量是一种睡眠锁
    • 同一时刻允许任意数量的锁持有者
    • 信号量数量为1时,称为二值信号量或互斥信号量
    • 如果一个任务试图获取被占用的信号量,信号量会将其推入等待队列,让其睡眠 。当持有信号量的进程释放后,等待的任务被唤醒,获得信号量
    • 信号量的特点 适合锁会被长时间持有的情况 锁被短时间持有,睡眠耗时可能比全部时间还长 会睡眠,不能在中断中调用 占有信号量时不能占用自旋锁 。自旋锁不允许休眠
    • 信号量支持两个原子操作P和V,荷兰语的探查和增加
    • 信号量相关文件:
    • 使用信号量
    //声明信号量
    static DECLARE_SEMAPHORE_GENERIC(name, count);
    //声明互斥量
    static DECLARE_MUTET(name);
    //以指针方式初始化信号量
    sema_init(sem, count);
    //以指针方式初始化互斥量
    init_MUTET(sem);
    //试图获得信号量
    down_interruptible(&name)
    //释放信号量
    up(&name)
    2.5 读写信号量
    • 与读写锁一样
    • 相关文件:
    2.6 完全变量
    • 提供代替信号量的简单解决方法
    • 相关文件:
    2.7 Seq锁
    • 提供一种简单的机制,用于读写共享数据
    • 内部实现主要一个序列计数器
    • 锁的初始化为0,写数据时,会得到锁,且序列值增加 。读数据之前和之后,序列号被读取,如果相同则没有被打断,如果为偶数则没有写操作发生
    2.8 屏障