linux同步

来源:互联网 发布:烘焙箱怎么做饼干知乎 编辑:程序博客网 时间:2024/06/06 01:11

如果进程正执行内核函数时,即它在内核态运行时,允许发生内核切换(被替换的进程是正在执行内核函数的进程),这个内核就是可抢占的。

所有进程切换都是由宏switch_to()来完成的。

使内核可抢占的目的是减少用户态进程的分派延迟,即从进程变为可执行状态到它实际开始运行之间的时间间隔。

只有当内核正在执行异常处理程序(或系统调用),而且内核抢占没有被显式禁用,才可能抢占内核。

内核抢占会引起不容忽视的开销。

临界区时一段代码,在其它的内核控制路径能够进入临界区前,进入临界区的内核控制路径必须全部执行完这段代码。

一旦临界区被确定,就必须对其采用适当的保护措施,以确保在任意时刻只有一个内核控制路径处于临界区。

中断处理程序,软中断和tasklet既不可以被抢占也不能被阻塞,所以它们不可能长时间处于挂起状态。

最简单也是最重要的同步技术包括把内核变量声明为每CPU变量,每CPU变量主要是数据结构的数组,系统的每个CPU对应数组的一个元素。

每CPU的数组元素在主存中被排列以使每个数据结构存放在硬件高速缓存的不同行中。

任何一个原子操作都必须以单个指令执行,中间不能中断,且避免其他的CPU访问同一存储单元。

作用于atomic_t的变量的函数和宏当作单独的,原子的汇编语言指令来使用。

优化屏障原语保证编译程序不会混淆放在原语操作之前的汇编语言指令和放在原语操作之后的汇编语言指令。在linux中,优化屏障就是barrier()宏,它展开为asm volatile(""":::"memory")。指令asm告诉编译程序要插入汇编语言片段。volatile关键字禁止编译器把asm指令与程序中的其他指令重新组合。memory关键字强制编译器假定RAM中的所有内存单元已经被汇编语言修改。因此,编译器不能使用存放在CPU寄存器中内存单元的值来优化asm指令前的代码。

内存屏障原语确保,在原语之后的操作开始执行之前,原语之前的操作已经完成。内存屏障原语的实现依赖于系统的体系结构。

自旋锁的循环指令表示忙等,即使等待的内核控制路径无视可做,它也在CPU上保持运行。一般来说,由自旋锁所保护的每个临界区都是禁止内核抢占的。

读写自旋锁的引入是为了增加内核的并发能力。

当使用读写自旋锁时,内核控制路径发出的执行read_lock或write_lock操作的请求具有相同的优先权:读者必须等待,直到写操作完成。同样的,写者也必须等待,直到堵操作完成。

顺序锁与读写锁非常相似,只是它为写者赋予了较高的优先级。当读者进入临界区时,不必禁用内核抢占,另一方面,由于写者获取自旋锁,所以它进入临界区时自动禁止内核抢占。

读-拷贝-更新(RCU)是为了保护在多数情况下被多个CPU读的数据结构儿设计的另一种同步技术。RCU允许多个读者和写者并发执行,而且RCU是不使用锁的。

1.RCU只保护被动态分配并通过指针引用的数据结构 2在被RCU保护的临界区,任何内核控制路径都不能睡眠。

事实上,当写者要更新数据结构时,它间接引用指针并生成整个数据结构的副本,接下来,写者修改这个副本。一旦修改完毕,写者改变指向数据结构的指针。

RCU主要用在网络层和虚拟文件系统中。

当内核控制路径试图获取内核信号量所保护的忙资源时,相应的进程被挂起。只有在资源被释放时,进程才再次变为可运行的。因此只有可以睡眠的函数才能获取内核信号量,中断处理程序和可延迟函数都不能使用内核信号量。

当进程希望释放内核信号量锁时,就调用up()函数。

当进程希望获取内核信号量锁时,就调用down()函数。__down()函数的主要任务是挂起当前进程,直到信号量被释放。

down_interrupt()函数广泛地用在设备驱动程序中,因为如果进程接受了一个信号但在信号量上被阻塞,就允许进程放弃down操作。

内核以严格的FIFO顺序处理等待读/写信号量的所有进程。如果读者或写者进程发现信号量关闭,这些进程就被插入到信号量等待队列链表的末尾。当信号量被释放时,就检查处于等待队列链表第一个位置的进程,第一个进程常被唤醒。

补充原语和信号量之间的真正差别在于如何使用等待队列中包含的自旋锁。在补充原语中,自旋锁用来确保complete()和wait_for_completion()不会并发执行。在信号量中,自旋锁用于避免并发执行的down()函数弄乱信号量的数据结构。

通过操纵当前tread_info描述符preempt_count字段中存放的软中断计数器。可以在本地CPU激活货禁止可延迟函数。

系统中的并发度取决于两个主要的因素:1同时运转的I/O设备数 2进行有效工作的CPU数。

原子操作比自旋锁和中断禁止都要快,只有在几个内核控制路径同时访问这个数据结构时速度才会慢下来。

记住,只要内核控制路径获得自旋锁,就禁止本地中断红或本地软中断,自动禁止内核抢占。

注意,信号量工作方式在单处理器系统和多处理器系统上完全相同。

每个中断处理程序都相对自己串行地执行,也就是说,中断处理程序本身不能同时运行多次。

在单处理器系统上,必须通过在中断处理程序的所有临界区上禁止中断来避免竞争条件。只能用这种方式进行同步,因为其他的同步原语都不能完成这件事。

事实上,即使在一个CPU上禁止了中断,中断处理程序还可以在其他CPU上执行。

可延迟函数的执行总是在一个CPU上串行进行。

中断处理程序不是可重入的且不能被异常中断。

异常处理程序可以通过local_bh_disable()宏简单地禁止可延迟函数,而不禁止本地中断。

可延迟函数不能阻止中断处理程序,因此必须通过在可延迟函数执行期间禁用本地中断来避免竞争条件。

中断处理函数可以随意访问被延迟函数访问的数据结构而不用关中断。

没有必要显式地禁止可延迟函数,因为当中断处理函数终止执行时,可延迟函数才能被实质激活。

0 0
原创粉丝点击