linux中double大小,Linux中double_down()函数分析

本文深入分析了Linux 2.4.20内核中的double_down()函数,该函数用于VFS操作中的锁定。通过讨论其可能导致死锁的场景,作者揭示了在双重锁定操作中先获取锁值较小的信号量的重要性,以避免死锁。文中提出了优化方案,即修改double_down()函数的比较条件,以减少死锁的风险。
摘要由CSDN通过智能技术生成

提示:本分析基于linux2.4.20内核代码

double_down()函数用在linux中VFS的一些函数中,例如vfs_rmdir()、vfs_rename_dir()等函数中,用来对两个文件系统对象(指dentry、inode等对象)进行锁定操作,其代码如下:

/*

* Whee.. Deadlock country. Happily there are only two VFS

* operations that does this..

*/

static inline void double_down(struct semaphore *s1, struct semaphore *s2)

{

if (s1 != s2) {

if ((unsigned long) s1 < (unsigned long) s2) {

struct semaphore *tmp = s2;

s2 = s1; s1 = tmp;

}

down(s1);

}

down(s2);

}

两个信号量,该先获得哪个呢?先看看一些相关结构的定义。

semaphore相关结构的定义如下:

struct semaphore {

atomic_t count;

int sleepers;

wait_queue_head_t wait;

#if WAITQUEUE_DEBUG

long __magic;

#endif

};

typedef struct { volatile int counter; } atomic_t;

根据以上两个结构的定义,可以知道double_down()函数中 “if ((unsigned long) s1 < (unsigned long) s2)”语句对两个信号量的counter值进行了无符号比较,选择两个counter中无符号值大的那个信号量先来获得(若得不到,就等待)。

再根据down()的代码(如下所示),如果一个信号量被初始化为1,那么这个信号量在运行中的可能值就是1、0、-1、-2等等。

/*

* This is ugly, but we want the default case to fall through.

* "__down_failed" is a special asm handler that calls the C

* routine that actually waits. See arch/i386/kernel/semaphore.c

*/

static inline void down(struct semaphore * sem)

{

#if WAITQUEUE_DEBUG

CHECK_MAGIC(sem->__magic);

#endif

__asm__ __volatile__(

"# atomic down operation\n\t"

LOCK "decl %0\n\t"     /* --sem->count */

"js 2f\n"  /* JS 符号位为 “1″ 时转移. 负数的符号位为1 */

"1:\n"

LOCK_SECTION_START("")

"2:\tcall __down_failed\n\t"

"jmp 1b\n"

LOCK_SECTION_END

:"=m" (sem->count)

:"c" (sem)

:"memory");

}

设想有下面这样的场景:

1)两个进程a和b

2)两个信号量A和B,并且都初始化为1

a()

{

double_down(A, B);

/* do something */

double_up(A, B);

}

b()

{

double_down(A, B);

/* do something */

double_up(A, B);

}

3)必然死锁的执行线路

【线程a得到信号量A(A减为0)】->【系统调度发生,切换到线程b】->【线程b执行double_down,检测到信号量B的值(为1)较大,于是得到B】->【... ...】

之后无论系统怎么调度,都会出现经典的死锁情景:线程a等待信号量B,而线程b等待信号量A,从而发生死锁。

4)不会死锁的执行线路

【系统的运行使信号量A的值为-1,B的值为0】->【此时,线程a执行double_down,由于-1的无符号值大于0的无符号值,所以线程a试图得到信号量A而不是B,这样得到信号量B的down操作永远不会被执行,从而不会死锁】

5)在3)中,如果在原来的行线路的第三步【线程b执行double_down,检测到信号量B的值(为1)较大,于是得到B】中,把double_down中得到信号量的检测条件改为得到较小的信号量并获取它,则线程b会等待信号量A,而不是把先占有信号量B,就可以避免死锁。

分析以上情景可以得到一个简单的结论:在一组锁上进行多重所操作时,应该尽量先试图去得到锁值(信号量值)最小的那个锁,并以此类推。

根据上面的结论,linux中的double_down()函数代码改为下面所示或许更合理(即去掉if语句中的那两个unsigned)。

static inline void double_down(struct semaphore *s1, struct semaphore *s2)

{

if (s1 != s2) {

if ((long) s1 < (long) s2) {

struct semaphore *tmp = s2;

s2 = s1; s1 = tmp;

}

down(s1);

}

down(s2);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值