简介
进程使用标准库例程,库例程接下来调用内核函数,最终,由内核负责在各个请求进程之间公平而且流畅地共享资源和服务
用户态
#include <stdio.h>
int main()
{
FILE *fp = NULL;
// w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件
fp = fopen("test.txt", "w");
fprintf(fp, "test\n");
fclose(fp);
}
编译,追踪
uname -a
Linux 5.11.0-27-generic #29~20.04.1-Ubuntu SMP Wed Aug 11 15:58:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
gcc write_test.c -o write_test
ldd write_test # ldd 用于打印程序或者库文件所依赖的共享库列表
# 待补
svc
用户层进入内核态执行系统调用函数,通过异常方式(库函数完成),将当前系统调用函数的调用号放入x8
寄存器,然后使用svc
指令,发起同步异常。参考[1]
Supervisor Call causes an exception to be taken to EL1.On executing an SVC instruction, the PE records the exception as a Supervisor Call exception in ESR_ELx, using the EC value 0x15
, and the value of the immediate argument.
进入内核态
以下源码参考:linux-5.10
// arch/arm64/kernel/entry.S
// 中断向量入口:
SYM_CODE_START(vectors)
kernel_ventry 1, sync_invalid // Synchronous EL1t
kernel_ventry 1, irq_invalid // IRQ EL1t
kernel_ventry 1, fiq_invalid // FIQ EL1t
kernel_ventry 1, error_invalid // Error EL1t
kernel_ventry 1, sync // Synchronous EL1h
kernel_ventry 1, irq // IRQ EL1h
kernel_ventry 1, fiq_invalid // FIQ EL1h
kernel_ventry 1, error // Error EL1h
kernel_ventry 0, sync // Synchronous 64-bit EL0 ,同步异常处理入口: 包括系统调用
kernel_ventry 0, irq // IRQ 64-bit EL0
kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0
kernel_ventry 0, error // Error 64-bit EL0
// kernel_ventry 宏处理过程
.macro kernel_ventry, el, label, regsize = 64
.align 7
sub sp, sp, #S_FRAME_SIZE
b el\()\el\()_\label // 展开为: b el0_sync // 跳转到el0_sync
.endm
SYM_CODE_START_LOCAL_NOALIGN(el0_sync)
kernel_entry 0 // 保存用户态在寄存器数据
mov x0, sp
bl el0_sync_handler // el0_sync 处理函数
b ret_to_user
SYM_CODE_END(el0_sync)
找到系统调用函数
// arch/arm64/kernel/entry-common.c
asmlinkage void noinstr el0_sync_handler(struct pt_regs *regs) {
unsigned long esr = read_sysreg(esr_el1);
switch (ESR_ELx_EC(esr)) {
// arch/arm64/include/asm :
// #define ESR_ELx_EC_SVC64 (0x15)
case ESR_ELx_EC_SVC64:
el0_svc(regs);
break;
... // 其他异常
}
}
static void noinstr el0_svc(struct pt_regs *regs) {
...
do_el0_svc(regs);
}
// arch/arm64/kernel/syscall.c
void do_el0_svc(struct pt_regs *regs){
sve_user_discard();
// __NR_syscalls 系统调用总数
// sys_call_table 系统调用表 它每个系统调用的size是.long,即4byte
el0_svc_common(regs, regs