# xv6 Kernel-16: Scheduling + swtch.S
#### `kernel/proc.c: yeild()`
`yeild()` 讓 `myproc()` 的 state 變成 `RUNNABLE`
並且呼叫 `sched()`,`sched()` 可以讓其他 function 也有機會可以被執行到
`yeild()` 想要做的事情是:
"我用 CPU 用夠久了,可以換其他人使用了"
所謂的 "換其他人使用" 也就是呼叫 `sched()`,它會幫我們處理
```Clike=
// Give up the CPU for one scheduling round.
void
yield(void)
{
struct proc *p = myproc();
acquire(&p->lock);
p->state = RUNNABLE;
sched();
release(&p->lock);
}
```
#### `kernel/proc.c: sched()` 解析
`sched()` 被以下幾個 function 呼叫:
* `kernel/proc.c: exit()`
* `kernel/proc.c: yeild()`
* `kernel/proc.c: sleep()`
從這裡可以發現,這都是在一些 "該輪到別人使用 CPU 了" 的時間點
所以可以推測 `sched()` 是用來決定下一個使用 CPU 的人是誰
不過讓我比較意外地點是,`scheduler()` 並沒有呼叫到 `sched()`,
那麼 `scheduler()` 到底扮演了什麼樣的角色呢
```C
// Switch to scheduler. Must hold only p->lock
// and have changed proc->state. Saves and restores
// intena because intena is a property of this
// kernel thread, not this CPU. It should
// be proc->intena and proc->noff, but that would
// break in the few places where a lock is held but
// there's no process.
void
sched(void)
{
int intena;
struct proc *p = myproc();
if(!holding(&p->lock)) // 我應該要 hold 我自己的 lock
panic("sched p->lock");
if(mycpu()->noff != 1) // TODO: why noff should be equals to 1 ?
panic("sched locks");
if(p->state == RUNNING) // sched() 的 caller 會先把 myproc() 設為 RUNNABLE
panic("sched running");
if(intr_get()) // TODO: learn sstatus first
panic("sched interruptible");
intena = mycpu()->intena; // why?
swtch(&p->context, &mycpu()->context);
mycpu()->intena = intena;
}
```
幾個困惑的點
* `swtch()` 實際上是如何運作的
* `mycpu()->context` 裡面到底裝著些什麼東西
* `mycpu()->context` 什麼時候會被初始化
* 為什麼 `mycpu()->noff` 應該要是 1
* `noff` 到底是為了什麼而存在?
* 被呼叫到的地方:
* `kernel/proc.c <<exit>>`
* `kernel/proc.c <<yield>>`
* `kernel/proc.c <<sleep>>`
* 最後會藉由 `swtch` 跑到 `scheduler()` 執行
### `schedul
```C
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing:
// - choose a process to run.
// - swtch to start running that process.
// - eventually that process transfers control
// via swtch back to the scheduler.
void
scheduler(void)
{
struct proc *p;
struct cpu *c = mycpu();
c->proc = 0;
for(;;){
// Avoid deadlock by ensuring that devices can interrupt.
intr_on();
// 所有的 process 都被 紀錄在 proc[] 這個 array 中
// scheduler() 負責依序把所有 RUNNABLE 的 process 拿來跑
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == RUNNABLE) { // 如果 這個
// Switch to chosen process. It is the process's job
// to release its lock and then reacquire it
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
swtch(&c->context, &p->context); // switch 在 sched() 也有被執行到
// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0; // 此時此刻這個 cpu 沒有執行任何的 process
}
release(&p->lock);
}
}
}
```
* 被呼叫到的地方:
* `kernel/main.c: main()` 的最後一行被執行到
* `sched()` 藉由 `swtch()` 跑來這裡
* 最大的功用就是確保每個 process 都有機會被執到
* 畢竟每個當下可能都有數個 process 正在執行當中
### `cpuid()`
```C
int
cpuid()
{
int id = r_tp();
return id;
}
```
### `mycpu()`
```C
struct cpu*
mycpu(void)
{
int id = cpuid();
struct cpu *c = &cpus[id];
return c;
}
```
* 利用 `cpuid()` 拿到 id 之後再去 `cpu[]` 中拿到 `struct cpu *c`
### `myproc()`
```C
struct proc*
myproc(void)
{
push_off();
struct cpu *c = mycpu();
struct proc *p = c->proc;
pop_off();
return p;
}
```
* 為什麼要 `push_off()` ?
* 就以行為上來說
* push 了幾次,就要 pop 幾次才可以回到 interrupt enable 的狀態
* 背後的動機?
* `push_off()`
* `intr_off()` // interrupt off
## `switch.S`
## 參考資料
[xv6 Kernel-16: Scheduling + swtch.S](https://www.youtube.com/watch?v=-O_JX5mMMHY&list=PLbtzT1TYeoMhTPzyTZboW_j7TPAnjv9XB&index=9)