一、概念
同步:为了达到多线程协同目的而设计的机制,通常包含异步通信机制和互斥机制;
常见并发原因:中断发送,软中断/tasklet,内核抢占,睡眠以及用户空间同步,多核处理器发生的多对称处理;
用户抢占 与 内核抢占 :用户抢占指当 内核从系统调用 或者 中断中返回用户空间时,当need_resched置位,会调用shedule调用;内核抢占 指 2.6 及以后的内核的特性,当更高优先级的任务出现,可以挂起当前正在内核态运行的低优先级进程;
二、典型同步方法
信号量:能导致睡眠的计数锁,count > 0 可获取【可长期锁定】
sema_init(struct semaphore *sem, int val) //初始化信号量,将信号量计数器值设置val。
down(struct semaphore *sem) // 获取信号量(不可被中断)。
down_interruptible(struct semaphore *sem) //可被中断地获取信号量,如果被信号中断返回错误。
up(struct semaphore *sem) //释放信号量sem。
互斥体:计数器为1的信号量(简化版)【可长期锁定】
mutex_init(struct mutex *lock) // 初始化锁
mutex_lock(struct mutex *lock) // 加锁,如果不可用则睡眠
mutex_unlock(struct mutex *lock) // 解锁
自旋锁:只能被一个可执行任务持有的锁,否则忙等【低开锁加锁开销,短期锁定,不可休眠】
spin_lock_init(&lock); // 初始化锁
spin_lock(&lock);//获取自旋锁
spin_lock_irq(&lock);//关中断,获取自旋锁,不建议使用
spin_lock_irqsave(&lock, flags);//关中断,保存中断状态,获取自旋锁
spin_unlock(&lock);//解锁
spin_unlock_irq(&lock);// 解锁,并打开中断(不建议使用)
spin_unlock_irqrestore(&lock, flags); // 解锁,并用flags恢复中断状态
原则操作:提供对整数和位操作的一组不能被分割指令执行的操作【性能最优,使用受限】
ATOMIC_INIT(int i) // 声明一个atomic_t变量初始化为i
int atomic_read(atomic_t *v) // 原子地读取整数变量v
void atomic_set(atomic_t *v, int i) // 原子地设置v值为i
void atomic_add(int i, atomic_t *v) // 原子地给v加i
void atomic_sub(int i, atomic_t *v) // 原子地从v减i
void atomic_inc(atomic_t *v) // 原子地给v加1
void atomic_dec(atomic_t *v) // 原子地给v减1
void set_bit(int nr, void *addr) // 原子地设置addr所指对象的第nr位
void clear_bit(int nr, void *addr) //原子地清空addr所指对象的第nr位
void change_bit(int nr, void *addr) // 原子地翻转addr所指对象的第nr位
等待队列:以队列为基础,与进程调度结合紧密,实现异步事件通知处理机制。
init_waitqueue_head(wait_queue_head_t *queue); //动态初始化等待队列
DECLARE_WAIT_QUEUE_HEAD(queue);// 静态定义并初始化等待队列
wait_event(queue, condition);
// 不可打断的在等待队列睡眠,直到唤醒时condition为真
wait_event_interruptible(queue, condition);
// 睡眠直到唤醒时condition为真返回0 或 被信号唤醒返回-ERESTARTSYS
wait_event_timeout(queue, condition, timeout);
// 指定timeout时间睡眠,若唤醒condition为真 返回剩余时间,若timeout返回0
wait_event_interruptible_timeout(queue, condition, timeout);
// 若唤醒condition为真 返回剩余时间),timeout返回0,信号唤醒返回-ERESTARTSYS
void wake_up(wait_queue_head_t *queue); // 唤醒
void wake_up_interruptible(wait_queue_head_t *queue); // 唤醒(只针对可信号打断的)
三、常见同步问题
锁、信号量、mutex、等待队列 没有初始化就开始使用;
AA死锁(连续请求同一把锁),或者 ABBA 死锁 (一个进程持有A请求B,一个进程持有B请求A)
wait_event_interruptible_timeout 返回值没有正确判断,注意3种返回值:若唤醒condition为真 返回剩余时间),timeout返回0,信号唤醒返回-ERESTARTSYS;