【进程管理】内核中的互斥操作

本文探讨了Linux内核中对共享资源的互斥访问机制,通过信号量和自旋锁来防范进程间的干扰。重点介绍了信号量的使用、自旋锁的原理及其在多处理器系统中的应用,并讨论了防止死锁的方法。此外,文章还阐述了互斥不仅局限于进程与进程之间,还包括了进程与中断服务程序之间的冲突,以及不同处理器上运行的进程间的干扰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核中的很多的操作在进行过程中都不允许受打扰的,比如队列操作,如果两个进程都要将一个数据结构链入到同一队列中的尾部,要是在一个进程完成了一半的时候发生了调度,让第二个进程差了进来,结果就可能乱了;类似的干扰也有可能来自某个中断服务程序或bh函数;在多处理器SMP结构中,这种干扰有可能来自另一个处理器;

不过除了一个进程主动调用schedule()让出CPU的情况(显然不会发生在不容许受到打扰的过程中途,程序员控制)之外,只有从系统空间返回到用户空间的前夕有可能发生调度;这样的安排使得上述两个进程间的干扰实际上不会发生在内核中;所以两个进程在内核中互相干扰的情况发生在多处理器的系统中,在单处理器中是不会发生的;但是系统中有些资源是共享的,但在具体使用时是需要独占的,而且对这些资源的访问可能会受阻而需要睡眠等待,进程在使用这类资源的,是有可能受到其他进程的干扰;中断服务程序(包括bh函数)的干扰,则总是有可能的;在SMP系统中,不但要防止来自同一处理器上的中断服务程序的干扰,还要防止来自其他处理器的中断服务程序的干扰;


(1)进程间对共享资源的互斥访问,或者说是对进程间的干扰的防范,是通过semaphore这种机制来实现的;内核为此提供了up和down函数,分别对应于操作系统理论中的P和V操作;在结构体semaphore中,count就是信号量中的那个量,代表着可使用资源的数量;wait_queue_head_t是count不能在继续访问的时候,进程等待的队列;sleepers表示有多少个等候进程;比如在安装或卸载文件系统sys_mount()时,通常是要受阻的,因此中途会发生调度的,所以使用mount_sem来保护这个过程,先down,成功才能继续运行,最后要up;

(2)宏定义DECLAR_MUTEX()中,经过一系列的宏替换得到static struct semaphore mount_sem = {{(1)}, 0 , ..}因此此方式信号量的count为1,只能有一个进程可进入临界区;而DECLAR_MUTEX_LOCKED()建立的count为0;

(3)对于down()函数,嵌入汇编的输出部为空,说明执行后并不改变寄存器的内容;而输入部使sem与ECX相结合;,执行count减的过程中会用LOCK将总线锁住,以防止来自同一系统中其他CPU的干扰;计数器不够时,会调用__down_failed()(主要是为了设置相关寄存器的值,进入__down())使进程进入睡眠;

(4)在__down()中,add_wait_queue_exclusive()则把当前进程的等待元素wait链入到由队列头sem->wait代表的等待队列的尾部(先进先出,进程的优先级并没有起作用);在for循环中atmoic_add_negative()(它是第一个值加到第二个值)很关键,若返回非0,表示当前进程仍需等待;返回0时,表示当前进程不需要再等了,可以进入临界区了;其中sleepers的设置很重要;对于高优先级的进程等待临界区的低优先级进程有时会有问题的;

(5)在up()中,递增sem->count,而在递增以后结果为0或负数就要调用_up_wakeup(),然后调用__up();在__up()中,会有wake_up的宏替换;最终调用__wake_up_common()根据相关的参数来进行唤醒;

(6)防止死锁最简单的办法就是,在一个临界区中,保证不进入另一个临界区;进一步,即使在一个临界区中企图进入另一个临界区,但是如果为所有的临界区都排好一个次序,所有的进程都遵循相同的次序,则也不会发生因循环而产生的死锁;目前linux内核中并无防止和化解死锁的措施,也没有进程在一个临界区中不按次序进入一个临界区的措施,因此还是有待改进的;

(7)互斥不仅是在进程与进程之间,进程与中断服务程序;信号量也可用于不同处理器上运行进程之间相互干扰的唯一手段;关中断只能保证统一处理器中进程与中断服务程序间互斥的一种手段,但是它不能防止来自另一个处理器上中断服务程序或进程的干扰;

(8)加锁同样可以用于共享资源的访问过程中,比如spin_unlock_irq();特别是在SMP结构的系统中,由软件实现的各种锁起着不可代替的作用 ;

(9)同组加锁有不同,比如spin_lock_irqsave()和spin_lock_irq()有区别;不同组加锁如spin_lock_irq()和read_lock_irq();其实每一个操作都包含了两个部分,一部分是操作名以local打头的,其作用是关闭或开启本处理器上的中断响应;另一部分是以_lock结尾的,其作用是防止来自其他处理器的干扰;

(10)比如local_irq_disable()和local_irq_disable()都是通过cli指令关闭中断的,前者还会把状态标志寄存器的内容保存起来;因为状态标志寄存器不是通用寄存器,所以要用push和pop指令经过堆栈将其内容保存到参数x中;

(11)对于spin_lock(),使用一个lock->lock来测试,若是0我、或负数,处理器会不断的循环测试它,用在非进程的上下文中,中断服务程序或bh函数(不可调度),所以加锁的时间不能太长;自旋的过程CPU仍可以响应中断;

(12)自旋锁和上述信号量一样,也要考虑到死锁的情况;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值