Xv6 锁机制

181 阅读2分钟

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

0072Vf1pgy1foxli9lpyoj31kw0w0qlm.jpg

Locking

Learning xv6-riscv-book Chapter 5 Locking

Concurrency: situations in which multiple instruction sreams are interleaved, due to multiprocessor parallelism, threasd switching, or interrupts.

Concurrency control: strategies aimed at correctness under concurrency.

Lock: provides mutual exclusion -- ensurign that only one CPU at a time can hold the lock.

Race conditions

Race condition:

  • a memory location is accessed concurrently
  • at list one write

E.g. two CPUs execute list.push at the same time:

struct element {
    int data;
    struct element *next;
};

struct element *list = 0;

void
push(int data) 
{
    struct element *l;
    
    l = malloc(sizeof *l);
    l->data = data;
    l->next = list;  // race
    list = l;        // race
}

race.JPEG

race condition -> offen bug:

  • lost update
  • read incompletely-updated data

To avoid races -> use a lock:

...

struct lock listlock;  // (+)

void
push(int data) 
{
    struct element *l;
    
    l = malloc(sizeof *l);
    l->data = data;
    
    acquire(&listlock);  // (+)
    l->next = list;
    list = l;
    release(&listlock);  // (+)
}

between acquire and release: critical section.

  • only one CPU at a time can operate on the data structure in the critical section.
  • locks limit performance: locks reduce parallelism:
    • conflict
    • contention

(A major challenge in kernel design is to avoid lock contention)

Locks

Xv6 has two types of locks:

  • spinlocks
  • sleep-locks

spinlocks

kernel/spinlock.h:

struct spinlock {
  uint locked;       // Is the lock held?
  ...
}

acquire:

use __sync_lock_test_and_set:

  • amoswap lk->locked and 1
  • return: old(swapped) contents of lk->locked
void
acquire(struct spinlock *lk)
{   
  ...

  while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
    ;

  ...
}

release:

use __sync_lock_release:

  • atomic lk->locked = 0
void
release(struct spinlock *lk)
{
  ...
  __sync_lock_release(&lk->locked);
  ...
}

Using locks

  • any time a variable can be written by one CPU at the same time that another CPU can read or write it, a lock should be used
  • locks protect invariants: if an invariant involves multiple memory locations, typically all of them need to be protected by a single lock to ensure the invariant is maintained
  • not to lock too much (for efficiency)

Deadlock and lock ordering

hold serveral locks at the same time:

  • IMPORTANT: all code paths acquire those locks in the same order => lock-order chains
  • ORTHERWISE: risk of deadlock

global deadlock-avoiding order: difficult!

Locks and interrut handlers

Some xv6 spinlocks protect data that is used by both threads and interrupt handlers:

  • To avoid deadlock: when a CPU acquires any lock, xv6 always disables interrupts on that CPU.
  • push_off in acquire & pop_off in release

Instruction and memory ordering

Compiler & CPU may do instruction re-orderings for performance.

Re-ordering can easily lead to incorrect behavior on multiprocessors.

To tell hardware and compiler not to perform such re-ordering:

  • Xv6 uses __sync_synchronize(): a memory barrier

Sleep locks

To hold a lock for a long time -- use sleep lock

e.g. file system keeps a file locked while reading & writing its content on the disk.

sleep-locks:

Notice:

  • sleep-locks leave interrupts enable: cannot be used in interrupt handlers.
  • sleep-locks may yield the CPU: cannot be used inside spinlock critical sections

By CDFMLR

顶部图片来自于小歪API,系随机选取的图片,仅用于检测屏幕显示的机械、光电性能,与文章的任何内容及观点无关,也并不代表本人局部或全部同意、支持或者反对其中的任何内容及观点。如有侵权,联系删除。