在 linux0.11 中添加自定义系统调用

在 linux0.11 中添加自定义系统调用

linux 调用 system call 的过程

  1. 用户态发起系统调用

    在用户态程序中,调用系统调用的接口,通常是通过一个库函数(如 glibc)封装。这个库函数会触发 int 0x80 软中断。int 0x80 是 x86 平台用于进入内核态的中断指令。

  2. 进入内核态

    软中断会使 CPU 切换到内核态,并根据中断向量号(0x80)在中断向量表中查找对应的中断处理程序。对于 int 0x80,中断处理程序的入口位于内核的 system_call 函数中。

  3. 保存寄存器状态

    进入 system_call 函数时,CPU 会自动保存一部分寄存器状态(如 CSEIPEFLAGS 等),而内核会通过保存当前任务的寄存器状态(包括用户态的 pt_regs 结构),来保存调用前的 CPU 寄存器内容。

  4. 处理系统调用号和参数
    system_call 函数会从 eax 寄存器中读取系统调用号(用户态程序通过 int 0x80 前将系统调用号放入 eax),并根据该系统调用号在系统调用表(sys_call_table)中找到相应的内核态系统调用函数。用户态传递的参数通常存放在其他寄存器中,如 ebx, ecx, edx 等。

  5. 执行系统调用

    找到对应的系统调用函数后,内核会使用这些寄存器中的参数执行系统调用。系统调用完成后,返回值通常会放入 eax 寄存器中。

  6. 返回用户态

    系统调用完成后,内核会恢复用户态的寄存器状态(使用 pt_regs),并使用 iret 指令返回用户态。此时,用户态程序可以从 eax 中获取系统调用的返回值。

添加自定义的系统调用

修改系统调用总数:

bash 复制代码
cd ~/oslab/linux-0.11/kernel
vim system_call.s

向系统调用表中添加自定义的函数:

bash 复制代码
cd ~/oslab/linux-0.11/include/linux
vim sys.h

我添加了两个自定义的函数:

  • printIntVal(): 打印一个整数值。

  • strToNum(): 将一个数字字符串转换为整形。

添加自定义系统调用的声明和系统调用号定义

bash 复制代码
cd ~/oslab/linux-0.11/include
vim unistd.h

添加自定义系统调用的实现

bash 复制代码
cd ~/oslab/linux-0.11/kernel
vim selfdefine.c
c 复制代码
#include <asm/segment.h> // 包含与内核段寄存器相关的函数,如 get_fs_byte 和 put_fs_long

/**
 * 自定义系统调用 printIntVal,用于打印一个整数值。
 *
 * @param a - 要打印的整数值
 * @return 无返回值
 */
int printIntVal(int a)
{
	// 使用 printk 输出内核信息,打印传入的整数值
	printk("in system print int val: %d\n", a);
}

/**
 * 自定义系统调用 strToNum,将字符串转换为整数,并存储结果到内存中。
 *
 * @param str - 字符串的指针,从用户态传入的字符串。
 * @param str_len - 字符串的长度。
 * @param ret - 保存转换后结果的指针,返回转换的 long 类型整数。
 * @return 无返回值
 */
int strToNum(char *str, int str_len, long *ret)
{
	long result = 0; // 存储转换后的结果
	char tmp;		 // 用于存储每次从用户空间读取的字符
	int i;			 // 循环计数器

	// 遍历传入的字符串,将其转换为整数
	for (i = 0; i < str_len; i++)
	{
		// 使用 get_fs_byte 从用户空间读取字符串中的每个字符
		tmp = get_fs_byte(str + i);

		// 将字符转换为对应的数字,累加到 result 中
		// '0' 是字符的 ASCII 值,需要减去以获得实际的数字值
		result = result * 10 + tmp - '0';
	}

	// 使用 put_fs_long 将结果写回到用户空间中的 ret 指针指向的地址
	put_fs_long(result, ret);
}

写完自定义函数的实现之后记得将这个文件保存哦!

修改 makefile 文件

bash 复制代码
cd ~/oslab/linux-0.11/kernel
vim Makefile

我们需要修改两个地方:

  1. 在定义 OBJS 这里添加 selfdefine.o
  1. 添加依赖

编译 linux0.11 内核

bash 复制代码
cd ~/oslab/linux-0.11
make clean
make

我不知道怎么通过修改 linux-0.11 的源代码生成用户态的系统调用接口,我就在 linux-0.11 编译号之后的系统中手动添加了!

添加用户态接口

bash 复制代码
cd ~/oslab

我们运行 mount-hdc 这个可执行文件,可以让我们在 ubantu 系统上编辑 linux0.11 系统中的文件。

bash 复制代码
./mount-hdc

现在我们需要提供用户态的系统调用接口:

bash 复制代码
vim ./hdc/usr/include/selfdefine.h
c 复制代码
#ifndef _LINUX_SELFDEFINE_H
#define _LINUX_SELFDEFINE_H

// 定义系统调用号
#define __NR_printIntVal  72
#define __NR_strToNum   73

// 提供用户调用系统调用的接口

// 系统调用:printval
static inline int printIntVal(int val) {
    int res;
    asm volatile(
        "int $0x80"                       // 通过中断调用系统调用
        : "=a"(res)                       // 返回值保存在 eax 寄存器
        : "0"(__NR_printIntVal), "b"(val) // 系统调用号在 eax,参数在 ebx
        : "memory");
    return res;
}

// 系统调用:str2num
static inline int strToNum(char *str, int str_len, long *ret) {
    int res;
    asm volatile(
        "int $0x80"                                            // 通过中断调用系统调用
        : "=a"(res)                                            // 返回值保存在 eax 寄存器
        : "0"(__NR_strToNum), "b"(str), "c"(str_len), "d"(ret) // 参数传递
        : "memory");
    return res;
}

#endif /* _LINUX_SELFDEFINE_H */

这里有个问题就是用户态的接口为啥要这么写呢?其实就是模仿着 linux-0.11 的源代码来写的:

上面是 open 系统调用的用户态接口,是不是差不多嘛!

编辑完成之后记得要保存哦!

编写测试程序

现在你所在的目录应该还是:

bash 复制代码
~/oslab

我们来编写测试程序:

bash 复制代码
vim ./hdc/usr/root/test.c
c 复制代码
#include <stdio.h>
#include <selfdefine.h>
int main() {
    int val = 42;
    char str[] = "12345";
    long result;  // c 语言的早期标准,变量必须定义在函数的最前面
    printIntVal(val);
    strToNum(str, 5, &result);
    printf("Converted number: %ld\n", result);
    return 0;
}

编写好了之后保存!然后我们需要取消 hdc 目录的挂载!

umount hdc

运行测试

我们运行 run 程序

bash 复制代码
./run

编译 test.c

bash 复制代码
gcc test.c

运行 a.out

bash 复制代码
./a.out

可以看到成功了!

相关推荐
Ven%14 分钟前
centos查看硬盘资源使用情况命令大全
linux·运维·centos
萨格拉斯救世主1 小时前
戴尔R930服务器增加 Intel X710-DA2双万兆光口含模块
运维·服务器
Jtti1 小时前
Windows系统服务器怎么设置远程连接?详细步骤
运维·服务器·windows
TeYiToKu1 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws1 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
yeyuningzi1 小时前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
上辈子杀猪这辈子学IT2 小时前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian
minihuabei2 小时前
linux centos 安装redis
linux·redis·centos
EasyCVR2 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
lldhsds3 小时前
书生大模型实战营第四期-入门岛-1. Linux前置基础
linux