用户层面 open
先说一个简单实验
int main(int argc,char *argv[])
{
int ret=0;
char buff[256]={0};
//用自己的open来打开文件
ret = open("testtest.c",O_RDONLY,0777);
printf("********************%d\n", ret);
read(ret,buff,30);
printf("*********************%s\n",buff);
close(ret);
return 0;
}
编译器的工作调用
摘自 https://blog.csdn.net/coldsnow33/article/details/12977139
io/fcntl.h中有open()的定义,这个open()就是应用程序的open啊。
extern int open (__const char *__file, int __oflag, ...) __nonnull ((1));
intl/loadmsgcat.c中继续找,原来是个宏;
# define open(name, flags) open_not_cancel_2 (name, flags)
sysdeps/unix/sysv/linux/not-cancel.h
#define open_not_cancel_2(name, flags) \
INLINE_SYSCALL (open, 2, (const char *) (name), (flags))
sysdeps/unix/sysv/linux/arm/sysdep.h找到这里看看INLINE_SYSCALL宏是个什么东西。
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...) \
({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); \
if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0)) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, )); \
_sys_result = (unsigned int) -1; \
} \
(int) _sys_result; })
# define INTERNAL_SYSCALL_RAW(name, err, nr, args...) \
({ \
register int _a1 asm ("r0"), _nr asm ("r7"); \
LOAD_ARGS_##nr (args) \
_nr = name; \
asm volatile ("swi 0x0 @ syscall " #name \
: "=r" (_a1) \
: "r" (_nr) ASM_ARGS_##nr \
: "memory"); \
_a1; })
swi是一条软中断指令,swi执行后处理器就会从usr模式切换到超级用户svc模式,svc也是一种异常模式。
发生swi 软中断要调用 el0_svc(ARM64)代码位置在 arch/arm64/kernel/entry.s
/*
* SVC handler.
*/
.align 6
el0_svc:
mov x0, sp
bl el0_svc_handler
b ret_to_user
ENDPROC(el0_svc)
/*
* EL0 mode handlers.
*/
.align 6
el0_sync:
kernel_entry 0
mrs x25, esr_el1 // read the syndrome register
lsr x24, x25, #ESR_ELx_EC_SHIFT // exception class
cmp x24, #ESR_ELx_EC_SVC64 // SVC in 64-bit state
b.eq el0_svc
cmp x24, #ESR_ELx_EC_DABT_LOW // data abort in EL0
b.eq el0_da
cmp x24, #ESR_ELx_EC_IABT_LOW // instruction abort in EL0
b.eq el0_ia
cmp x24, #ESR_ELx_EC_FP_ASIMD // FP/ASIMD access
b.eq el0_fpsimd_acc
cmp x24, #ESR_ELx_EC_SVE // SVE access
b.eq el0_sve_acc
cmp x24, #ESR_ELx_EC_FP_EXC64 // FP/ASIMD exception
b.eq el0_fpsimd_exc
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el0_sys
代码位置在 arch/arm64/kernel/syscall.c
sys_call_table为系统调用表
asmlinkage void el0_svc_handler(struct pt_regs *regs)
{
sve_user_discard();
el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table); //sys_call_table 为系统调用表
}
el0_svc_common
->invoke_syscall
-> __invoke_syscall(regs, syscall_fn)
syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)]; //sc_nr 是系统调用号, 也是 在进入软中断之前作为参数 传递的,通过这编号找到 open close ,red 等对应系统调用函数
static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
{
return syscall_fn(regs); //把参数传给系统调用,完成调用过程
}
现在我们写的程序 通过编译器编译 和 汇编指令,和系统接口函数对应上了
下面再说了 sys_call_table 怎么和下面的 sys_open 对上的,再说这个之前 必须提到 posix , API可以在各种不同的操作系统上实现给应用程序提供完全相同的接口,
而它们本身在这些系统上的实现却可能迥异,系统sys_call_table 也是按照posix排序实现系统调用的。
内核层面 open
fs/open.c文件里面有如下
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
if (force_o_largefile())
flags |= O_LARGEFILE;
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
在 include/linux/syscalls.h 里面有这个宏的定义
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
展开宏最终变成 sys_open
我们来说下 sys_open ( vfs 调用)