免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1049 | 回复: 0
打印 上一主题 下一主题

Linux 同步方法剖析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-02-06 11:55 |只看该作者 |倒序浏览

http://www.ibm.com/developerworks/cn/linux/l-linux-synchronization.html

学习 Linux® 的过程中,您也许接触过并发(concurrency)、临界段(critical
section)和锁定,但是如何在内核中使用这些概念呢?本文讨论了 2.6 版内核中可用的锁定机制,包括原子运算符(atomic
operator)、自旋锁(spinlock)、读/写锁(reader/writer lock)和内核信号量(kernel
semaphore)。 本文还探讨了每种机制最适合应用到哪些地方,以构建安全高效的内核代码。
本文讨论了 Linux 内核中可用的大量同步或锁定机制。这些机制为 2.6.23 版内核的许多可用方法提供了应用程序接口(API)。但是在深入学习 API 之前,首先需要明白将要解决的问题。
并发和锁定
当存在并发特性时,必须使用同步方法。当在同一时间段出现两个或更多进程并且这些进程彼此交互(例如,共享相同的资源)时,就存在并发 现象。
在单处理器(uniprocessor,UP)主机上可能发生并发,在这种主机中多个线程共享同一个 CPU 并且抢占(preemption)创建竞态条件。抢占 通过临时中断一个线程以执行另一个线程的方式来实现 CPU 共享。竞态条件
生在两个或更多线程操纵一个共享数据项时,其结果取决于执行的时间。在多处理器(MP)计算机中也存在并发,其中每个处理器中共享相同数据的线程同时执
行。注意在 MP 情况下存在真正的并行(parallelism),因为线程是同时执行的。而在 UP
情形中,并行是通过抢占创建的。两种模式中实现并发都较为困难。
Linux 内核在两种模式中都支持并发。内核本身是动态的,而且有许多创建竞态条件的方法。Linux 内核也支持多处理(multiprocessing),称为对称多处理(SMP)。可以在本文后面的
参考资料
部分学到更多关于 SMP 的知识。
临界段概念是为解决竞态条件问题而产生的。一个临界段 是一段不允许多路访问的受保护的代码。这段代码可以操纵共享数据或共享服务(例如硬件外围设备)。临界段操作时坚持互斥锁(mutual exclusion)原则(当一个线程处于临界段中时,其他所有线程都不能进入临界段)。

界段中需要解决的一个问题是死锁条件。考虑两个独立的临界段,各自保护不同的资源。每个资源拥有一个锁,在本例中称为 A 和
B。假设有两个线程需要访问这些资源,线程 X 获取了锁 A,线程 Y 获取了锁
B。当这些锁都被持有时,每个线程都试图占有其他线程当前持有的锁(线程 X 想要锁 B,线程 Y 想要锁
A)。这时候线程就被死锁了,因为它们都持有一个锁而且还想要其他锁。一个简单的解决方案就是总是按相同次序获取锁,从而使其中一个线程得以完成。还需要
其他解决方案检测这种情形。表 1 定义了此处用到的一些重要的并发术语。
表 1. 并发中的重要定义
术语定义竞态条件临界段互斥锁死锁两个或更多线程同时操作资源时将会导致不一致的结果。用于协调对共享资源的访问的代码段。确保对共享资源进行排他访问的软件特性。由两个或更多进程和资源锁导致的一种特殊情形,将会降低进程的工作效率。




回页首
Linux 同步方法

果您了解了一些基本理论并且明白了需要解决的问题,接下来将学习 Linux
支持并发和互斥锁的各种方法。在以前,互斥锁是通过禁用中断来提供的,但是这种形式的锁定效率比较低(现在在内核中仍然存在这种用法)。这种方法也不能进
行扩展,而且不能保证其他处理器上的互斥锁。
在以下关于锁定机制的讨论中,我们首先看一下原子运算符,它可以保护简单变量(计数器和位掩码
(bitmask))。然后介绍简单的自旋锁和读/写锁,它们构成了一个 SMP 架构的忙等待锁(busy-wait
lock)覆盖。最后,我们讨论构建在原子 API 上的内核互斥锁。




回页首
原子操作
Linux 中最简单的同步方法就是原子操作。原子
味着临界段被包含在 API 函数中。不需要额外的锁定,因为 API 函数已经包含了锁定。由于 C 不能实现原子操作,因此 Linux
依靠底层架构来提供这项功能。各种底层架构存在很大差异,因此原子函数的实现方法也各不相同。一些方法完全通过汇编语言来实现,而另一些方法依靠 c
语言并且使用 local_irq_save 和 local_irq_restore 禁用中断。

旧的锁定方法
在内核中实现锁定的一种不太好的方法是通过禁用本地 CPU 的硬中断。这些函数均可用并且仍得到使用(有时用于原子运算符),但我们并不推荐使用。local_irq_save 例程禁用中断,而 local_irq_restore 恢复以前启用过的中断。这些例程都是可重入的(reentrant),也就是说它们可以在其他例程上下文中被调用。
当需要保护的数据非常简单时,例如一个计数器,原子运算符是种理想的方法。尽管原理简单,原子 API 提供了许多针对不同情形的运算符。下面是一个使用此 API 的示例。
要声明一个原子变量(atomic variable),首先声明一个 atomic_t 类型的变量。这个结构包含了单个 int 元素。接下来,需确保您的原子变量使用 ATOMIC_INIT 符号常量进行了初始化。 在清单 1 的情形中,原子计数器被设置为 0。也可以使用atomic_set function 在运行时对原子变量进行初始化。
清单 1. 创建和初始化原子变量
               
atomic_t my_counter ATOMIC_INIT(0);
... or ...
atomic_set( &my_counter, 0 );
原子 API 支持一个涵盖许多用例的富函数集。可以使用 atomic_read 读取原子变量中的内容,也可以使用 atomic_add 为一个变量添加指定值。最常用的操作是使用 atomic_inc 使变量递增。也可用减号运算符,它的作用与相加和递增操作相反。清单 2. 演示了这些函数。
清单 2. 简单的算术原子函数
               
val = atomic_read( &my_counter );
atomic_add( 1, &my_counter );
atomic_inc( &my_counter );
atomic_sub( 1, &my_counter );
atomic_dec( &my_counter );
该 API 也支持许多其他常用用例,包括 operate-and-test 例程。这些例程允许对原子变量进行操纵和测试(作为一个原子操作来执行)。一个叫做 atomic_add_negative 的特殊函数被添加到原子变量中,然后当结果值为负数时返回真(true)。这被内核中一些依赖于架构的信号量函数使用。
许多函数都不返回变量的值,但两个函数除外。它们会返回结果值( atomic_add_return 和 atomic_sub_return),如清单 3所示。
清单 3. Operate-and-test 原子函数
               
if (atomic_sub_and_test( 1, &my_counter )) {
  // my_counter is zero
}
if (atomic_dec_and_test( &my_counter )) {
  // my_counter is zero
}
if (atomic_inc_and_test( &my_counter )) {
  // my_counter is zero
}
if (atomic_add_negative( 1, &my_counter )) {
  // my_counter is less than zero
}
val = atomic_add_return( 1, &my_counter ));
val = atomic_sub_return( 1, &my_counter ));
如果您的架构支持 64 位长类型(BITS_PER_LONG 是 64 的),那么 可以使用 long_t atomic 操作。可以在 linux/include/asm-generic/atomic.h 中查看可用的长操作(long operation)。

子 API 还支持位掩码(bitmask)操作。跟前面提到的算术操作不一样,它只包含设置和清除操作。许多驱动程序使用这些原子操作,特别是
SCSI。位掩码原子操作的使用跟算术操作存在细微的差别,因为其中只有两个可用的操作(设置掩码和清除掩码)。使用这些操作前,需要提供一个值和将要进
行操作的位掩码,如清单 4 所示。
清单 4. 位掩码原子函数
               
unsigned long my_bitmask;
atomic_clear_mask( 0, &my_bitmask );
atomic_set_mask( (1

原子 API 原型
原子操作依赖于架构,可以在 ./linux/include/asm-/atomic.h 中找到。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/90973/showart_1815351.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP