内核机制之互斥与同步

并发来源:
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;

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值