4.信号量

Linux中的信号量是一种睡眠锁。如果一个任务试图获得一个已经被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。这时处理器能重获自由,从而去执行其他代码。当持有信号量的进程将信号量释放后,处于等待队列中的那个任务将被唤醒,并获得该信号量。

信号量和自旋锁在使用上的差异:

1)由于争用信号量的过程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会被长时间持有的情况;相反,锁被短时间持有时,使用信号量就不太适宜了。因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁被占用的全部时间还要长。

2)由于执行线程在锁被争用时会睡眠,所以只能在进程上下文中才能获取信号量锁,因为中断上下文中是不能进行调度的。

3)你可以在持有信号量时去睡眠,因为当其他进程试图获得同一信号量时不会因此而死锁(因为该进程也只是去睡眠而已,最终会继续执行的)。

4)在你占用信号量的同时不能占用自旋锁。因为在你等待信号量时可能会睡眠,而在持有自旋锁时是不允许睡眠的。

5)信号量同时允许任意数量的锁持有者,而自旋锁在一个时刻最多允许一个任务持有它。

静态声明信号量

static DECLARE_SEMAPHORE_GENRIC( name, count );

name 是信号量变量名,count 是信号量的使用者数量,创建互斥信号量也可以使用:

static DECLARE_MUTEX( name );

动态声明信号量

sema_init( sem, count );

sem 为结构指针,count 是使用者数量,初始化一个互斥信号量也可以使用:

init_MUTEX( sem );

/* 定义并声明一个信号量,名字为 mr_sem,用于信号量计数 */
static DECLARE_MUTEX( mr_sem );/* 试图获取信号量... */
if ( down_interruptible( &mr_sem ) ) {/* 信号被接收,信号量还未获取 */
}/* 临界区...  *//* 释放给定的信号量 */
up( &mr_sem );

5.读-写信号量

读-写信号量在内核中是由 rw_semaphore 结构表示的,定义在文件<linux/rwsem.h>中,通过以下语句创建读-写信号量。

静态:static DECLARE_RWSEM( name );

动态:init_rwsem( struct rw_semaphore *sem );

所有的读-写信号量都是互斥信号量,只要没有写者,并发持有读锁的读者锁不限。相反,只有惟一的写者(在没有读者时)可以获得写锁。所有读-写锁的睡眠都不会被信号打断,所以它只有一个版本的down操作。

static DECLARE_RWSEM( mr_rwsem );/* 试图获取信号量用于读... */
down_read( &mr_rwsem );/* 临界区(只读)...  *//* 释放信号量  */
up_read( &mr_rwsem );/* 试图获取信号量用于写... */
down_write( &mr_sem );/* 临界区(读和写)... *//* 释放信号量 */
up_write( &mr_sem );

读-写信号量也提供了 down_read_trylock 和 down_write_trylock方法。如果成功获得了信号量锁,它们返回非0值;如果信号量锁被争用,则返回0。要小心——这与普通信号量的情形完全相反。

读-写信号量相比读-写自旋锁多一种特有的操作,downgrade_writer。这个函数可以动态地将获取的写锁转换为读锁。

6.完成变量

如果在内核中一个任务需要发出信号通知另一任务发生了某个特定事件,利用完成变量(completion variable)是使两个任务得以同步的简单方法。如果一个任务要执行一些工作时,另一个任务就会在完成变量上等待。当这个任务完成工作后,会使用完成变量去唤醒在等待的任务。

7.Seq锁

这种锁提供了一种很简单的机制,用于读写共享数据。实现这种锁主要依靠一个序列计数器。当有疑义的数据被写入时,会得到一个锁,并且序列值会增加。在读取数据之前和之后,序列号都被读取。如果读取的序列号值相同,说明在读操作进行的过程中没有被写操作打断过。此外,如果读取的值是偶数,那么就表明写操作没有发生(要明白因为锁的初值是0,所以写锁会使值成奇数,释放的时候变成偶数)。

/* 定义一个seq锁 */
seqlock_t mr_seq_lock = SEQLOCK_UNLOCKED;write_seqlock( &mr_seq_lock );
/* 写锁被获取... */
write_sequnlock( &mr_seq_lock );/* 在读的情况,与自旋锁有很大不同 */
unsigned long seq;do {seq = read_seqbegin( &mr_seq_lock );/* 这里读数据... */
} while ( read_seqretry( &mr_seq_lock, seq ) );

在多个读者和少数写者共享一把锁的时候,seq锁有助于提供一种非常轻量级和具有可扩展性的外观。但是 seq 锁对写者更有利,只要没有其他写者,写锁总是能够被成功获得。挂起的写者会不断地使得读操作循环(前一个例子),直到不再有任何写者持有锁为止。

8.禁止抢占

由于内核是抢占性的,内核中的进程在任何时刻都可能停下来以便另一个具有更高优先权的进程运行。这意味着一个任务与被抢占的任务可能会在同一个临界区内运行。为了避免这种情况,内核抢占代码使用自旋锁作为非抢占区域的标记。如果一个自旋锁被持有,内核便不能进行抢占。

实际中,某些情况并不需要自旋锁,但是仍然需要关闭内核抢占。为了解决这个问题,可以通过 preempt_disable 禁止内核抢占。这是一个可以嵌套调用的函数,可以调用任意次。每次调用都必须有一个相应的 preempt_enable 调用。当最后一次 preempt_enable 被调用后,内核抢占才重新占用。

preempt_disable();
/* 抢占被禁止... */
preempt_enable();

为了用更简洁的方法解决每个处理器上的数据访问问题,可以通过 get_cpu 获得处理器编号,这个函数在返回当前处理器号前首先会关闭内核抢占。

int cpu;/* 禁止内核抢占,并将 cpu 设置为当前处理器 */
cpu = get_cpu();
/* 对每个处理器的数据进行操作... *//* 再给予内核抢占性 */
put_cpu();

9.顺序和屏障

编译器和处理器为了提高效率,可能对读和写重新排序,幸好,所有可能重新排序和写的提供了机器指令来确保顺序要求,同样也可以指示编译器不要对给定点周围的指令序列进行重新排序。这些确保顺序的指令称做屏障(barrier)。

转载于:https://www.cnblogs.com/pennant/archive/2012/12/28/2833383.html

《Linux内核设计与实现》读书笔记(8)--- 内核同步方法(2)相关推荐

  1. Linux内核设计与实现 读书笔记

    第二章 Linux内核 1 内核开发特点 1)内核编译时不能访问C库: 2)浮点数很难使用: 3)内核只有一个定长堆栈: 4)注意同步和并发. 第三章 进程管理 1 current宏:查找当前运行进程 ...

  2. linux+模块与设备关系,linux内核设计与实现读书笔记——设备和模块

    一.设备类型 1.块设备 blkdev:以块为单位寻址,支持重定位(数据随机访问),通过块设备节点来访问. 2.字符设备cdev:不可寻址,提供数据流访问,通过字符设备节点访问. 3.网络设备:对网络 ...

  3. linux内核设计与实现看不懂,Linux内核设计与实现读书笔记

    Unix强大的根本原因: Unix简洁, 提供几百个系统调用, 设计目的明确 Unix中 所有东西都被当做文件对待 Unix内核和相关系统工具是用C语言开发的, 移植能力强大 Unix进程创建迅速, ...

  4. 进程调度(一)--linux内核设计与实现读书笔记

    进程的调度程序是保证进程能有效工作的一个内核子系统.调度程序负责决定将哪个进程投入运行,何时运行以及运行多少时间.简单的来说,调度程序就是在给一堆就绪的进程分配处理器的时间,调度程序是多任务操作系统的 ...

  5. Linux内核设计与实现读书笔记

    1.进程管理 内核通过task_struct管理进程. 2.进程调度 1.可执行队列runqueue. 2.用户抢占.从中断返回或者系统调用返回的时候发生. 3.系统调用 1.系统调用参数放在寄存器里 ...

  6. 《Linux/Unix设计思想》读书笔记与感想

    英文名:Linux and the Unix Philosophy 作者:Mike Gancarz    翻译:漆渀(ben) NIH - Not Invented Here 准则1:小既是美     ...

  7. Linux内核设计与实现学习笔记目录

    **注:**这是别人的笔记,我只是把目录抄过来 <Linux内核设计与实现学习笔记> 1.<Linux内核设计与实现>读书笔记(一)-内核简介 2.<Linux内核设计与 ...

  8. 领域驱动设计DDD之读书笔记

    查看文章   领域驱动设计DDD之读书笔记  转载原地址:http://hi.baidu.com/lijiangzj 2007-08-17 16:53 一.当前Java软件开发中几种认识误区 Hibe ...

  9. Redis 设计与实现 读书笔记(菜鸟版)

    Redis 设计与实现 读书笔记(简略版) 写在前面 第一章(内部数据结构) SDS List Dictionary Rehash Rehash 与 COW 渐进式Rehash 字典收缩 Skipli ...

  10. Linux Shell脚本攻略 读书笔记

    内容目录: 嗨,Echo一下 给终端来点颜色 shell的控制结构 算术比较 目录操作 网站下载 tar 归档工具 rsync 备份系统快照 ftp自动传输 磁盘管理 故障排查 使用syslog记录日 ...

最新文章

  1. 怎么理解ASM中的Failgroup
  2. Javascript到PHP加密通讯的简单实现
  3. oracle 9i 安装及连接远程数据库
  4. 崛起的超级智能:是否会是下一个“麦克卢汉式”预言?
  5. python菜鸟excel教程-Python菜鸟之路: 封装通用excel操作
  6. jQuery源码dom ready分析
  7. 数据结构——HDU1312:Red and Black(DFS)
  8. 分页池内存持续增长_Java技术学习之对虚拟机(JVM)内存模型的分析
  9. Prism for WPF 搭建一个简单的模块化开发框架
  10. 泛型在继承方面的体现与通配符
  11. image 3d view HTML5,Cute Slider - 3D 2D HTML5 Image Slider
  12. 11.乘最多水的容器
  13. -bash:fork:Resource temporarily unavailable
  14. PLC和MCU单片机有什么差异
  15. paip.Log4j配置不起作用的解决
  16. 记一次jenkins 构建go项目经历
  17. 【广告算法工程师入门 32】从直播答题,跳一跳,抢红包等产品策略扯到用户受益商业变现
  18. 网络资产扫描工具 -- Goby
  19. Incompatible types
  20. Data Wrangling

热门文章

  1. apache禁止访问html,apache 限制目录访问
  2. 简述PHP中有哪些运算符,PHP运算符简述
  3. mysql view none,MySQL笔记之视图的使用详解
  4. AI视觉组基于ESP32的裁判系统第一版本设计要求
  5. ATMEGA328实验电路板
  6. Prolific PL2303SA 调试
  7. NS4146 D类音频放大电路
  8. 成绩统计自动计算表格_电气计算太慢?62套自动计算EXCEL表格,一键下载秒出结果...
  9. python事件处理函数_事件驱动的简明讲解(python实现)
  10. php约瑟夫问题,php 解决约瑟夫问题实现方法