锁部分: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总体报告-百度文库算法