首页 理论教育深入剖析Linux内核与设备驱动:数据保护一致性操作

深入剖析Linux内核与设备驱动:数据保护一致性操作

【摘要】:在内核提供的基本服务中已经简单介绍了一些锁的功能,这里对与驱动相关的数据一致性保护服务进行进一步介绍。还有各种RCU、读写锁等,通常都是针对网络协议栈、文件系统等有一定读写特点的数据一致性保护操作,在驱动中使用的情况并不多,就不进行详述了。总之对数据一致性保护操作,是要在正确的前提下,尽量根据数据操作的上下文以及特点选择合适的接口进行操作,在完成功能的基础上尽力提升性能。

在内核提供的基本服务中已经简单介绍了一些锁的功能,这里对与驱动相关的数据一致性保护服务进行进一步介绍。

1ᤫ顺序和屏障

驱动经常要涉及很多读写操作,设备的读写操作很多是要有顺序性的,对编译器和处理器来说并不能在之前就进行这种顺序性的假设,而是尽力优化代码,相应的驱动就需要保证这种顺序性,这是矛盾的,不过还好编译器和处理器都提供了相关功能来保证操作的顺序性。Linux内核对编译器和处理器的相关指令和操作进行了封装,提供给其他模块使用,相关的操作就是形成一个屏障,屏障保证之前和之后的某些操作的顺序性,下面是相关的接口及说明:

●rmb():阻止跨越屏障的读操作发生重排序,保证前后读操作的顺序性。

●read_barrier_depends():阻止跨越屏障的具有数据依赖关系的读操作重排序。

●wmb():阻止跨越屏障的写操作发生重排序。

●mb():阻止跨越屏障的读和写操作重新排序。

●smp_rmb():在SMP上提供rmb()功能,在UP上提供barrier()功能。

●smp_read_barrier_depends():在SMP上提供read_barrier_depends()功能,在UP上提供barrier()功能。

●smp_wmb():在SMP上提供wmb()功能,在UP上提供barrier()功能。

●smp_mb():在SMP上提供mb()功能,在UP上提供barrier()功能。

●barrier():阻止编译器跨越屏障对读或写操作进行优化。(www.chuimin.cn)

2ᤫ内核抢占

Linux内核提供了内核抢占之后在一定程度上也会造成数据的不一致,比如说进程的切换造成数据访问的交叉执行,这样只在进程上下文的数据操作可以考虑通过内核抢占的接口来保证数据一致性,接口说明如下:

●preempt_disable():增加抢占计数值,从而禁止内核抢占。

●preempt_enable():减少抢占计算,并当该值降为0时检查和执行被挂起的需调度的任务。

●preempt_enable_no_resched():激活内核抢占但不再检查任何被挂起的需调度的任务。

●preempt_count():返回抢占计数。

这里的preempt_disable()和preempt_enable()是可以嵌套调用的,disable和enable的次数最终应该是一样的。

另外在中断上下文和进程上下文中需要的数据一致性保护可以通过中断开关解决,多处理器操作的数据一致性保护可以通过自旋锁解决,在实际的应用中自旋锁的接口提供了相关中断的操作,所以通常都直接使用正确的自旋锁接口进行相关的保护操作。还有原子操作和互斥锁也是数据一致性保护操作,原子操作主要针对单变量的数据,没有上下文的限制,而互斥锁通常是在多个进程上下文中进行数据一致性的保护。这些接口都在内核基本服务中有简单介绍,这里就不重复说明了。

还有各种RCU(主要差别在如何进行回写上)、读写锁等,通常都是针对网络协议栈、文件系统等有一定读写特点的数据一致性保护操作,在驱动中使用的情况并不多,就不进行详述了。

总之对数据一致性保护操作,是要在正确的前提下,尽量根据数据操作的上下文以及特点选择合适的接口进行操作,在完成功能的基础上尽力提升性能。