本文将按照自己之前根据《深入Linux设备驱动程序内核机制》对linux内核互斥与同步的学习和理解,将自己当时所做的笔记写到此blog上。其中有的图片会是从《深入Linux设备驱动程序内核机制》中截取的。
1. 自旋锁
spinlock_t结构体的定义:
typedef struct spinlock {
union {
struct raw_spinlock rlock;//这个是和体系结构相关的实现
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
struct {
u8 __padding[LOCK_PADSIZE];
struct lockdep_map dep_map;
};
#endif
};
} spinlock_t;
spin_lock()的原理就是通过不断循环查询spinlok_t.raw_spinlock.arch_spinlock_t.init来实现原地自旋。
常用的变体有:
上图来源于《深入Linux设备驱动程序内核机制》。
preempt_disable():"preempt_count+1"操作,禁止内核抢占
local_irq_disable():禁止cpu响应中断
local_irq_save(flags):禁止cpu响应中断,并保存FLAGS寄存器
local_bh_disable():禁止软中断
ps:内核抢占发生的时机:1. 中断处理程序返回内核空间之前 2. 内核中显式调用schedule()函数 3. 内核执行中被阻塞(此时也会调用schedule(),和2类似)
用户抢占发生的时机:1. 中断处理程序返回用户空间时 2. 系统调用返回用户控件时
2. 读写锁
rwlock的特点:1. 如果当前有进程正在写,那么其他进程就不能读,当然也不能写 2. 如果当前有进程正在读,那么其他进程可以读,但是不能写。
相对于spinlock的多个版本,rwlock同样有多个版本。
对于读取者:
void read_lock(rwlock_t *lock);
void read_lock_irq(rwlock_t *lock);
void read_lock_irqsave(rwlock_t *lock, unsigned long flags);
void read_unlock(rwlock_t *lock);
void read_unlock_irq(rwlock_t *lock);
void read_unlock_irqsave(rwlock_t *lock, unsigned long flags);
对于写入者:
void write_lock(rwlock_t *lock);
void write_lock_irq(rwlock_t *lock);
void write_lock_irqsave(rwlock_t *lock, unsigned long flags);
void write_unlock(rwlock_t *lock);
void write_unlock_irq(rwlock_t *lock);
void write_unlock_irqsave(rwlock_t *lock, unsigned long flags);
try版本:
int read_lock(rwlock_t *lock);
int write_lock(rwlock_t *lock);
3.信号量
初始化信号量:
获取信号量:
释放信号量:
4. 小总结
spin_lock:通过不断循环查询spin_lock_t.raw_spinlock_t.arch_spinlock_t.int来实现原地自旋。
semaphore:通过等待链表(类似等待队列)实现任务的挂起来等待信号量。将当前不能获取该信号量的任务添加到该等待链表sem->wait_list(类似等待队列)上进行等待,有down()获取/等待以及up()释放/唤醒两种操作,不能获取semaphore的任务将会挂起等待,因此会有schedule()发生。通过sem->lock实现sem的互斥访问。
等待队列:通过wait_event()会建立一个wait_queue_t结构体,并将current进程放到该结构体中,将该结构体添加到wait_queue_head_t的链表上,将current任务状态设置为TASK_UNINTERRUPTIBLE,即挂起当前任务通过wake_up()唤起等待队列上的任务。重点在于等待某个时间发生,将任务挂起或者唤醒。
工作队列:中断的下半部机制,通过sechedule_work()将一个任务添加到系统的工作队列(也可以是自己创建的工作队列),然后唤醒系统的worker工作线程来处理工作队列中的任务。重点在于将工作任务交给系统线程来执行。
工作队列和等待队列将会在下一次介绍。