http://blog.csdn.net/tq02h2a/article/details/4317211

看了看linux 2.6 kernel的源码,下面结合代码来分析一下在X86体系结构下,互斥锁的实现原理。

代码分析

1. 首先介绍一下互斥锁所使用的数据结构:
struct mutex {
 引用计数器
 1: 所可以利用。
 小于等于0:该锁已被获取,需要等待
 atomic_t  count;
 
 自旋锁类型,保证多cpu下,对等待队列访问是安全的。
 spinlock_t  wait_lock;
 
 等待队列,如果该锁被获取,任务将挂在此队列上,等待调度。
 struct list_head wait_list;
};

2. 互斥锁加锁函数
void inline __sched mutex_lock(struct mutex *lock)
调用了宏:
__mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath);

宏的定义:
将mutex数据结构中,引用计数器减1,如果不为负数就返回,
如果为负数,需要调用函数:__mutex_lock_slowpath,接下来我们再来
分析这个函数,我们先来分析一下这个宏。
#define __mutex_fastpath_lock(count, fail_fn)   /
do {        /
 unsigned int dummy;     /
        /
 检查参数类型的有效性
 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"    /
       如果减后为负数,调用回调函数,尝试阻塞该进程
       "   call " #fail_fn "/n"   /
       "1:/n"     /
       : "=a" (dummy)    /
       : "a" (count)    /
       : "memory", "ecx", "edx");   /
} while (0)

3. 回调函数
static noinline int __sched __mutex_lock_killable_slowpath(atomic_t *lock_count)
{
  通过结构的成员地址,获取该结构地址
 struct mutex *lock = container_of(lock_count, struct mutex, count);

该函数在后面做详细介绍
 return __mutex_lock_common(lock, TASK_KILLABLE, 0, _RET_IP_);
}

4. 阻塞进程真正获取锁的地方
static inline int __sched
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
         unsigned long ip)
{
  获取当前进程的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);

将该任务添加到该锁的等待队列上
 list_add_tail(&waiter.list, &lock->wait_list);
 waiter.task = task;
 
 用一条汇编指令对count进行付值,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;

如果该进程是可中断的,或者该进程是可kiilable的,如果有信号
  被递送到该任务,那么该进程将从等待队列中移除
  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);

如果还不能获取所,则将自旋锁解除,当从schedule返回时再次获取自旋锁,
  重复如上操作。
  spin_unlock_mutex(&lock->wait_lock, flags);
  schedule();
  spin_lock_mutex(&lock->wait_lock, flags);
 }

表示已经获取了锁
done:
 lock_acquired(&lock->dep_map);
将该任务从等待队列中删除
 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变为1
 __mutex_fastpath_unlock(&lock->count, __mutex_unlock_slowpath);
}

该宏是对引用计数器实行加1操作,如果加后小于等于0,说明该等待队列
上还有任务需要获取锁。调用__mutex_unlock_slowpath函数。
#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)

该函数调用了__mutex_unlock_slowpath函数。
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)
{
  通过结构的成员地址,获取该结构地址
 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);

先看看等待队列是不是为空了,如果已经为空,不需要做任何处理,否则
 将该等待队列上面的队首进程唤醒
 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);
}

总结:互斥锁的实现,实际上就是一把锁维护了一个等待队列和一个引用计数器,当获取锁
之前,先对引用计数器减1操作,如果为非负,则可以获取锁进入临界区。否则需要将该任务
挂在该等待对列上。

linux 2.6 互斥锁的实现-源码分析的更多相关文章

  1. Java高并发之无锁与Atomic源码分析

    目录 CAS原理 AtomicInteger Unsafe AtomicReference AtomicStampedReference AtomicIntegerArray AtomicIntege ...

  2. java中的锁之AbstractQueuedSynchronizer源码分析(一)

    一.AbstractQueuedSynchronizer类介绍. 该抽象类有两个内部类,分别是静态不可继承的Node类和公有的ConditionObject类.AbstractQueuedSynchr ...

  3. java中的锁之AbstractQueuedSynchronizer源码分析(二)

    一.成员变量. 1.目录. 2.state.该变量标记为volatile,说明该变量是对所有线程可见的.作用在于每个线程改变该值,都会马上让其他线程可见,在CAS(可见锁概念与锁优化)的时候是必不可少 ...

  4. Java锁及AbstractQueuedSynchronizer源码分析

    一,Lock 二,关于锁的几个概念 三,ReentrantLock类图 四,几个重要的类 五,公平锁获取 5.1 lock 5.2 acquire 5.3 tryAcquire 5.3.1 hasQu ...

  5. Java并发编程之ReentrantLock源码分析

    ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...

  6. 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02

    百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...

  7. concurrent(三)互斥锁ReentrantLock & 源码分析

    参考文档:Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock:http://www.cnblogs.com/skywang12345/p/3496101.html Reentr ...

  8. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)

    http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...

  9. linux内存源码分析 - SLAB分配器概述

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 之前说了管理区页框分配器,这里我们简称为页框分配器,在页框分配器中主要是管理物理内存,将物理内存的页框分配给申请 ...

随机推荐

  1. 从0开始学习react(三)

    这次我们来讲解第三节知识,考虑了下,先不去讲什么理论了,毕竟网上一搜一大堆,而且理论真心看不太懂啊!!! 今天我们就直接上实例喽! 大家HIGH起来!!!(想了好久,还是没舍得删这句话) 1.根据下图 ...

  2. 编程语言中的Namespace

    Namespace 1.C struct 2.C++(Pronounced 'see jia-jia' or 'see plus-plus') namespace 3.Python module(s) ...

  3. HP平台由于变量声明冲突导致程序退出时的core

    最近遇到一个莫名的问题,在HP-UX B.11.31 U ia64平台下,程序PetriService在接收到产品化退出或Ctrl-C时,程序在main函数返回后析构全局的CTQueue<SMs ...

  4. Java使用泛型类来提高方法的可重用性

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3832268.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  5. SOCKET编程:为什么recv不阻塞

    服务器端: #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #incl ...

  6. 完美高仿精仿京东商城手机客户端android版源码

    完美高仿精仿京东商城手机客户端android版源码,是从安卓教程网那边转载过来的,这款应用源码非常不错的,也是一个非常优秀的应用源码的,希望能够帮到学习的朋友. _js_op> <igno ...

  7. mysql 触发器,insert,auto字段竟然一样....

    a 表的字段有id,uid,name,其中id是自增值, CREATE TRIGGER trigger_insert_productAFTER INSERT ON aFOR EACH ROWBEGIN ...

  8. 《APUE》第6章练习1

    问题:编写一个程序,它调用uname并输出utsname结构中的所有字段,并将输出与uname(1)命令的输出结构做比较. 我本来以为utsname结构也跟passwd结构一样,也有那三个函数来获取整 ...

  9. Yii框架中集成phprpc、hprose

    在项目开发的过程中有时候会涉及到对外提供接口供第三方程序调用或者是不同程序间需要相互通信,那么最通用的做法是用传统的SOAP方式来实现,用XML的文档格式来作为传输载体.但是这种方式不灵活,支持的数据 ...

  10. Kakfa揭秘 Day8 DirectKafkaStream代码解析

    Kakfa揭秘 Day8 DirectKafkaStream代码解析 今天让我们进入SparkStreaming,看一下其中重要的Kafka模块DirectStream的具体实现. 构造Stream ...