你的位置:电感厂 > 基础知识功率电感

透过 Linux 内核看无锁编程

2015-10-09 20:05:21      点击次数:
上一篇:Yaffs2文件系统中对NAND Flash磨损均衡的改进 贴片电感

Linux 内核中的无锁分析

Linux 内核可能是当今最大最复杂的并行程序之一,它的并行主要来至于中断、内核抢占及 SMP 等。内核设计者们为了不断提高 Linux 内核的效率,从全局着眼,逐步废弃了大内核锁来降低锁的粒度;从细处下手,不断对局部代码进行优化,用无锁编程替代基于锁的方案,如 seqlock 及 RCU 等;不断减少锁冲突程度、降低等待时间,如 Double-checked locking 和原子锁等。

无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,此时就可以利用 Double-checked Locking 模式来减少锁竞争和加锁载荷。目前 Double-checked Locking 已经广泛应用于单例 (Singleton) 模式中。内核设计者基于此思想,巧妙的将 Double-checked Locking 方法运用于内核代码中。

当一个进程已经僵死,即进程处于 TASK_ZOMBIE 状态,如果父进程调用 waitpid() 系统调用时,父进程需要为子进程做一些清理性的工作,代码如下所示:

清单 3. 少锁操作

984 static int wait_task_zombie(task_t *p, int noreap,

985 struct siginfo __user *infop,

986 int __user *stat_addr, struct rusage __user *ru)

987 {

……

1103 if (p->real_parent != p->parent) {

1104 write_lock_irq(&tasklist_lock);

1105 /* Double-check with lock held. */

1106 if (p->real_parent != p->parent) {

1107 __ptrace_unlink(p);

1108 // TODO: is this safe?

1109 p->exit_state = EXIT_ZOMBIE;

……

1120 }

1121 write_unlock_irq(&tasklist_lock);

1122 }

……

1127 }

如果将 write_lock_irq 放置于 1103 行之前,锁的范围过大,锁的负载也会加重,影响效率;如果将加锁的代码放到判断里面,且没有 1106 行的代码,程序会正确吗?在单核情况下是正确的,但在双核情况下问题就出现了。一个非主进程在一个 CPU 上运行,正准备调用 exit 退出,此时主进程在另外一个 CPU 上运行,在子进程调用 release_task 函数之前调用上述代码。子进程在 exit_notify 函数中,先持有读写锁 tasklist_lock,调用 forget_original_parent。主进程运行到 1104 处,由于此时子进程先持有该锁,所以父进程只好等待。在 forget_original_parent 函数中,如果该子进程还有子进程,则会调用 reparent_thread(),将执行 p->parent = p->real_parent; 语句,导致两者相等,等非主进程释放读写锁 tasklist_lock 时,另外一个 CPU 上的主进程被唤醒,一旦开始执行,继续运行将会导致 bug。

严格的说,Double-checked locking 不属于无锁编程的范畴,但由原来的每次加锁访问到大多数情况下无须加锁,就是一个巨大的进步。同时从这里也可以看出一点端倪,内核开发者为了降低锁冲突率,减少等待时间,提高运行效率,一直在持续不断的进行改进。

原子操作可以保证指令以原子的方式执行——执行过程不被打断。内核提供了两组原子操作接口:一组针对于整数进行操作,另外一组针对于单独的位进行操作。内核中的原子操作通常是内联函数,一般是通过内嵌汇编指令来完成。对于一些简单的需求,例如全局统计、引用计数等等,可以归结为是对整数的原子计算。

1. Lock-free 应用场景一 —— Spin Lock

Spin Lock 是一种轻量级的同步方法,一种非阻塞锁。当 lock 操作被阻塞时,并不是把自己挂到一个等待队列,而是死循环 CPU 空转等待其他线程释放锁。 Spin lock 锁实现代码如下:

清单 4. spin lock 实现代码

static inline void __preempt_spin_lock(spinlock_t *lock)

{

……

do {

preempt_enable();

while (spin_is_locked(lock))

cpu_relax();

preempt_disable();

} while (!_raw_spin_trylock(lock));

}

static inline int _raw_spin_trylock(spinlock_t *lock)

{

char oldval;

__asm__ __volatile__(

"xchgb %b0,%1"

:"=q" (oldval), "=m" (lock->lock)

:"0" (0) : "memory");

return oldval > 0;

}

汇编语言指令 xchgb 原子性的交换 8 位 oldval( 存 0) 和 lock->lock 的值,如果 oldval 为 1(lock 初始值为 1),则获取锁成功,反之,则继续循环,接着 relax 休息一会儿,然后继续周而复始,直到成功。

对于应用程序来说,希望任何时候都能获取到锁,也就是期望 lock->lock 为 1,那么用 CAS 原语来描述 _raw_spin_trylock(lock) 就是 CAS(lock->lock,1,0);大电流电感

  • FPGA 技术在视频处理领域的应用 视频处理综述视频处理是目前多媒体领域最热门的技术,主要分为视频编解码和目标信息识别两大类。前者为了节省视频数据的传输带宽,主要依靠传统的信息论理论,目前已经比较成熟;后者则为了提取用户信息,是了人工

  • 基于DSP通讯全桥开关电源的研究与设计 摘要:针对传统开关电源中损耗较大,超调量较大,动态性能较差等问题,提出了基于DSP的全桥软开关技术。通过Matlab仿真结果表明模糊自适应PID控制算法比传统PID控制算法在超调量

  • 电感和电感线圈的原理电感是电子电路阻止电流改变的一种性质。注意“改变”一词的物理意义,这点非常重要,有点像力学中的惯性。一个电感线圈被用在磁场中储存能量,你会发现这个现象非常重要。为了理

  • 请教如何粗略计算纽扣电池供电使用时间
  • 保持电源/负载电路组合稳定的推荐方案序列
  • 飞兆半导体集成式智能功率级模块具有更高的功率
  • uc3842问题请教
  • 英飞凌F3系列IC设计小功率辅助电源图文详述
  • 家庭自动化无线通信解决方案
  • 一种线型组网的三线制数据测量方法
  • [DCDC]OC2004开关降压型DC-DC,GPS,电动车,电源类
  • 汽车自动变速器电控单元设计
  • 一种多路输出隔离驱动电路及其在短路限流器中的