鎖部分:spinlock.h/spinlock.c以及相關其餘文件代碼html
// Mutual exclusion lock. struct spinlock { uint locked; // 0未被佔用, 1已被佔用 // For debugging: char *name; // Name of lock. struct cpu *cpu; // The cpu holding the lock. uint pcs[10]; // The call stack (an array of program counters) // that locked the lock. }; // 初始化自旋鎖 void initlock(struct spinlock *lk, char *name) { lk->name = name; lk->locked = 0; lk->cpu = 0; } // Acquire the lock. // Loops (spins) until the lock is acquired. // Holding a lock for a long time may cause // other CPUs to waste time spinning to acquire it. void acquire(struct spinlock *lk) { // 關中斷 pushcli(); // disable interrupts to avoid deadlock. if(holding(lk)) // 判斷鎖的持有是否爲當前cpu panic("acquire"); // The xchg is atomic. // It also serializes, so that reads after acquire are not // reordered before it. while(xchg(&lk->locked, 1) != 0); // 拿不到鎖開始自旋 // Record info about lock acquisition for debugging. lk->cpu = cpu; getcallerpcs(&lk, lk->pcs); } // Release the lock. void release(struct spinlock *lk) { if(!holding(lk)) panic("release"); lk->pcs[0] = 0; lk->cpu = 0; // The xchg serializes, so that reads before release are // not reordered after it. The 1996 PentiumPro manual (Volume 3, // 7.2) says reads can be carried out speculatively and in // any order, which implies we need to serialize here. // But the 2007 Intel 64 Architecture Memory Ordering White // Paper says that Intel 64 and IA-32 will not move a load // after a store. So lock->locked = 0 would work here. // The xchg being asm volatile ensures gcc emits it after // the above assignments (and after the critical section). xchg(&lk->locked, 0); popcli(); }
1.什麼是臨界區? 什麼是同步和互斥? 什麼是競爭狀態? 臨界區操做時中斷是否應該開啓? 中斷會有什麼影響? XV6的鎖是如何實現的,有什麼操做? xchg 是什麼指令,該指令有何特性?node
// x86.h 調用方式如xchg(&lk->locked, 1) static inline uint xchg(volatile uint *addr, uint newval) { uint result; // The + in "+m" denotes a read-modify-write operand. asm volatile("lock; xchgl %0, %1" : "+m" (*addr), "=a" (result) : "1" (newval) : "cc"); return result; }
2.基於XV6的spinlock, 請給出實現信號量、讀寫鎖、信號機制的設計方案(三選二,請寫出相應的僞代碼)?程序員
struct semaphore { int value; struct spinlock lock; struct proc *queue[NPROC]; // 進程等待隊列,這是一個循環隊列 int end; // 隊尾 int start; // 隊頭 }; // 初始化信號量 void sem_init(struct semaphore *s, int value) { s->value = value; initlock(&s->lock, "semaphore_lock"); end = start = 0; } void sem_wait(struct semaphore *s) { acquire(&s->lock); // 競爭鎖,若是競爭不到進入自旋 s->value--; if (s->value < 0) { s->queue[s->end] = myproc(); // myproc()獲取當前進程, 放入隊尾 s->end = (s->end + 1) % NPROC; // 循環隊列計算新的隊尾 // 1. 釋放鎖(下一個sem_wait的進程才能進入acquire), // 2. 而後進入睡眠等待, 被喚醒時從新競爭鎖 sleep(myproc(), &s->lock); } release(&s->lock); } void sem_signal(struct semaphore *s) { acquire(&s->lock); // 競爭鎖 s->value++; if (s->value <= 0) { wakeup(s->queue[s->start]); // 喚醒循環隊列頭的進程 s->queue[s->start] = 0; s->start = (s->start + 1) % NPROC; // 從新計算隊頭 } release(&s->lock); } // proc.h // Per-process state struct proc { uint sz; // Size of process memory (bytes) pde_t* pgdir; // Page table char *kstack; // Bottom of kernel stack for this process enum procstate state; // Process state volatile int pid; // Process ID struct proc *parent; // Parent process struct trapframe *tf; // Trap frame for current syscall struct context *context; // swtch() here to run process void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory char name[16]; // Process name (debugging) };
[1] xv6鎖-博客園
[2] xv6鎖-xchg
[3] xv6鎖-CSDN
[4] xv6總體報告-百度文庫算法