linux内核调度 - 图文(3)

2019-04-23 20:18

的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定,最好使用信号量。 自旋锁的基本形式如下: spin_lock(&mr_lock); /*临界区*/

spin_unlock(&mr_lock);

因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好的满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。

自旋锁在内核中有许多变种,如对bottom half 而言,可以使用spin_lock_bh()用来获得特定锁并且关闭半底执行。相反的操作由spin_unlock_bh()来执行;如果临界区的访问逻辑可以被清晰的分为读和写这种模式,那么可以使用读者/写者自旋锁,调用形式为:

读者的代码路径:

read_lock(&mr_rwlock); /*只读临界区*/

read_unlock(&mr_rwlock);

写者的代码路径:

write_lock(&mr_rwlock); /*读写临界区*/

write_unlock(&mr_rwlock);

简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。

死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。 信号量

Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。 信号量基本使用形式为:

static DECLARE_MUTEX(mr_sem);//声明互斥信号量 …

if(down_interruptible(&mr_sem))

/*可被中断的睡眠,当信号来到,睡眠的任务被唤醒 */ /*临界区…*/ up(&mr_sem);

同自旋锁一样,信号量在内核中也有许多变种,比如读者-写者信号量等,这里不再做介绍了。 信号量和自旋锁区别

虽然听起来两者之间使用条件复杂,其实在实际使用中信号量和自旋锁并不易混淆。注意以下原则。

如果代码需要睡眠——这往往是发生在和用户空间同步时——使用信号量是唯一的选择。由于不受睡眠的限制,使用信号量通常来说更加简单一些。如果需要在自旋锁和信号量中作选择,应该取决于锁被持有的时间长短。理想情况是所有的锁都应该尽可能短的被持有,但是如果锁的持有时间较长的话,使用信号量是更好的选择。另外,信号量不同于自旋锁,它不会关闭内核抢占,所以持有自旋锁的代码可以被抢占。这意味者信号量不会对影响调度反应时间带来负面影响。 自旋锁对信号量

―――――――――――――――――――――――――――――――

需求 建议的加锁方法 低开销加锁 优先使用自旋锁

短期琐定 优先使用自旋锁 长期加锁 优先使用信号量 中断上下文中加锁 使用自旋锁 持有锁是需要睡眠、调度 使用信号量

―――――――――――――――――――――――――――――――

引自 《Linux内核开发》

防止并发的方式除了上面提到的外还有很多,我们不详细介绍了。说了这么多希望大家认识到,并发控制在内核编程中是个特别难缠的问题,要驾御它必须清楚的认识到内核中各种任务的调度时机与特点,并且在开发初期就应特别小心保护共享数据(一切共享数据、一切能被别人看到的数据都要注意保护),别等到开发完成才去亡羊补牢。 内核中的调度与同步实例 代码功能介绍

我们希望能在内核中能“制造”一些“冲突”——多种内核任务并发访问共享数据——从而来观察在Linux内核中到底会存在那些潜在的并发危险,有哪些冲突可能造成系统故障,又有哪些措施可以化解冲突。

我们假设存在这样一个的内核共享资源:链表。另外我们构造一个内核多任务访问链表的场景:内核线程向链表加如新节点;内核定时器定时删除接点;系统调用删除销毁链表。

上面三种内核任务并发时,有可能会破坏链表数据的完整性,所以我们会对链表进行同步访问保护,以确保数据一致性。 代码结构体系介绍 内核任务简介和并发关系

内核任务本文中是指处于内核态执行的任务,具体讲包括:“内核线程”、“系统调用”、“硬件中断”、“半底任务”等几类,下面简要介绍几种我们将用到的内核任务。

系统调用:系统调用是用户程序通过门机制来进入内核执行的内核例程,它运行在内核态,处于进程上下文中(进程上下文包括进程的堆栈等等环境),可以认为是代表用户进程的内核任务,因此具有用户态任务的特性。

但它属于内核任务,所以在执行过程中不能被抢占(2.6内核前),只能自己放弃cpu(睡眠)时,系统才能可能重新调度别的任务。

内核线程:内核线程可以理解成在内核中运行的特殊进程,它有自己的“进程上下”(借用调用它的用户进程的上下文),所以同样被进程调度程序调度,也可以睡眠——它和用户进程属性何其相似,不同之处就在于内核线程运行于内核空间,可访问内核数据,运行期间不能抢占。

传统的Unix系统把一些重要的任务委托给周期性执行的进程,这些任务包括刷新磁盘高速缓存,交换出不用的页框,维护网络链接等等。事实上,以严格线性的方式执行这些任务的确效率不高,如果把他们放在后台调度,不管是对它们的函数还是对终端用户进程都能得到较好的


linux内核调度 - 图文(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:Unit 2 Space Invaders课文翻译综合教程四

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: