riscv-xv6中的锁机制

本篇文章主要介绍riscv-xv6中的锁机制,在riscv-xv6系统中,内核有不少的共享变量,如进程表,物理内存空闲链表,用于console读写的缓冲区等,这些共享变量在多个进程中共享需要互斥进行访问,否则就会出现数据一致性问题。

在riscv-xv6中有两种锁机制,一种为自旋锁(spinlock),一种为睡眠锁(sleeplock)。下面分别介绍一起其实现的机制原理以及应用的场合。

spinlock的实现: 关于__sync_lock_test_and_set函数和__sync_synchronize函数的实现和详细注释可以参考[1];用push_off关闭中断可以解决在嵌套调用情况下可能会导致的误开中断的问题[2]。

问答:在锁的获取和释放之间的过程中断为什么要关闭,如果中断不关闭,可能会出现哪些问题?自旋锁的临界区代码可以在不同 CPU 上执行吗?也就是自旋锁中为什么加上cpu结构体的指针?

回答:这三个问题是spinlock实现中的相互有关联的问题,将做一起回答。中断与特定的cpu相关,如果中断没有关闭,有可能会出现中断导致当前进程被挂起,下次调度运行时自旋锁的临界区代码就可以运行在不同的cpu上,这样可能会带来:(1)、cpu缓存一致性问题带来的性能问题;(2)、进程A已经获取锁并由于中断(如定时器中断)被让出cpu进入等待状态,进程B刚好也要获取锁,但由于锁已经被进程A获取,进程B将阻塞,导致系统处于较为复杂的进程间依赖情况,影响调度性能,有可能会导致死锁;(3)如果临界区在同一个cpu上不间断运行,如果当前 CPU 已持有锁并再次尝试获取,就可以直接报错,防止出现递归锁问题和死锁。(4)在多核系统中,调试并发问题是非常困难的,如果锁结构中存有持有锁的 CPU,那么:可以方便地打印日志,追踪哪个 CPU 在哪个时刻 获取了锁。在出现死锁或资源竞争时,能够快速确定哪个 CPU 或线程导致的问题。

睡眠锁的实现:睡眠锁的使用场景可以参考[3],sleeplock是阻塞式锁,用于长时间拥有资源的使用权限和持有锁的场景,如文件系统。关于acquiresleep和sleep函数的实现,可以参考[4]中的注释说明。在文件系统的缓冲区缓存的访问中,就调用了acquiresleep来访问缓冲区(如果已经被其他进程锁住,则进入睡眠等待,否则加锁并返回缓冲区,见[5]中的bget函数的实现和注释)

References


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *