☰
MIT 6.S081 util 实验篇(lab1):sleep (easy)
2026/10/6 7:49:38 网站建设 项目流程

sleep (easy)


实验目标

本实验是 6.S081 的"热身关",目的不在难度,而是先把手感建立起来:

  1. 熟悉 xv6 实验环境——git 分支怎么切、内核怎么构建、怎么运行、怎么调试、怎么评分。
  2. 写出第一个用户程序sleep,体会"用户程序如何通过系统调用向内核请求服务"。
  3. 跑通评分脚本,建立"写代码 → 改 Makefile → 编译 → 测试"这一整套开发闭环。

后续所有实验都建立在这套环境之上,所以这一关虽简单,但不建议跳过——把工具链跑顺,后面才不会被环境问题卡住。


前置知识

动手前,先把环境和几个核心概念理清楚。

1. 实验仓库结构

仓库里几个文件/目录后续会反复出现:

路径作用
kernel/操作系统内核空间源码,负责底层硬件管理与系统功能实现
user/用户空间应用程序源码,运行在内核提供的环境之上(我们写的sleep.c就放这里)
Makefile构建脚本,用来编译出 xv6 内核与所有用户程序镜像
grade-lab-<xxx>评分脚本,完成某个小实验或整个 lab 后运行它即可打分

2. 常用命令速查

切换到第一个实验分支(util):

gitcheckout util

开发与运行相关:

makeqemu# 构建并启动 xv6# 退出 xv6:先按 Ctrl+a,再按 xmakegrade# 给整个 lab 打分makeGRADEFLAGS=sleep grade# 只测 sleep 这一个子任务(util lab 中)

GDB 双终端调试:

# 终端 1:以单核 + gdb 方式启动 qemumakeCPUS=1qemu-gdb# 终端 2:连上 gdbriscv64-unknown-elf-gdb kernel/kernel

3. 系统调用与 sleep 的含义

sleep 实验的核心,是让你第一次亲手"调用一个系统调用"。这一点在 xv6 手册里有明确定位。

xv6 handout(Chapter 1: Operating system interfaces)
“xv6 provides the basic interfaces—system calls, pages, files, and directories—that Unix provides.”

手册系统调用一览表(Figure 1-1)中对 sleep 的描述是:
sleep(n): Pause for n clock ticks.

也就是说,sleep本质是一个系统调用:用户程序调用它,内核让当前进程挂起n个时钟滴答(tick)后再被唤醒。参数单位是 tick,qemu 下时钟约 100Hz,所以sleep 10大致暂停零点几秒(实际观感因人而异,把数字调大效果更明显)。

关键概念:用户程序自己没有"暂停自己"的权限——能不能睡、睡多久,必须由内核来裁决。这正是“系统调用 = 用户态请求内核服务的唯一合法入口”这个 OS 核心思想的第一个具象例子。


实现思路

顺着"写一个命令行工具并让它真正能用"的思路,只需两步:

  1. 在user/下新建sleep.c
    • 程序需要接收一个参数(要睡的 tick 数),所以先检查argc是否为 2,不对就打印用法并exit(1)。
    • 参数通过argv[1]传入,它是字符串,需要用atoi转成整数后传给sleep()。
    • 正常执行完调用exit(0)表示成功退出。
  2. 在Makefile的UPROGS里注册
    • 新写的用户程序不会被自动编译进内核镜像,必须手动把$U/_sleep加进UPROGS列表,否则sleep命令在 xv6 里根本不存在。

这一步虽小,却揭示了 xv6 一个贯穿全程的套路:任何用户程序 / 系统调用,都要在 Makefile 里"登记"才能被构建。后面 labs 加新程序、新系统调用时,这个动作会反复出现。


代码实现

user/sleep.c—— 用户态 sleep 命令

/* * user/sleep.c */#include"kernel/types.h"#include"user/user.h"intmain(intargc,char*argv[]){if(argc!=2){printf("usage: sleep <ticks>\n");exit(1);}sleep(atoi(argv[1]));exit(0);}

几点说明:

Makefile—— 把程序编进内核镜像

/* * Makefile */UPROGS=\ $U/_cat\ $U/_echo\ $U/_forktest\ $U/_grep\ $U/_init\ $U/_kill\ $U/_ln\ $U/_ls\ $U/_mkdir\ $U/_rm\ $U/_sh\ $U/_stressfs\ $U/_usertests\ $U/_grind\ $U/_wc\ $U/_zombie\ $U/_sleep\ # 添加 $U/_sleep\

验证

方式一:手动在 xv6 里测

makeqemu# 启动 xv6

进入 shell 后执行(数字大一点暂停更明显):

sleep10

观察程序是否真的暂停了相应时间,再正常回到 shell 提示符。

方式二:用评分脚本测

在 Linux 终端直接运行:

./grade-lab-utilsleep

应看到三个子测试全部通过:

==Test sleep, no arguments==sleep, no arguments: OK(1.6s)==Test sleep, returns==sleep, returns: OK(0.3s)==Test sleep, makes syscall==sleep, makes syscall: OK(0.9s)

三个测试分别验证了:无参数时的健壮性、执行后能正常返回、以及确实发起了sleep系统调用。

踩坑:python 找不到

如果运行脚本报:

/usr/bin/env: 'python': No such file or directory

原因是系统里只有python3没有python。把grade-lab-util文件开头的:

#!/usr/bin/env python

改成:

#!/usr/bin/env python3

保存后重新执行./grade-lab-util sleep即可。后续想测 util 里的其他小实验,把命令末尾的实验名换掉就行。


复盘

本实验解决了什么

表面上只是写了 20 行代码实现一个sleep命令,实际上它帮你完成了三件"基础设施"级别的事:

  1. 跑通了工具链——git 分支、make 构建、qemu 运行、gdb 调试、grade 评分,这一整套环境从此不再是黑盒。
  2. 建立了"系统调用"的第一手直觉——用户程序sleep()→ 陷入内核 → 内核让进程挂起,这是你第一次亲手走完"用户态请求内核服务"的完整动作。
  3. 记住了 xv6 的登记套路——新用户程序要在Makefile的UPROGS里注册,这个动作后面会不断重复。

收获

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询