并发来源:
1 中断 2 处理器的调度 3 多处理器的并发执行
local_irq_enable: 打开本地处理器中断,
对应于 local_irq_save:保存当前处理器状态
local_irq_disable: 关闭本地处理器中断
对应于 local_irq_restore
自旋锁
设置多处理器共享的全局变量锁V,并定义V =1 时为上锁状态,V=0时为解锁状态。当处理器A上的代码要进入临界区时,首先读取v的值,并判断v的值是否是0,如果v != 0,则其他处理器上的代码正在访问临界区代码,处理器A就处于忙等待状态。如果V == 0,那么处理器可以访问临界区代码,先把V设置1,进入临界区代码,当访问完临界区代码时,将v= 0。
关键是在 读取V,并判断V值 , 必须是原子操作
typedef struct raw_spinlock{
volatile unsigned int raw_lock;
}raw_spinlock_t;
typedef struct spinlock{
union{
struct raw_spinlock rlock;
};
}spinlock_t;
static inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
}
//宏preempt_disable,在定义CONFIG_PREEMPT即支持内核可抢占调度下,关闭调度器的可抢占性
static inline void raw_spin_lock(raw_spinlock_t * lock)
{
preempt_disable();//关闭内核调度器的可抢占性
do_raw_spin_lock(lock);
}
static inline void spin_unlock(spinlock_t *lock)
{
raw_spin_unlock(&lock->rlock);
}
static inline void raw_spin_unlock(raw_spinlock_t *lock)
{
do_raw_spin_unlock(lock)
preempt_eanble();
}
spin_lock(spinlock_t *lock);并没有关闭中断,只关闭了可抢占性。所以有可能在处理器A获取自旋锁后,在临界区代码执行中,发生硬件中断,若中断处理函数中也需要对共享资源访问时,就会发生死锁状态。
static inline void spin_lock_irq(spinlock_t *lock)
{
raw_spin_lock_irq(&lock->rlock);
}
static inline void raw_spin_lock_irq(raw_spinlock_t *lock)
{
local_irq_disable();
preempt_disable();
do_raw_spin_lock(lock);
}
static inline void spin_unlock_irq(spinlock_t *lock)
{
raw_spin_unlock_irq(&lock->rlock);
}
static inline void raw_spin_unlock_irq(raw_spinlock_t *lock)
{
do_raw_spin_unlock(lock);
preempt_enable();
local_irq_enable();
}
拥有自旋锁的代码必须是原子的,不能睡眠。不能调用像kmallock 能够引起睡眠的函数等。
当知道一个自旋锁可能在中断上下文中使用时,要使用spin_lock_irq(spinlock_t *lock);
而spin_lock(spinlock_t *lock);只能在确定不会出现在中断上下文中时
与spin_lock_irq相似的宏 spin_lock_irqsave(spinlock_t *lock,unsigned long flag);
就是关闭中断前,将当前的处理器状态宝存在变量flag中
当调用spin_unlock_irqrestore释放锁时,将flag写回到寄存器中。
综上所述:spin_lock_irq与spin_unlock_irq
不管在多处理器系统与单处理器上,还是内核抢占与内核不可抢占上都有很好的可移植性。
调用自旋锁的进程切忌不能睡眠
自旋锁rwlock
允许任意的读取着同时进入临界区代码,但写入着必须互斥访问。如果一个进程想进入临界区进行读,则需要检查是否有写入着,如果有的话,就必须自旋,否则,可以进入临界区。如果一个进程想进入临界区进行写,则需要检查是否有写或者正在读的进程,若有,则需自旋等待。
void read_lock_irq(rwlock_t *lock);
void write_lock_irq(rwlock_t *lock);
信号量(semaphore)
相对与自旋锁,信号量最大的区别是信号量允许调用的进程可以进行睡眠,也就是可能丧失对处理器的控制,出现进程切换。
struct semaphore{
spinlock_t lock;
unsigned int count;//允许进入临界区的个数
struct list_head wait_list;//不能获得信号量的进程投入到等待队列中
};
//semaphore init
static inline void sema_init(struct semaphore *sema,int val);
//down operation
int down_interruptible(struct semaphore *sem)
{
unsigned long flag;
int result = 0;
spin_lock_irqsave(&sem->lock,flag);
if( likely(sem->count > 0) )
{
sem->count--;
}
else
{
result = __down_interruptible(sem);
}
spin_unlock_irqrestore(&sem->lock,flag);
return result;
}
//__down_interruptible cal在内部调用__down_common(struct semaphore *sem,long state,long timeout);
//state = TASK_INTERRUPTIBLE
//timeout = LONG_MAX
static inline int __down_common(struct semaphore *sem,long state,long timeout)
{
struct task_struct *task = current;
list_add_tail(&waiter.list,&sem->list);
waiter.task = task;
waiter.up = 0;
while(1)
{
if(signal_pending_state(state,task))
goto interrupted;
if(timeout <= 0)
goto timeout;
__set_task_state(task,state);
spin_unlock_irq(&sem->lock);
timeout = schedule_out(timeout);
spin_lock_irq(&sem->lock);
if(waiter.up)
return 0;
}
timeout:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTER;
}
//具体的事例代码如下:
struct semaphore demosem;
sem_init(&demosem,5);
if(down_interruptible(&demosem))
return -ERESTARTSYS;
void up(struct semaphore *sem)
{
unsigned long flag;
spin_lock_irqsave(&sem->lock,flag);
if( list_empty(&sem->list) )
sem->count++;
else
__up(sem);
spin_unlock_irqrestore(&sem->lock,flag);
}
static inline void __up(struct semaphore *sem)
{
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter,list);
list_del(&waiter->list);
waiter->up = 1;
wake_up_precess(waiter->task);
}
信号量的用途
实现互斥 机制,也就是初始化时值 1,之允许一个进程进入临界区。
DECLARE_MUTEX(name) struct semaphore name = _SEMAPHORE_INITIALIZER(name,1)
DECLARE_MUTEX(demosem);
void demo_write()
{
if(down_interruptible(&demosem))
return -ERESTARTSYS;
//进入临界区代码
up(&demosem);
}
互斥锁Mutex
completion机制
struct completion{
unsigned int done;
};