最近这段时间一直忙于AIX下面文件系统的开发,边学边做,有所感悟。今天想总结一下AIX kernel service中提供的一些用于保护共享资源的互斥机制,顺道和Linux下面的spin_lock, mutex_lock等做个比较。

Linux内核中用得最多的是spin_lock及其变体,我们都知道这个lock在无法获取锁时是不会睡眠的,所以可以用在interrupt的上下文中。Linux下还有一个用得广泛的东西叫mutex_lock,其本质是semaphore,在无法获得锁时可能会进入睡眠状态,所以不能用在interrupt上下文中。


AIX下当然也有类似的东西,不过叫法和用法上和Linux内核中都有所不同。依次罗列如下:
1. i_disable
其函数原型为: int i_disable(int new)
大体上,这个函数类似于linux下面的local_irq_disable,该kernel service主要用于对UP系统下的process-interrupt之间共享资源的保护。参数int new是新设定的中断优先级,在/usr/include/sys/intr.h下面定义的中断优先级有:

点击(此处)折叠或打开

  1. #define INTMAX 0 /* all interrupts disabled */
  2. #define INTEPOW INTMAX /* early power off warning */
  3. #define INTCLASS0 1 /* device class 0 */
  4. #define INTCLIST INTCLASS0 /* character list services */
  5. #define INTCLASS1 2 /* device class 1 */
  6. #define INTCLASS2 3 /* device class 2 */
  7. #define INTIOEVENT INTCLASS2 /* HFI IO events, not be INTMAX */
  8. #define INTCLASS3 4 /* device class 3 */
  9. #define INTTIMER 5 /* timer interrupt priority */
  10. #define INTOFFL0 6 /* off-level for class 0 devices */
  11. #define INTOFFL1 7 /* off-level for class 1 devices */
  12. #define INTOFFL2 8 /* off-level for class 2 devices */
  13. #define INTOFFL3 9 /* off-level for class 3 devices */
  14. #define INTOFFLVL INTIODONE /* lowest off-level class */
  15. #define INTIODONE 10 /* block I/O services */
  16. #define INTPAGER INTIODONE /* pager interrupt priority */
  17. #define INTBASE 11 /* everything enabled */
中断优先级从INTMAX (the most favored)到INTBASE (the least favored)依次递减,简单地说,假如调用i_disable(INTCLASS2),那么从包括INTCLASS2开始一直到INTBASE这个范围内的所有中断都会被disable掉。函数返回当前的中断优先级,后续会被i_enable使用来恢复中断优先级 void i_enable(int old)。显然,在MP系统中,对于process-interrupt共享资源的保护,必须借助于某种锁,如同Linux内核中的spin_lock.

2. Simple Locks
这种锁有两种状态:locked和unlocked,其类似与Linux kernel下的spin_lock,但是是exclusive-write,从这点上看,其又相当于linux下的seqlock。不过有意思的是,这种锁在某些情况下可以睡眠,比如如果在process上下文中调用simple_lock,如果该锁的当前拥有者正在运行,那么simple_lock的调用者会先spin,然后如果spin的次数超过系统设定的spin threshold,那么simple_lock的调用者会进入睡眠状态。如果该锁的当前拥有者正在睡眠,那么simple_lock的caller会立即进入睡眠模式。
如果是在interrupt上下文中调用simple_lock,如果该锁的拥有者正在运行,那么simple_lock的caller将会一直spin直到成功获得锁(以防止interrupt上下文中出现进程切换),如果该锁的拥有者正在睡眠,那么这种情况可以认为是系统中出现了一个bug. IBM AIX6.1:Technical Reference:kernel and subsystems, vol 1上写"The simple_lock and siple_lock_try kernel services can be called from the process environment only",综合其他的文档来看,个人认为此处说simple_lock只能用在进程上下文环境中的描述不准确,因为很明显下面要提到的disable_lock就包含了对simple_lock的调用,而IBM的其他文档则明确说disable_lock即可用在process环境也可运行在interrupt环境。此处存疑,综合其他方面的资料看,simple_lock完全可用在interrupt上下文中。
 
所以,AIX下的simple locks在进程上下文环境中其行为非常类似于mutex_lock,而在中断上下文中,其行为又非常类似于spin_lock. 如同Linux下spin_lock的一条使用规则,在用simple lock来保护一个进程和中断都可能访问的临界区时,务必保证临界区中不会出现导致进程休眠的代码,因为假设进程上下文的代码先获得该锁进入临界区,然后进入sleep状态,此时发生了一个中断,中断处理函数试图获得该锁,根据前面所述,因为lock的owner此时处于sleeping状态,中断处理函数将不得不spin直到它的owner释放掉该锁。这对中断处理函数是不可接受的,如果出现这种情况,可以认为是代码设计时出现的bug. 因为simple lock不涉及到中断disable/enable的操作,所以在MP系统上对一个process-interrupt临界区进行保护时,必须加入i_disable代码。为了方便这种情况,AIX系统又引入了下面的互斥函数

3. disable_lock
这种锁类似于Linux下的spin_lock_irq,也就是先i_disable(),然后再simple_lock(). 在UP系统下,disable_lock包含的simple_lock没有意义,此时disable_lock相当与i_disable,如果在MP系统上,simple_lock可以保证在不同处理器上运行的进程能够串行化。这种锁在AIX下主要用在thread-interrupt临界区。
所以如果你的代码要保护一个process-interupt共享的资源,不论是UP还是MP,都应该使用disable_lock和unlock_enable,这个跟Linux下的spin_lock_irq和spin_unlock_irq用法是一样的。
如果你的代码只需要保护process-process之间的共享资源,那么使用simple_lock等就足够了,如同Linux下的spin_lock/spin_unlock.





02-05 18:24