哈工大操作系统实验四:从零实现一个系统调用

1. 实验前的准备:理解系统调用的“桥梁”作用

大家好,我是你们的老朋友,一个在操作系统领域摸爬滚打了十多年的“老码农”。今天咱们不聊那些高大上的概念,就来实实在在地做一件事:在哈工大操作系统实验四(Lab4)里,从零开始,亲手给Linux 0.11这个“老古董”内核添加一个全新的系统调用。我知道,很多同学一听到“系统调用”、“内核修改”这些词就头大,感觉这是操作系统大神才能玩转的东西。别怕,当年我第一次做这个实验的时候,也踩了无数的坑,尤其是那个“挂载”操作,愣是让我对着黑屏命令行琢磨了一下午。所以,这篇文章我会用最“小白”的语言,把每一步都掰开了、揉碎了讲给你听,保证你看完就能上手,而且能避开我当年掉进去的那些“坑”。

首先,咱们得搞清楚,咱们到底要干嘛。系统调用是什么?你可以把它想象成用户程序和操作系统内核之间的一道“安检门”或者“服务窗口”。你写的普通程序(用户态程序)就像普通市民,而内核则是管理整个计算机资源的“政府大楼”。市民不能随便进大楼翻看文件、动用警力(硬件资源),他必须通过一个标准的“服务窗口”(系统调用)来提交申请。比如,你想在屏幕上打印一行字(printf),背后其实就是调用了 write 这个系统调用,由内核帮你把数据送到显示器。我们这个实验,就是要自己新开一个“服务窗口”,实现一个叫 iamwhoami 的功能。简单说,iam 就是告诉内核“我的名字是XXX”,whoami 就是问内核“刚才谁报过名了?”。听起来很简单对吧?但正是通过实现这个简单的功能,你能把用户态到内核态这条完整的通路彻底走通,这对理解操作系统的运作机制至关重要。

为了完成这个实验,你需要准备好实验环境。通常,哈工大的实验会提供一个叫“oslab”的Linux 0.11实验环境。你需要确保这个环境已经正确部署并能运行。关键的工具有:Bochs(一个模拟x86硬件环境的虚拟机,用来运行我们修改后的Linux 0.11)、GCC编译器(用来编译我们的测试程序)、以及一个文本编辑器(Vim或VS Code都可以)。在开始动手写代码之前,我强烈建议你先花点时间,在oslab目录下随便逛逛,熟悉一下几个关键目录:hdc/ 这是模拟的硬盘镜像挂载点,linux-0.11/ 这是内核源代码所在地。搞清楚它们的关系,后面才不会迷路。

2. 第一步:编写用户态的测试程序

万事开头难,但我们从最简单、最直观的用户程序开始。我们的目标是写两个小程序:iam.cwhoami.c。它们将运行在用户态,用来测试我们即将添加的系统调用。这里有一个超级大坑,我当年就栽在这里:这些用户程序应该写在哪里?怎么编译?

正确的路径是:oslab/hdc/usr/root/。但是,你直接进这个目录是看不到的,或者进去了也是空的。为什么?因为 hdc 目录实际上是一个“挂载点”。你可以把它理解成一个“共享文件夹”。我们的硬盘镜像文件(里面装着Linux 0.11的文件系统)平时是“锁起来”的,只有通过“挂载”操作,才能把这个镜像文件里的内容“映射”到 hdc 目录下,这样我们才能在宿主机(比如你的Ubuntu)上看到并修改里面的文件。等我们修改完,再“卸载”挂载,这些改动才会被真正写入镜像文件,然后被Bochs虚拟机读取。

所以,操作流程是这样的:

  1. oslab 目录下,执行 sudo ./mount-hdc。输入密码后,挂载就成功了。
  2. 此时,你再进入 oslab/hdc/usr/root/,就能看到里面可能有之前实验留下的文件了。我们就在这里创建 iam.cwhoami.c
  3. 写完代码,编译测试前,必须先回到 oslab 目录,执行 sudo umount hdc 卸载挂载。记住,Bochs虚拟机无法在挂载状态下启动! 这个顺序搞反了,虚拟机就会报错,这也是最常见的坑点之一。

现在来看代码。iam.c 的核心是调用一个还不存在的系统调用 iam

#define __LIBRARY__        // 必须定义这个宏,才能使用下面的 _syscallX 宏
#include <unistd.h>        // _syscallX 宏定义在这个头文件里
#include <stdio.h>

// 声明系统调用。_syscall1 表示这个系统调用有1个参数。
// 格式:_syscall1(返回值类型, 系统调用名, 参数1类型, 参数1变量名)
_syscall1(int, iam, const char*, name);

int main(int argc, char **argv) {
    int wlen = 0;
    // 检查是否传入了参数(程序名是argv[0],我们的名字应该是argv[1])
    if(argc < 2) {
        printf("Usage: %s <your_name>\n", argv[0]);
        return -1;
    }
    // 调用iam系统调用,将命令行第一个参数(名字)传递给内核
    wlen = iam(argv[1]);
    // 返回值是成功拷贝的字符数,我们把它打印出来
    printf("iam: copied %d characters.\n", wlen);
    return wlen;
}

whoami.c 类似,它调用 whoami 来获取内核中存储的名字。

#define __LIBRARY__
#include <unistd.h>
#include <stdio.h>

// _syscall2 表示这个系统调用有2个参数
_syscall2(int, whoami, char*, name, unsigned int, size);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值