linux内核情景分析之内核中的互斥操作
信号量机制:
struct semaphore {atomic_t count;//资源数目int sleepers;//等待进程数目wait_queue_head_t wait;//等待队列#if WAITQUEUE_DEBUGlong __magic;#endif};
/** 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_DEBUGCHECK_MAGIC(sem->__magic);#endif__asm__ __volatile__("# atomic down operation\n\t"LOCK "decl %0\n\t" /* --sem->count lock字段把总线锁住,防止其他cpu干扰*/"js 2f\n" /*如果小于0,那就跳转到2号*/"1:\n" /*成功拿到,从此处进入临界区*/".section .text.lock,\"ax\"\n""2:\tcall __down_failed\n\t" /*count--后为负值,休眠*/"jmp 1b\n"/*失败睡眠,但经过一段时间被唤醒,并且进入临界区,就跳转到1*/".previous":"=m" (sem->count):"c" (sem):"memory");}
asm(".align 4\n"".globl __down_failed\n""__down_failed:\n\t""pushl %eax\n\t""pushl %edx\n\t""pushl %ecx\n\t""call __down\n\t""popl %ecx\n\t""popl %edx\n\t""popl %eax\n\t""ret");
void __down(struct semaphore * sem){struct task_struct *tsk = current;DECLARE_WAITQUEUE(wait, tsk);//wait代表tsktsk->state = TASK_UNINTERRUPTIBLE;//设置为睡眠状态add_wait_queue_exclusive(&sem->wait, &wait);//把当前进程的等待队列元素wait加入到sem->wait等待队列队尾spin_lock_irq(&semaphore_lock);sem->sleepers++;//等待进程数目+1for (;;) {int sleepers = sem->sleepers;//禁止本地中断并获取指定的锁/*返回非0,表示进程需要等待* 假设有2个进程,进程资源已经被占用,当前进程执行down失败,跳转到这里,等待调度结郭前一个进程归还了资源,count变为0(之前down2次为-1),sleeper-1也为0,相加等于0,于是可以进入临界区*/if (!atomic_add_negative(sleepers - 1, &sem->count)) {//返回0表示可以进入临界区sem->sleepers = 0;//睡眠的进程为0,因为要唤醒这进程了break;}sem->sleepers = 1; /* 没法到临界区,那就需要阻塞,执行到这,设置为1,那就只有us - see -1 above */spin_unlock_irq(&semaphore_lock);schedule();//调度tsk->state = TASK_UNINTERRUPTIBLE;//将当前进程设置为睡眠状态spin_lock_irq(&semaphore_lock);}spin_unlock_irq(&semaphore_lock);//解锁remove_wait_queue(&sem->wait, &wait);//当前进程可以进入临界区后,从wait队列移除tsk->state = TASK_RUNNING;//设置为可执行状态wake_up(&sem->wait);//唤醒等待队列(然而等待队列很多进程依旧无法进入临界区)}
/** Note! This is subtle. We jump to wake people up only if* the semaphore was negative (== somebody was waiting on it).* The default case (no contention) will result in NO* jumps for both down() and up().*/static inline void up(struct semaphore * sem){#if WAITQUEUE_DEBUGCHECK_MAGIC(sem->__magic);#endif__asm__ __volatile__("# atomic up operation\n\t"LOCK "incl %0\n\t" /* ++sem->count */"jle 2f\n"//如果资源充足(也就是递增结果为正数,直接从1:跳出临界区,不用唤醒阻塞进程(应该说没有阻塞临界区的进程)"1:\n"".section .text.lock,\"ax\"\n""2:\tcall __up_wakeup\n\t" /*递增结果为负数或者非0值,就唤醒阻塞进程*/"jmp 1b\n"".previous":"=m" (sem->count):"c" (sem):"memory");}
asm(".align 4\n"".globl __up_wakeup\n""__up_wakeup:\n\t""pushl %eax\n\t""pushl %edx\n\t""pushl %ecx\n\t""call __up\n\t""popl %ecx\n\t""popl %edx\n\t""popl %eax\n\t""ret");
/** Semaphores are implemented using a two-way counter:* The "count" variable is decremented for each process* that tries to acquire the semaphore, while the "sleeping"* variable is a count of such acquires.** Notably, the inline "up()" and "down()" functions can* efficiently test if they need to do any extra work (up* needs to do something only if count was negative before* the increment operation.** "sleeping" and the contention routine ordering is* protected by the semaphore spinlock.** Note that these functions are only called when there is* contention on the lock, and as such all this is the* "non-critical" part of the whole semaphore business. The* critical part is the inline stuff in <asm/semaphore.h>* where we want to avoid any extra jumps and calls.*//** Logic:* - only on a boundary condition do we need to care. When we go* from a negative count to a non-negative, we wake people up.* - when we go from a non-negative count to a negative do we* (a) synchronize with the "sleeper" count and (b) make sure* that we're on the wakeup list before we synchronize so that* we cannot lose wakeup events.*/void __up(struct semaphore *sem){wake_up(&sem->wait);//唤醒等待队列中的进程}
#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,WQ_FLAG_EXCLUSIVE)
void __wake_up(wait_queue_head_t *q, unsigned int mode, unsigned int wq_mode){__wake_up_common(q, mode, wq_mode, 0);}
linux内核情景分析之内核中的互斥操作的更多相关文章
- 几个常用内核函数(《Windows内核情景分析》)
参考:<Windows内核情景分析> 0x01 ObReferenceObjectByHandle 这个函数从句柄得到对应的内核对象,并递增其引用计数. NTSTATUS ObRefer ...
- [1]windows 内核情景分析---说明
本文说明:这一系列文章(笔记)是在看雪里面下载word文档,现转帖出来,希望更多的人能看到并分享,感谢原作者的分享精神. 说明 本文结合<Windows内核情景分析>(毛德操著).< ...
- windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数
windows内核情景分析之—— KeRaiseIrql函数与KeLowerIrql()函数 1.KeRaiseIrql函数 这个 KeRaiseIrql() 只是简单地调用 hal 模块的 KfRa ...
- [2]windows内核情景分析--系统调用
Windows的地址空间分用户模式与内核模式,低2GB的部分叫用户模式,高2G的部分叫内核模式,位于用户空间的代码不能访问内核空间,位于内核空间的代码却可以访问用户空间 一个线程的运行状态分内核态与用 ...
- [7] Windows内核情景分析---线程同步
基于同步对象的等待.唤醒机制: 一个线程可以等待一个对象或多个对象而进入等待状态(也叫睡眠状态),另一个线程可以触发那个等待对象,唤醒在那个对象上等待的所有线程. 一个线程可以等待一个对象或多个对象, ...
- [4]Windows内核情景分析---内核对象
写过Windows应用程序的朋友都常常听说"内核对象"."句柄"等术语却无从得知他们的内核实现到底是怎样的, 本篇文章就揭开这些技术的神秘面纱. 常见的内核对象 ...
- [12]Windows内核情景分析 --- MDI
Mdl意为'内存映射描述符'.'缓冲描述符',一个mdl就代表一个缓冲.(任意一块物理内存,可以同时映射到用户地址空间和系统地址空间的) 设备IO方式分为三种:缓冲方式.直接IO方式.直接方式 缓冲方 ...
- [3]windows内核情景分析--内存管理
32位系统中有4GB的虚拟地址空间 每个进程有一个地址空间,共4GB,(具体分为低2GB的用户地址空间+高2GB的内核地址空间) 各个进程的用户地址空间不同,属于各进程专有,内核地址空间部分则几乎完全 ...
- c# 分析SQL语句中的表操作
最近写了很多方向的总结和demo.基本包含了工作中的很多方面,毕竟c#已经高度封装并且提供了很多类库.前面已经总结了博文.最近2天突然感觉前面的SQL分析阻组件的确麻烦,也注意看了下.为了方便大家学习 ...
随机推荐
- Vuejs中关于computed、methods、watch的区别。
Vue.js在模板表达式中限制了,绑定表达式最多只能有一条表达式,但某些数据需要一条以上的表达式运算实现,此时就可以将此数据放在计算属性(computed)当中. Vuejs中关于computed.m ...
- Gym - 101908G Gasoline 二分+最大流
G - Gasoline Gym - 101908G 题意:给出R个提供点,P个接收点,每个接收点都要接收满,还有一个运输的时间,问最小时间能够完成所有的运输 题解:首先每次都必须要满流,所以我们只要 ...
- (转) Redis哨兵的详解
1 哨兵的作用 哨兵是redis集群架构中非常重要的一个组件,主要功能如下: 1. 集群监控:负责监控redis master和slave进程是否正常工作 2. 消息通知:如果某个redis实例有故障 ...
- HOJ_14001 Just Terraffic!
题意相对来说比较扭曲..所以来说下模型,具体做法有兴趣的孩纸去问度娘或者波塞冬吧~~ 给出一个序列长度,并且输入该序列,该序列的含义是横坐标: 任何两个相邻坐标绝对值小于等于1000的必然为一个整体, ...
- 「微信小程序免费辅导教程」25,基本内容组件text的使用及个人帐号允许的服务类目
- MySQL之索引(三)
聚簇索引 聚簇索引并不是一种单独的索引类型,而是一种数据存储方式.具体的细节依赖于其实现方式,但InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行.当表有聚簇索引时,它的数据行实 ...
- 计算时间复杂度&空间复杂度
1.下面函数的复杂度是: long foo(long x){ if(x<2) return 1; return x*x*foo(x-1); } 解析: 当n>=2时 foo(n)=n^2* ...
- ffmpeg转换参数和对几种视频格式的转换分析
我们在将多种格式的视频转换成flv格式的时候,我们关注的就是转换后的flv视频的品质和大小.下面就自己的实践所得来和大家分享一下,主要针对avi.3gp.mp4和wmv四种格式来进行分析.通常在使用f ...
- Java中为什么字段不能被重写
官方说法: 在一个类中,一个具有相同名称的字段隐藏了父类的父类的领域,即使他们的类型是不同的.在子类中,父类中的字段是不能用简单的名称引用.相反,该字段必须通过超级访问.一般来说,我们不建议隐藏字段, ...
- [网站公告]又拍云API故障造成图片无法上传(已恢复)
大家好,18:00左右开始,又拍云API出现故障,调用图片上传API时出现错误:“The remote server returned an error: (403) Forbidden.”,造成图片 ...