Linux中的竞态与并发

一、并发

并发就是多个进程同时、并行执行。在但处理器下,并发只是宏观上的并发,用户感觉是多个进程共同执行,其实只是多个进程轮流占用处理器运行,只有在多处理器的情况下才会实现真正的同时执行。

 

二、临界区

临界区就是访问和操作共享数据的代码段。进程并发访问临界区的数据和操作共享资源是不安全的。如果两个进程同时访问临界区,那就会发生资源抢夺,这种情况就叫做竞争条件。避免并发和防止竞争条件被称为同步。

 

三、内核造成并发执行的原因

(1)中断:中断是随时可能产生,内核一旦接收到中断,就会优先执行中断。如果中断代码中修改了之前运行进程的共享资源,就会出现bug

(2)内核抢占:支持内核抢占的情况下,正在执行的进程随时可能被抢占。

(3)睡眠:当在内核中执行的进程睡眠时,就会唤醒调度程序,调度新的进程执行。

(4)多处理器:多个处理器就能同时执行多个进程。

 

四、单CPU

1、(1)在单处理器不支持抢占的情况下,运行在内核的两个内核进程,并不会产生并发。(2)中断上下文和普通进程之间,会产生并发。内核进程在执行时,随时可能被中断打断,所以临界区的代码,可以通过关闭中断来避免并发。

2、(1)单处理器支持抢占的情况下,运行在内核的两个进程,会产生并发。访问临界区时需要关抢占。

2)中断上下文与普通内核进程之间,会产生并发。在临界区代码,可以通过关闭中断来避免并发。

 

五、SMP

1、多处理器抢占式内核的内核同步需要防:

(1)防内核抢占

(2)防中断打断

(3)防其他处理器

接下来主要介绍以下如何防其他处理器。

2、自旋锁

代码在进入临界区前加上锁,在进程还没出临界区之前,别的进程(包括自身处理器和别的处理器的进程)都不能进入临界区。

自旋锁可理解为,每个进程进入上锁的临界区前,必须先获得锁,否则在获得锁这边代码上查询(忙等待),直到临界区里面的进程走出临界区,别的进程获得锁后进入临界区。有且只有一个获得锁的进程进入临界区。

(1)使用自旋锁需要先定义并初始化自旋锁:

1)静态定义并初始化

spinlock_t lock = SPIN_LOCK_UNLOCKED;

2)也可以动态定义并初始化

spinlock_t lock;

spin_lock(&lock);

(2)在进入临界区前,必须先获得锁,使用函数:

spin_lock(&lock);

(3)在退出临界区后,须释放锁,使用函数:

spin_unlock(&lock);

 

3、信号量

自旋锁不能睡眠,要求临界区执行时间尽可能的短。信号量是一种睡眠锁,当进程试图获取已经别占用的信号量,它就会被放到等待队列中,直到信号量释放后被唤醒。信号量有两种:互斥信号量和计数信号量。互斥信号量就是说同一时间只能有一个进程获得锁并进入临界区。

4、互斥量

互斥量是互斥信号量的升级板。使用方法如下:

(1)定义并初始化(两种)

1)静态定义并初始化:

DEFINE_MUTEX(name)

2)动态定义并初始化

struct mutex mutex;

mutex_init(&mutex);

(2)获得互斥量

void inline __sched mutex_lock(struct mutex *lock) //不能获得锁是进入不可中断睡眠

int __sched mutex_lock_interruptible(struct mutex *lock) //进入可中断睡眠

int __sched mutex_trylock(struct mutex *lock) //尝试获得锁

(3)释放信号量

void __sched mutex_unlock(struct *lock)

 

5、原子操作(atomic_t

原子操作就是这段代码不会被其他进程打断,加上自旋锁之后的操作也算是原子操作。

你可能感兴趣的:(Linux中的竞态与并发)