最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Why xv6 reacquire ptable.lock in scheduler() even when sleep() already holds it? - Stack Overflow

programmeradmin0浏览0评论

When learning about the sleep/wakeup() mechanism in xv6 (x86 version, .828/2018/xv6.html), I have a question regarding the usage the ptable.lock in sleep().

  1. sleep() acquire ptable.lock
  2. It then calls sched() to switch to scheduler, at this point, ptable.lock should be held (field .locked should be 1)
  3. However, in scheduler, inside the for loop, it first attempt to acquire ptable.lock, even though the lock should be already held, why does it need to acquire the lock again? and why does the acquisition can be succeed?
void sleep(void *chan, struct spinlock *lk)
{
  struct proc *p = myproc();
  ...
  if(lk != &ptable.lock){  //DOC: sleeplock0
    acquire(&ptable.lock);  // <---1
    release(lk);
  }

  // Go to sleep.
  p->chan = chan;
  p->state = SLEEPING;

  sched();  // <--- 2

  // Tidy up.
  p->chan = 0;

  // Reacquire original lock.
  if(lk != &ptable.lock){  //DOC: sleeplock2
    release(&ptable.lock);
    acquire(lk);
  }
}
void scheduler(void) {
  struct proc *p;
  struct cpu *c = mycpu();
  c->proc = 0;
  
  for(;;){
    ...
    // Loop over process table looking for process to run.
    acquire(&ptable.lock);  // <--- 3
    ...

When learning about the sleep/wakeup() mechanism in xv6 (x86 version, https://pdos.csail.mit.edu/6.828/2018/xv6.html), I have a question regarding the usage the ptable.lock in sleep().

  1. sleep() acquire ptable.lock
  2. It then calls sched() to switch to scheduler, at this point, ptable.lock should be held (field .locked should be 1)
  3. However, in scheduler, inside the for loop, it first attempt to acquire ptable.lock, even though the lock should be already held, why does it need to acquire the lock again? and why does the acquisition can be succeed?
void sleep(void *chan, struct spinlock *lk)
{
  struct proc *p = myproc();
  ...
  if(lk != &ptable.lock){  //DOC: sleeplock0
    acquire(&ptable.lock);  // <---1
    release(lk);
  }

  // Go to sleep.
  p->chan = chan;
  p->state = SLEEPING;

  sched();  // <--- 2

  // Tidy up.
  p->chan = 0;

  // Reacquire original lock.
  if(lk != &ptable.lock){  //DOC: sleeplock2
    release(&ptable.lock);
    acquire(lk);
  }
}
void scheduler(void) {
  struct proc *p;
  struct cpu *c = mycpu();
  c->proc = 0;
  
  for(;;){
    ...
    // Loop over process table looking for process to run.
    acquire(&ptable.lock);  // <--- 3
    ...
Share Improve this question edited Apr 11 at 7:55 Jacky Wang asked Mar 2 at 4:15 Jacky WangJacky Wang 3,5203 gold badges29 silver badges48 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

I think you've mixed up two functions with similar names. sleep() doesn't call scheduler(), it calls sched(). And you can see that sched() doesn't acquire the ptable lock; rather, it asserts that it is already held.


The reason scheduler() needs to acquire ptable.lock again, even though sleep() already had it, is because of what happens during the context switch.

When a process calls sleep(), it first acquires ptable.lock (if it wasn’t already held), marks itself as SLEEPING, and then calls sched(). Inside sched(), the process gives up the CPU, and during this switch, ptable.lock is released.

Now, when scheduler() runs, it needs to look at the process table to find the next runnable process. But since the lock was released when the previous process switched out, scheduler() has to acquire it again before scanning the process table.

So, even though ptable.lock was held earlier in sleep(), it gets released when the process is switched out, allowing scheduler() to successfully acquire it again. This prevents race conditions and ensures the process table isn’t modified by multiple processes at the same time.

发布评论

评论列表(0)

  1. 暂无评论