Linux内核的并发与同步

来源:互联网 发布:淘宝助手 编辑:程序博客网 时间:2024/05/16 15:09

1、简介

1.1 基本概念

抢占式内核: 用户程序在执行系统调用期间可以被高优
先级进程抢占
非抢占式内核:用户程序执行系统调用不能被其他进程
抢占

对称多处理器(SMP): 一个计算机上汇集了多个处理
器,他们共享内存和总线,可并行处理数据
单处理器: 只有一个CPU

并发指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态。
当多个进程、线程或中断同时访问同一个资源,可能导致错误。

2 原子变量

共享资源恰巧是一个简单的整数值,普通整数的加减操作不能保证在多线程下的原子操作,使用锁机制代价又太高,所以出现了原子操作。

2.2 主要方法

//定义原子变量,两种方法atomic_t n = ATOMIC_INIT(0);void atomic_set(atomic_t *v, int i);//读原子变量的值void atomic_read(atomic_t *v);//原子变量加、减i值void atomic_add(int i, atomic_t *v);void atomic_sub(int i, atomic_t *v);//原子变量加、减1void atomic_inc(atomic_t *v);void atomic_dec(atomic_t *v);

3、信号量

linux内核中的信号量是一种资源锁,进程只有得到该锁(信号量)才能执行临界区代码,执行完毕释放该锁
信号量采用睡眠等待机制:如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推到一个等待队列中睡眠,当持有信号量的进程将信号量释放后,处于等待队列中的那个任务被唤醒,并将获得该信号量。
中断服务函数不能进行睡眠,因此信号量不能用于中断当中,但可以使用后面介绍的自旋锁

3.1 使用流程

1、定义一个信号量
2、初始化信号量
3、获得信号量(减操作)
==>执行临界区代码
4、释放信号量(加操作)

3.2 主要方法

#include <linux/semaphore.h>//信号量相关函数的头文件//定义一个信号量struct semaphore my_sem;//初始化信号量void sema_init(struct semaphore *sem, int val);参数1:信号量变量参数2:信号量的计数值//获取信号量(减操作,不能被系统消息打断,导致调用者睡眠)void down(struct semaphore *sem);//获取信号量(减操作,可以被系统消息打断,导致调用者睡眠)int down_interruptible(struct semaphore *sem);//尝试获得信号量,成功返回0,失败返回非0,不会导致调用者睡眠int down_trylock(struct semaphore *sem);//释放信号量,即使信号量加1(如果线程睡眠,将其唤醒)void up(struct semaphore *sem);

4、互斥体

互斥体与信号量类似,不过更简单,只有加锁、解锁两种状态

4.1 主要方法

//定义锁变量struct mutex my_mutex;//初始化锁mutex_init(struct mutex *lock);//加锁,如果加锁不成功,会阻塞当前进程void mutex_lock(struct mutex *lock);//解锁void mutex_unlock(struct mutex *lock);//尝试加锁,会立即返回,不会阻塞进程int mutex_trylock(struct mutex *lock);

5、自旋锁

自旋锁的名称源于它的工作原理:尝试获取一个自旋锁,如果锁空闲就获取该自旋锁并继续向下执行;如果锁已被占用就循环检测该锁是否被释放( 原地打转直到锁被释放)
 只有在占用锁的时间极短的情况下使用;不能递归使用一个自旋锁(形成死锁);占用锁时不能使用可能引起进程调度的函数,如copy_xx_user()、kmalloc()、msleep()…
自旋锁主要针对SMP或单CPU抢占内核的情况,而对于单CPU非抢占内核自旋锁退化为空操作

5.1 主要方法

#include <linux/spinlock.h>//定义自旋锁变量struct spinlock my_spinlock;spinlock_t my_spinlock;//自旋锁初始化spin_lock_init(&my_spinlock); //获得自旋锁(可自旋等待,可被软、硬件中断)void spin_lock(spinlock_t *my_spinlock);//释放自旋锁,退出临界区void spin_unlock(spinlock_t *lock)//尝试获得自旋锁(不自旋等待,成功返回1、失败则返回0)int spin_trylock(spinlock_t *lock)

自旋锁变体

//获得自旋锁(可自旋等待,保存中断状态并关闭软、硬件中断)void spin_lock_irqsave(spinlock_t *my_spinlock,unsigned long flags);//释放自旋锁,退出临界区void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)