Linux内核开发之并发控制(三)

算了,既然给你那么多秘籍了,也不在乎这剩下的两三招:

出招表五:顺序锁(seqlock)

使用顺序锁,读执行单元绝不会被写执行单元阻塞,同时写执行单元也不需要等待所有读执行单元完成读操作后才进行写操作。但是写执行单元之间仍然是互斥的。如果读执行单元在读操作期间,写执行单元已经发生了操作,那么,读执行单元必须重新读取数据,以便确保得到的数据是完整的。

致命弱点:顺序锁有一个限制,就是它必须要求被保护的共享资源不含有指针。因为写执行单元可能使得指针失效,但读执行单元如果正要访问该指针,将导致Oops。

在Linux内核中。读执行单元设计如下顺序读操作。

1)读开始

unsigned read_seqbegin(const seqlock_t *s1);

read_seqbegin_irqsave(lock, flag);【read_seqbegin_irqsave(lock, flag)=local_irq_save() + read_seqbegin();】

2)重读

int read_seqretry(const seqlock_t *s1, unsigned iv);

read_seqretry_irqrestore(lock, iv, flag);【read_seqretry_irqrestore(lock, iv, flag)= read_seqretry()+ local_irq_restore();】

读执行单元使用顺序锁的模式如下:

do{

    seqnum = read_seqbegin(&seqlock_a);

    //读操作代码块

    。。。

}while(read_seqretry(&seqlock_a, seqnum));

在Linux内核中。写执行单元设计如下顺序读操作。

1)获得顺序锁

void write_seqlock(seqlock_t *s1);

int write_ tryseqlock(seqlock_t *s1);

write_seqlock_irqsave(lock, flags);【=local_irq_save() + write_seqlock()】

write_seqlock_irq(lock);【=local_irq_disable() + write_seqlock()】

write_seqlock_bh(lock);【=local_bh_disable() + write_seqlock()】

2)释放顺序锁

void write_sequnlock(seqlock_t *s1);

write_sequnlock_irqrestore(lock, flag);【=write_sequnlock() + local_irq_restore()】

write_sequnlock_irq(lock);【=write_sequnlock() + local_irq_enable()】

write_sequnlock_bh(lock);【write_sequnlock()+local_bh_enable()】

写执行单元使用顺序锁的模式如下:

write_seqlock(&seqlock_a);

…//写操作代码

write_sequnlock(&seqlock_a);

 

出招表六:RCU(Read-Copy-Update)

对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它,但写者在访问它时首先备份一个副本,然后对副本进行修改,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作时。

1)读锁定。                                                    2)读解锁。                                    使用RCU进行读的模式如下:

rcu_read_lock();                                             rcu_read_unlock();                        rcu_read_lock()

rcu_read_lock_bh();                                        rcu_read_unlock_bh();                   …//读临界区

                                                                                                                    rcu_read_unlock()

3)与RCU相关的写者函数包括:

struct rcu_head{

     struct rcu_head *next;//下一个RCU

     void (*func)(struct rcu_head *head);//获得竞争条件后的处理函数

};

synchronize_rcu(void);//阻塞读者,直到所有的读者已经完成读端临界区,写者才可以继续下一步操作

synchronize_sched();//等待所有CPU都处在可抢占状态,保证所有中断(不包括软中断)处理完毕

void call_rcu(struct rcu_head *head, void (*func)(void *arg),void arg);//不阻塞写者,可以在中断上下文或软中断使用,

使用synchronize_rcu的写操作流程如下:

DEFINE_SPINLOCK(foo_spinlock);

Int a_new;

spin_lock(&foo_spinlock);

//a_new = a;

//write a_new;

synchronize_rcu();

//a=a_new;

spin_unlock(&foo_spinlock);

//使用call_rcu的写操作流程如下:

struct protectRcu

{

    int protect;

    struct rcu_head rcu;

};

struct protectRcu *global_pr;

//一般用来释放老的数据

void callback_function(struct rcu_head *r)

{

    struct protectRcu *t;

    t=container_of(r, struct protectRcu, rcu);

    kfree(t);

}

void write_process()

{

    struct protectRcu *t, *old;

    t = kmalloc (sizeof(*t),GFP_KERNEL);//创建副本

    spin_lock(&foo_spinlock);

    t->protect = xx;

    old= global_pr;

    global_pr=t;//用副本替换

    spin_unlock(&foo_spinlock);

    call_rcu(old->rcu, callback_function);

}


你可能感兴趣的:(Linux内核开发之并发控制(三))