linux 系统调用

ARM工作模式

ARM处理器有7种工作模式,分别是用户模式、快速中断模式、外部中断模式、管理模式、数据访问终止模式、未定义指令终止模式和系统模式。 ‌
用户模式(User)
正常程序执行模式,权限最低,无法直接切换到其他模式。 ‌
快速中断模式(FIQ)
用于处理高速数据传输或通道中断,具有独立寄存器组以加快处理速度。 ‌
外部中断模式(IRQ)
处理通用外设中断请求,优先级低于FIQ。 ‌
管理模式(Supervisor/SVC)
操作系统内核使用的保护模式,复位或复位后进入此模式,用于系统初始化和管理。 ‌
数据访问终止模式(Abort)
处理存储器访问异常(如非法地址访问),支持虚拟内存和存储保护。 ‌
未定义指令终止模式(Undefined)
执行未定义指令时进入,可用于硬件协处理器的软件仿真。 ‌
系统模式(System)
运行特权级操作系统任务,与用户模式共享寄存器组,但具有更高权限。 ‌

用户态 linux arm处理器系统调用

Linux中,ARM64架构系统调用是使用管理模式实现的,使用svc #0指令触发系统调用,其核心流程如下:
指令触发
用户态程序通过svc #0(Supervisor Call)指令发起系统调用,参数通过AAPCS64规则传递:
系统调用号存于x8寄存器
参数按顺序存于x0~x5(最多6个)
返回值存于x0 ‌‌
svc #0触发EL0_SYNC异常

syscall定义

源码路径:bionic/libc/arch-arm64/bionic/syscall.S

ENTRY(syscall)
    /* Move syscall No. from x0 to x8 */
    mov     x8, x0
    /* Move syscall parameters from x1 thru x6 to x0 thru x5 */
    mov     x0, x1
    mov     x1, x2
    mov     x2, x3
    mov     x3, x4
    mov     x4, x5
    mov     x5, x6
    svc     #0

    /* check if syscall returned successfully */
    cmn     x0, #(MAX_ERRNO + 1)
    cneg    x0, x0, hi
    b.hi    __set_errno_internal

    ret
END(syscall)

系统调用号的定义

路径:bionic/libc/include/bits/glibc-syscalls.h

  #define SYS_brk __NR_brk
#endif
#if defined(__NR_cachectl)
  #define SYS_cachectl __NR_cachectl
#endif
#if defined(__NR_cacheflush)
  #define SYS_cacheflush __NR_cacheflush
#endif
#if defined(__NR_capget)
  #define SYS_capget __NR_capget
#endif
#if defined(__NR_capset)
  #define SYS_capset __NR_capset
#endif
#if defined(__NR_chdir)
  #define SYS_chdir __NR_chdir
#endif
#if defined(__NR_chmod)
  #define SYS_chmod __NR_chmod
#endif
#if defined(__NR_chown)
  #define SYS_chown __NR_chown
#endif
#if defined(__NR_chown32)
  #define SYS_chown32 __NR_chown32
#endif
#if defined(__NR_chroot)
  #define SYS_chroot __NR_chroot
#endif
#if defined(__NR_clock_adjtime)
  #define SYS_clock_adjtime __NR_clock_adjtime
#endif
#if defined(__NR_clock_getres)
  #define SYS_clock_getres __NR_clock_getres
#endif
#if defined(__NR_clock_gettime)
  #define SYS_clock_gettime __NR_clock_gettime
#endif
#if defined(__NR_clock_nanosleep)
  #define SYS_clock_nanosleep __NR_clock_nanosleep
#endif
#if defined(__NR_clock_settime)
  #define SYS_clock_settime __NR_clock_settime
#endif
#if defined(__NR_clone)
  #define SYS_clone __NR_clone
#endif
#if defined(__NR_close)
  #define SYS_close __NR_close
#endif
#if defined(__NR_connect)
  #define SYS_connect __NR_connect
#endif
#if defined(__NR_copy_file_range)
...

用户使用

在这里插入图片描述
用户直接使用syscall接口进行系统调用,第一个参数是调用号,后面是调用的具体参数最多不能超过6个。

内核态的实现

syscall调用号与执行函数的声明

内核syscall调用号与执行函数的声明:
源码路径:kernel/arch/arm64/include/asm/unistd32.h
在这里插入图片描述
这里截取部分系统调用号的设定。
对系统调用的统一管理:
源码路径:
kernel/arch/arm64/kernel/sys.c or sys32.c

void * const sys_call_table[__NR_syscalls] __aligned(4096) = {
	[0 ... __NR_syscalls - 1] = sys_ni_syscall,
#include <asm/unistd.h>
};

整个头文件被整个引用,作为数据成员数据。

回调函数的实现

在内核态 基本所有的系统调用都是由SYSCALL_DEFINEX宏定义实现,以下是SYSCALL_DEFINEX的代码定义:
代码路径:kernel/include/linux/syscalls.h

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define SYSCALL_DEFINE_MAXARGS  6

#define SYSCALL_DEFINEx(x, sname, ...)                          \
        SYSCALL_METADATA(sname, x, __VA_ARGS__)                 \
        __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...)                                 \
        asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))       \
                __attribute__((alias(__stringify(SyS##name))));         \
        static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));  \
        asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));      \
        asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))       \
        {                                                               \
                long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));  \
                __MAP(x,__SC_TEST,__VA_ARGS__);                         \
                __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));       \
                return ret;                                             \
        }                                                               \
        static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

以SYSCALL_DEFINE1(brk, unsigned long, brk)为例:
上面会定义几个以下接口:
sys_brk();
SYS_brk();
SYSC_brk()其中SYSC_brk会接管{}里面的内容。SYS_brk会调用到函数SYSC_brk,而sys_brk作为SYSC_brk的别名,为系统调用的入口。

内核对各种系统调用的对接

用户syscall与内核系统调用的对接在Entry.s文件里面实现
文件路径:kernel/arch/arm64/kernel/entry.S 系统调用会触发el0_svc函数的调用

el0_svc:
	adrp	stbl, sys_call_table		// 将sys_call_table加载到内存
	mov	wscno, w8					// 将系统调用号加载到wscno寄存器
	mov	wsc_nr, #__NR_syscalls	//将__NR_syscalls值加载到wsc_nr寄存器
el0_svc_naked:					// compat entry point
	stp	x0, xscno, [sp, #S_ORIG_X0]	// save the original x0 and syscall number
	enable_dbg_err_irq
	ct_user_exit 1

	ldr	x16, [tsk, #TSK_TI_FLAGS]	//加载当前task的flag
	tst	x16, #_TIF_SYSCALL_WORK
	b.ne	__sys_trace
	cmp     wscno, wsc_nr			// 比较调用号与支持的最大值
	b.hs	ni_sys						//大于等于跳转
	mask_nospec64 xscno, xsc_nr, x19	// enforce bounds for syscall number
	ldr	x16, [stbl, xscno, lsl #3]	// 具体的加载函数执行地址到x16寄存器
	//x16:目标寄存器,用于存储加载的系统调用处理函数地址
	//stbl:系统调用表基地址寄存器 sys_call_table
	//xscno:系统调用号寄存器,存储当前系统调用的编号
	//lsl #3:逻辑左移3位操作,相当于乘以8(64位系统指针大小为8字节)。因为sys_call_table是一个指针。如果调用	      //号为0那么直接调用sys_call_table数组下标为0的地址回调函数。如果为1那么就下找到下标为1的地址,也就是下一个指针地址:sys_call_table+8 .以此类推,调用号为N的函数地址就是为:sys_call_table + N * 8
	blr	x16				// 调用到具体的调用号的回调函数
	b	ret_fast_syscall
ni_sys:
	mov	x0, sp
	bl	do_ni_syscall
	b	ret_fast_syscall
ENDPROC(el0_svc)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值