跳到正文
神奇海星的Blog

oslab_2任务解析(sleep primes sixfive find&exec)

2 分钟 课程

sleep

两个重要参数

​ argc——参数的个数

​ argv——参数的数组

​ 示例:

$ find . 123 -exec echo

​ 此时argc = 5

​ argv[0]=“find”

​ argv[1]=“.”

​ argv[2]=“123”

​ argv[3]=“-exec”

​ argv[4]=“echo”

​ argv[5]=“0” ——结束符

atoi

​ 作用是将字符串格式的命令行参数转为整数

  • ​ 遇到任何非数字字符,循环立刻停止,返回已经累积的值
  • ​ 开头就是非数字 → 一次循环都不进 → 返回 0

*pasue的实现流程

​ pasue函数在user.h声明,在usys.S中切换到内核态指向sys_pause()函数

​ 通过时钟中断来“计数”,每0.1s执行clockintr()使得ticks+1,并且进行一次wakeup

​ wakeup后进程的state被设置为RUNNABLE

​ sleep函数就是把进程的state改为SLEEPING后使用sched()从当前进程切换到scheduler,使得当前进程处于sleep状态

​ 第一,“处于 sleep 状态”——被动变 RUNNABLE,不是永久睡眠。

​ 进程不是“睡着了就一直睡到时间到”,而是进入排队状态,每 0.1 秒被 wakeup 拍一下、重新排一次队。所以准确说是:反复“出队 → 检查 → 再排队”的循环

​ 第二,scheduler 只挑 RUNNABLE 的:

if(p->state == RUNNABLE) {
  p->state = RUNNING;
  c->proc = p;
  swtch(&c->context, &p->context);

​ SLEEPING 的进程会被这个 if 直接跳过。 这就是“睡眠”的实际效果——不被调度。state = SLEEPING 是一个“请不要调度我”的标记。

sleep 依赖 ticks 计数的循环:时钟中断每 0.1 秒把 ticks 加一并唤醒进程,直到 ticks - ticks0 达到 100 才退出

primes

p[0]/p[1] 的完整含义是

p[0] = 3  ──►  ofile[3] ──► struct file{ type:FD_PIPE, readable:1, writable:0, pipe: ─┐}
                                                                                        │
p[1] = 4  ──►  ofile[4] ──► struct file{ type:FD_PIPE, readable:0, writable:1, pipe: ─┤
                                                                                        ▼
                                                                          ┌──────────────────┐
                                                                          │ struct pipe      │
                                                                          │ data[512]        │
                                                                          │ nread / nwrite   │
                                                                          │ readopen=1       │
                                                                          │ writeopen=1      │
                                                                          └──────────────────┘

两个 fd 指向同一个内核 struct pipe 对象。 通信成立。

在 main 刚启动、fd编号只有 0/1/2 被占用

main: 写 2,3,4,…,40 到管道0,close(写端) ↓ sieve(rfd=3) ← 第一级,筛 2 ├─ read → 2 ├─ print “prime 2” ├─ pipe() → 新管道1 (p[0]=5, p[1]=6) ├─ fork() │ ├─ 子: close(3), close(6), sieve(5) ← 第二级,筛 3 │ └─ 父: close(5) ├─ 父循环: 读 3,4,5,…,40 │ ├─ 3%2≠0 → write(6, 3) ┐ │ ├─ 4%2=0 → 丢弃 │ 奇数流入下游 │ ├─ 5%2≠0 → write(6, 5) │ │ └─ … 39%2≠0 → write(6,39) ┘ ├─ read 返回 0(上游写端已关)→ 跳出循环 ├─ close(3), close(6) ← ★ 发 EOF 给下一级 ├─ wait(0) ← 等子进程跑完 └─ return → main exit(0)

子进程(筛 3): ├─ read(5) → 3, print “prime 3” ├─ pipe() → 管道2, fork() │ ├─ 孙: close(5), close(p[1]), sieve(p[0]) │ └─ 子: close(p[0]) ├─ 读 3,5,7,9,…,39 │ ├─ 3%3=0 → 丢弃 │ ├─ 5%3≠0 → write → 下游 │ └─ … ├─ close(5), close(写端), wait(0) └─ return → 回到父进程的 wait(0),子进程结束

sixfive

较简单,过!

find&exec

分为find和exec两个阶段

find通过递归找target文件是否存在,重点是T_DIR这个case

while(read(fd, &de, sizeof(de)) == sizeof(de))实现依次读这个目录下的文件夹/文件

exec通过在”-exec“后构建一个新的argv参数数组来执行其后面的命令: 构造 argv = g_cmd[0..n-1] + path + 0. g_cmd

是-exec后面的命令部分