xv6自旋锁

     在多核和多线性的系统中,需要注意访问安全,即需要考虑不同的CPU和多线程之间因竞争而存在的互斥关系。比如下面的例子:

struct list { int data; struct list *next; }; struct list *list = 0; void insert(int data) { struct list *l; l = malloc(sizeof *l); l->data = data; l->next = list; list = l; } 

考虑多核的情况:如果多个CPU在第16行之前同时访问第15行,那么后面CPU的赋值会将前面CPU的赋值覆盖

考虑多线程的情况:如果第一个线程执行完15行之后,时钟中断,切换到第二个线程并恰好也执行第15行,则也会发生冲突

因此,对于上述情况,我们应该每次仅仅允许单个访问,可以通过互斥来实现独占的访问。

 

以上面的为例子,使用互斥锁的一种最常见的形式是:

struct list *list = 0; struct lock listlock; void insert(int data) { struct list *l; acquire(&listlock); l = malloc(sizeof *l); l->data = data; l->next = list; list = l; release(&listlock); } 

即在即将访问临界资源的时候,取得锁;在访问完后,释放锁。

 

接下来的问题,是如果实现acquire函数,即获取锁的操作,考虑下面的方法

void acquire(struct spinlock *lk) { for(;;) { if(!lk->locked) { lk->locked = 1; break; } } } 

这是一个朴素的想法,但是这种方法存在问题,因为如果lk->locked为0时。第一个线程进入到if内,正打算执行lk->locked = 1,这时发生了一个时钟中断,切换到第二个线程。对于第二个线程,这时ik->lock依旧为0,因此也会进入到if语句内。

为什么会产生这样的情况呢?主要是因为判断和条件语句有可能分隔执行,即这两个不是原子操作。

 

可以使用x86的硬件指令xchg来实现原子操作,xv6操作系统中的自旋锁就是采用的这种方式:

void acquire(struct spinlock *lk) { while(xchg(&lk->locked, 1) != 0) ; } void release(struct spinlock *lk) { xchg(&lk->locked, 0); }  

在需要获取锁的时候,只有当有人释放锁使得lk->locked为0时,xchg才会返回0,同时使得lk->locked又变成1,离开while循环,否则一直繁忙等待 (这也是自旋锁效率不高的一个原因哈)。

 

 

 

 

 

 

 

 

你可能感兴趣的:(多线程,list,struct,insert,X86)