L20 Kernel Synchronizations
L20 Kernel Synchronizations
Fall 2019
Jason Tang
• Mutexes
• Spinlocks
• Completions
• Delaying Work
• Creating Kthreads
2
Kernel Threads
3
Kernel Concurrency
• Just as with user space threads, kernel can have race conditions, and its
threads can deadlock
• Example:
• A driver creates the device /dev/foo that user processes may write to
4
Mutexes
#include <linux/mutex.h>
static DEFINE_MUTEX(foo_mutex);
…
static void foo_func(void) {
mutex_lock(&foo_mutex);
/* critical section */
mutex_unlock(&foo_mutex);
}
• Use DEFINE_MUTEX() macro to declare and initialize a mutex at compile
time; otherwise call mutex_init() during runtime to initialize it
5
Mutexes
• If another kthread else already has lock, this function returns zero
• If no kthread has lock, this function acquires lock and returns one
6
Spinlocks
• In most programs (and kernel drivers), code is rarely within critical sections
• In some places, a kthread may not sleep because preemption has been
disabled
7
自旋锁
· 在 include/linux/spinlock.h 中定义
· 与互斥锁类似,因为它强制互斥
· 与互斥锁不同,如果 kthread 无法获得自旋锁,它会处于忙等状态直到获得锁
Spinlocks
· Kthread 不会立即切换上下文(至少在其时间片到期之前不会)
· 可用于 kthread 不能休眠的地方
· 大多数内核驱动程序使用自旋锁而不是互斥锁
• Defined in include/linux/spinlock.h
• Kthread will not immediately context switched away (at least, not until its
time slice expires)
8
Spinlocks
#include <linux/spinlock.h>
static DEFINE_SPINLOCK(foo_lock);
…
static void foo_func(void) {
spin_lock(&foo_lock);
/* critical section */
spin_unlock(&foo_lock);
}
9
自旋锁和抢占
· 在单处理器系统上,当获得自旋锁时,内核可能会禁用抢占
· 当自旋锁被持有时,临界区中不能有任何其他东西,因此内核最好继续调
度该 kthread
· 要强制禁用该CPU 的抢占,请调用spin_lock_irqsave()
10
Kthreads and Synchronizations
• Declared in include/linux/completion.h
11
Completion Variables
#include <linux/completion.h>
static DECLARE_COMPLETION(foo_cv);
…
static void foo_producer(void) {
/* produce work */
complete(&foo_cv);
}
12
Completion Variables
/* 'work' is a shared resource */ static void foo_consumer(void) {
static int work; spin_lock(&foo_lock);
… while (work == 0) {
static void foo_producer(void) { spin_unlock(&foo_lock);
spin_lock(&foo_lock); wait_for_completion(&foo_cv);
work++; spin_lock(&foo_lock);
complete_all(&foo_cv); }
spin_unlock(&foo_lock); work--;
} spin_unlock(&foo_lock);
}
13
Reusing Completion Variables
• Time since the kernel has been booted is stored in the global counter
jiffies, declared in include/linux/jiffies.h
15
Busy-Waiting
unsigned long later = jiffies + 5 * HZ;
while (time_before(jiffies, later))
cpu_relax();
16
Scheduling
unsigned long later = 5 * HZ;
set_current_state(TASK_INTERRUPTIBLE);
/* suspend for at least 5 seconds */
schedule_timeout(5 * HZ)
17
Interruptible vs. Uninterruptible
18
Creating Kthreads
struct task_struct *kthread_run(int (*threadfn)(void *data),
void *data, const char namefmt[], …);
19
Linux Kernel Pointers and Error Codes
• Kernel stores error codes within pointers via special integer values
• Kernel code do not tend to dynamically create and destroy threads frequently
21