linux 2.6 互斥锁的实现-源码分析
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 互斥锁的实现-源码分析的更多相关文章
- Java高并发之无锁与Atomic源码分析
目录 CAS原理 AtomicInteger Unsafe AtomicReference AtomicStampedReference AtomicIntegerArray AtomicIntege ...
- java中的锁之AbstractQueuedSynchronizer源码分析(一)
一.AbstractQueuedSynchronizer类介绍. 该抽象类有两个内部类,分别是静态不可继承的Node类和公有的ConditionObject类.AbstractQueuedSynchr ...
- java中的锁之AbstractQueuedSynchronizer源码分析(二)
一.成员变量. 1.目录. 2.state.该变量标记为volatile,说明该变量是对所有线程可见的.作用在于每个线程改变该值,都会马上让其他线程可见,在CAS(可见锁概念与锁优化)的时候是必不可少 ...
- Java锁及AbstractQueuedSynchronizer源码分析
一,Lock 二,关于锁的几个概念 三,ReentrantLock类图 四,几个重要的类 五,公平锁获取 5.1 lock 5.2 acquire 5.3 tryAcquire 5.3.1 hasQu ...
- Java并发编程之ReentrantLock源码分析
ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...
- 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02
百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...
- concurrent(三)互斥锁ReentrantLock & 源码分析
参考文档:Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock:http://www.cnblogs.com/skywang12345/p/3496101.html Reentr ...
- Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7)
http://blog.chinaunix.net/uid-20543672-id-3157283.html Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3 ...
- linux内存源码分析 - SLAB分配器概述
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 之前说了管理区页框分配器,这里我们简称为页框分配器,在页框分配器中主要是管理物理内存,将物理内存的页框分配给申请 ...
随机推荐
- In-App-Purcharse 官方原文摘要笔记
这并不是一篇关于 In-App-Purcharse 的专业深入分析文章,只是在初次浏览有关IAP官方文档后记录的一些需要注意的地方,就像是课堂笔记. 因为这是原版.并且涉及到支付的内容,所以就不翻译, ...
- md5值计算
1.md5(Message Digest 5th/消息概要加密算法 第5版) REFER: MD5 On wikipedia 2.应用范围 ① 验证下载文件的完整性 ② 3.关于MD5的几个问题 ①只 ...
- Linux 下添加普通用户,登陆并删除
adduser 命令.LINUX创建用户的命令useradd -g test -d /home/test1 -s /etc/bash -m test1注解:-g 所属组 -d 家目录 -s 所用的SH ...
- MAC机中安装RUBY环境
在安装CocoaPods之前要先配置好RUBY环境,本文就怎么安装RUBY的环境进行一总结.安装Ruby环境首先需要安装Xcode然后需要安装Homebrew,接下来需要安装RVM最后安装Ruby环境 ...
- 纯CSS3代码实现简单的图片轮播
以4张图片为例:1.基本布局:将4张图片左浮动横向并排放入一个div容器内,图片设置统一尺寸,div宽度设置4个图片的总尺寸,然后放入相框容器div,相框设置1个图片的大小并设置溢出隐藏,以保证正确显 ...
- 【转】 c++拷贝构造函数(深拷贝,浅拷贝)详解
c++拷贝构造函数(深拷贝,浅拷贝)详解 2013-11-05 20:30:29 分类: C/C++ 原文地址:http://blog.chinaunix.net/uid-28977986-id-3 ...
- php代码加密|PHP源码加密——实现方法
Encipher - PHP代码加密 | PHP源码加密下载地址:https://github.com/uniqid/encipher 该加密程序是用PHP代码写的,加密后代码无需任何附加扩展,无需安 ...
- javascript跨域解决方案
最近遇到了https跨域访问http域的问题,很多朋友理所当然的认为简单,问题是https跨域访问到http域上的资源,会进行相互通信,没有解决该问题,只能把外部资源扔到了新浪的sae上,通过http ...
- dapper extensions (predicates)
https://github.com/tmsmith/Dapper-Extensions/wiki/Predicates The predicate system in Dapper Extensio ...
- Demo学习: FileUpload
FileUpload 文件上传,学习TUniFileUpload控件的使用 TUniFileUpload主要属性: Filter: 文件类型过滤,这个属性在web模式下是无效的,UniGUI目前版本还 ...