你想要用“手动中断(int 指令)”的方式触发系统调用,而不是用 lcall
(调用门),这是完全可行的,而且在早期 Linux(如 int 0x80)中就是这么做的。
下面是你实现这类机制所需要的完整步骤,包括用户态和内核态的设计。
✅ 一、用户态:使用 int
指令触发中断
你可以写一个系统调用封装函数,比如:
static inline int sys_call(syscall_args_t *args) {
int ret;
__asm__ __volatile__ (
"int $0x80\n"
: "=a"(ret)
: "a"(args)
: "memory"
);
return ret;
}
📌 说明:
int $0x80
:触发中断向量 0x80;args
(系统调用参数结构体地址)通过eax
寄存器传给内核;- 返回值也通过
eax
返回。
✅ 二、内核态:中断向量 0x80 的处理逻辑
你需要设置 IDT 的 0x80 向量指向你自己的系统调用处理函数:
set_idt_gate(0x80, exception_handler_syscall, DPL_USER);
确保这个中断门设置的是:
- 权限允许用户态触发(DPL=3);
- 目标函数是
exception_handler_syscall
; - 类型为中断门或陷阱门。
✅ 三、异常处理入口:exception_handler_syscall
这段逻辑可以和你之前的调用门逻辑共用:
exception_handler_syscall:
pusha
push %ds
push %es
push %fs
push %gs
pushf
mov $(KERNEL_SELECTOR_DS), %eax
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
mov %esp, %eax ; 当前内核栈
push %eax
call do_handler_syscall
add $4, %esp
popf
pop %gs
pop %fs
pop %es
pop %ds
popa
iret
✅ 四、系统调用处理函数:do_handler_syscall
void do_handler_syscall(trapframe_t *tf) {
syscall_args_t *args = (syscall_args_t *)(tf->eax); // 从 eax 中取参数
int result = syscall_dispatch(args->id, args->arg0, args->arg1, args->arg2, args->arg3);
tf->eax = result; // 将结果返回给用户态(iret 后生效)
}
🔁 用户态回到原处
中断返回使用 iret
,CPU 会自动恢复 CS/EIP/EFLAGS,回到用户态的 int 0x80
之后的指令,用户态函数 sys_call()
获取返回值,结束。
✅ 总结:你需要改哪些地方?
位置 | 原 lcall 方式 | 新 int 方式 |
---|---|---|
用户态调用 | lcalll *(%[gate]) | int $0x80 |
参数传递 | 压栈 | eax 寄存器(可扩展用其他寄存器) |
内核入口 | 调用门目标函数 | IDT 中断向量 0x80 |
权限设置 | GDT 调用门 DPL=3 | IDT 中断门 DPL=3 |
返回方式 | lret | iret |
❗️进阶建议
你还可以将多个参数放到寄存器里(比如 eax、ebx、ecx、edx 等),模仿 Linux 的做法(int 0x80 调用约定),不用结构体传参。
如果你想我帮你设计一个更轻量的 int 调用 + 寄存器传参 + dispatch 表实现,我也可以写一个模板给你。要试试看吗?