内核机制之互斥与同步

来源:互联网 发布:井上真央松本润 知乎 编辑:程序博客网 时间:2024/05/16 16:13

并发来源:
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 initstatic inline void sema_init(struct semaphore *sema,int val);//down operationint 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_MAXstatic 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;};
0 0