Linux 2.6 kernel的源碼茂契,下面結(jié)合代碼來分析一下在X86體系結(jié)構(gòu)下,互斥鎖的實(shí)現(xiàn)原理。
代碼分析:
1. 首先介紹一下互斥鎖所使用的數(shù)據(jù)結(jié)構(gòu):
struct mutex {
atomic_t count; //引用計數(shù)器,1: 所可以利用,小于等于0:該鎖已被獲取惭等,需要等待
spinlock_t wait_lock;//自旋鎖類型,保證多cpu下办铡,對等待隊列訪問是安全的辞做。
spinlock_t wait_lock; //等待隊列,如果該鎖被獲取寡具,任務(wù)將掛在此隊列上秤茅,等待調(diào)度。
struct list_head wait_list;
};
2. 互斥鎖加鎖函數(shù)
void inline __sched mutex_lock(struct mutex *lock)
調(diào)用了宏:
__mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);
宏的定義:將mutex數(shù)據(jù)結(jié)構(gòu)中童叠,引用計數(shù)器減1框喳,如果不為負(fù)數(shù)就返回,如果為負(fù)數(shù)厦坛,需要調(diào)用函數(shù):__mutex_lock_slowpath五垮,接下來我們再來分析這個函數(shù),我們先來分析一下這個宏杜秸。
#define __mutex_fastpath_lock(count, fail_fn) /
do { /
unsigned int dummy; /
//檢查參數(shù)類型的有效性
typecheck(atomic_t *, count); /
typecheck_fn(void (*)(atomic_t *), fail_fn); /
//輸入放仗,輸出寄存器為eax,輸入為count,輸出為dummy,僅將eax的值減1
asm volatile(LOCK_PREFIX " decl (%%eax)/n" /
" jns 1f /n" /
//如果減后為負(fù)數(shù),調(diào)用回調(diào)函數(shù)撬碟,嘗試阻塞該進(jìn)程
" call " #fail_fn "/n" /
"1:/n" /
: "=a" (dummy) /
: "a" (count) /
: "memory", "ecx", "edx"); /
} while (0)
3. 回調(diào)函數(shù)
static noinline int __sched __mutex_lock_killable_slowpath(atomic_t *lock_count)
{
//通過結(jié)構(gòu)的成員地址诞挨,獲取該結(jié)構(gòu)地址
struct mutex *lock = container_of(lock_count, struct mutex, count);
//該函數(shù)在后面做詳細(xì)介紹
return __mutex_lock_common(lock, TASK_KILLABLE, 0, _RET_IP_);
}
4. 阻塞進(jìn)程真正獲取鎖的地方
static inline int __sched
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,unsigned long ip)
{
//獲取當(dāng)前進(jìn)程的task_struct的地址
struct task_struct *task = current;
struct mutex_waiter waiter;
unsigned int old_val;
unsigned long flags;
//對該鎖上的等待隊列加自旋鎖,防止多個CPU的情況呢蛤。
spin_lock_mutex(&lock->wait_lock, flags);
//將該任務(wù)添加到該鎖的等待隊列上
list_add_tail(&waiter.list, &lock->wait_list);
waiter.task = task;
//用一條匯編指令對count進(jìn)行付值惶傻,lock->count=-1,保證該操作在一個cpu上是原子的
old_val = atomic_xchg(&lock->count, -1);
//如果lock->count之前的值為1,說明是可以獲取鎖的
if (old_val == 1)
goto done;
lock_contended(&lock->dep_map, ip);
for (;;) {
//在這個地方其障,又嘗試去獲取鎖达罗,處理方式如上。
old_val = atomic_xchg(&lock->count, -1);
if (old_val == 1)
break;
//如果該進(jìn)程是可中斷的静秆,或者該進(jìn)程是可kiilable的粮揉,如果有信號被遞送到該任務(wù),那么該進(jìn)程將從等待隊列中移除
if (unlikely((state == TASK_INTERRUPTIBLE &&signal_pending(task)) ||(state == TASK_KILLABLE &&fatal_signal_pending(task)))) {
mutex_remove_waiter(lock, &waiter,task_thread_info(task));
mutex_release(&lock->dep_map, 1, ip);
spin_unlock_mutex(&lock->wait_lock, flags);
debug_mutex_free_waiter(&waiter);
//返回被信號中斷
return -EINTR;
}
__set_task_state(task, state);
//如果還不能獲取所抚笔,則將自旋鎖解除扶认,當(dāng)從schedule返回時再次獲取自旋鎖,重復(fù)如上操作殊橙。
spin_unlock_mutex(&lock->wait_lock, flags);
schedule();
spin_lock_mutex(&lock->wait_lock, flags);
}
//表示已經(jīng)獲取了鎖
done:
lock_acquired(&lock->dep_map);
//將該任務(wù)從等待隊列中刪除
mutex_remove_waiter(lock, &waiter, task_thread_info(task));
debug_mutex_set_owner(lock, task_thread_info(task));
//如果等待隊列為空將lock->count置為0
if (likely(list_empty(&lock->wait_list)))
atomic_set(&lock->count, 0);
spin_unlock_mutex(&lock->wait_lock, flags);
debug_mutex_free_waiter(&waiter);
return 0;
}
5. 解鎖過程
void __sched mutex_unlock(struct mutex *lock)
{
//解鎖后lock->count將從0變?yōu)?
__mutex_fastpath_unlock(&lock->count,__mutex_unlock_slowpath);
}
//該宏是對引用計數(shù)器實(shí)行加1操作辐宾,如果加后小于等于0狱从,說明該等待隊列上還有任務(wù)需要獲取鎖。調(diào)用__mutex_unlock_slowpath函數(shù)叠纹。
#define __mutex_fastpath_unlock(count, fail_fn) /
do { /
unsigned int dummy; /
/
typecheck(atomic_t *, count); /
typecheck_fn(void (*)(atomic_t *), fail_fn); /
/
asm volatile(LOCK_PREFIX " incl (%%eax)/n" /
" jg 1f/n" /
" call " #fail_fn "/n" /
"1:/n" /
: "=a" (dummy) /
: "a" (count) /
: "memory", "ecx", "edx"); /
} while (0)
//該函數(shù)調(diào)用了__mutex_unlock_slowpath函數(shù)季研。
static noinline void
__mutex_unlock_slowpath(atomic_t *lock_count)
{
__mutex_unlock_common_slowpath(lock_count, 1);
}
static inline void
__mutex_unlock_common_slowpath(atomic_t *lock_count, int nested)
{
//通過結(jié)構(gòu)的成員地址,獲取該結(jié)構(gòu)地址
struct mutex *lock = container_of(lock_count, struct mutex, count);
unsigned long flags;
//為等待隊列加自旋鎖
spin_lock_mutex(&lock->wait_lock, flags);
mutex_release(&lock->dep_map, nested, _RET_IP_);
debug_mutex_unlock(lock);
if (__mutex_slowpath_needs_to_unlock())
atomic_set(&lock->count, 1);
//先看看等待隊列是不是為空了誉察,如果已經(jīng)為空与涡,不需要做任何處理,否則將該等待隊列上面的隊首進(jìn)程喚醒
if (!list_empty(&lock->wait_list)) {
struct mutex_waiter *waiter =list_entry(lock->wait_list.next,struct mutex_waiter, list);
debug_mutex_wake_waiter(lock, waiter);
wake_up_process(waiter->task);
}
debug_mutex_clear_owner(lock);
spin_unlock_mutex(&lock->wait_lock, flags);
}
總結(jié):
互斥鎖的實(shí)現(xiàn)持偏,實(shí)際上就是一把鎖維護(hù)了一個等待隊列和一個引用計數(shù)器驼卖,當(dāng)獲取鎖之前,先對引用計數(shù)器減1操作鸿秆,如果為非負(fù)酌畜,則可以獲取鎖進(jìn)入臨界區(qū)。否則需要將該任務(wù)掛在該等待對列上卿叽。
轉(zhuǎn)自:http://blog.csdn.net/tq02h2a/article/details/4317211