如何做问卷调查网站,网站弄论坛形式怎么做,微信公众号里的小网站怎么做的,做社群最好的网站源码说明 
基于riscv64 soc  linux_5.10.4平台#xff0c;通过新增一个系统调用深入了解下系统调用实现原理。 
简介 
Linux 软件运行环境分为用户空间和内核空间#xff0c;默认情况下#xff0c;用户进程无法访问内核#xff0c;既不能访问内核所在的内存空间#xff0c;也不…说明 
基于riscv64 soc  linux_5.10.4平台通过新增一个系统调用深入了解下系统调用实现原理。 
简介 
Linux 软件运行环境分为用户空间和内核空间默认情况下用户进程无法访问内核既不能访问内核所在的内存空间也不能调用内核中的函数。为了给应用层提供系统支持Linux提供了一组系统调用接口用户可以通过调用它们访问linux内核的数据和函数。Linux系统调用实现原理是固定不同平台arm64riscv只是切换至内核态的汇编指令不同大致原理如下 
程序将系统调用参数填充到对应的平台通用寄存器。调用平台特定的汇编指令触发同步异常切换至内核态运行。内核初始化时已设置异常向量表应用层触发同步异常后CPU会跳到异常向量表对应的异常处理执行通常是一段平台相关的汇编代码。异常处理代码会检查系统调用号是否超出未超出再根据定义的系统调用表(sys_call_table)找到相应的系统调用函数入口地址执行后再通过汇编指令返回应用层。 
新增系统调用实现步骤如下 
修改系统调用表syscall_table新增一项。系统调用声明。系统调用实现。 
修改系统调用表 
系统调用表syscall_table定义如下 
// file: arch/riscv/kernel/syscall_table.c
#undef __SYSCALL
#define __SYSCALL(nr, call)     [nr]  (call),const void *sys_call_table[__NR_syscalls]  {[0 ... __NR_syscalls - 1]  sys_ni_syscall,
#include asm/unistd.h //通过unistd.h导入实际定义
};asm/unistd.h最终定义如下 
//file: include/uapi/asm-generic/unistd.h 
...
#define __NR_openat2 437     //系统调用openat2 编号
__SYSCALL(__NR_openat2, sys_openat2) //系统调用openat2 syscall_table项定义
#define __NR_pidfd_getfd 438
__SYSCALL(__NR_pidfd_getfd, sys_pidfd_getfd)
#define __NR_faccessat2 439
__SYSCALL(__NR_faccessat2, sys_faccessat2)
#define __NR_process_madvise 440
__SYSCALL(__NR_process_madvise, sys_process_madvise)#undef __NR_syscalls
#define __NR_syscalls 441 //系统调用表 项个数
...新增一项系统调用(mytest) 
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 15279e8d8..7df066dc5 100644
--- a/include/uapi/asm-generic/unistd.hb/include/uapi/asm-generic/unistd.h-860,8 860,11  __SYSCALL(__NR_faccessat2, sys_faccessat2)#define __NR_process_madvise 440__SYSCALL(__NR_process_madvise, sys_process_madvise)#define __NR_mytest 441
__SYSCALL(__NR_mytest, sys_mytest)
#undef __NR_syscalls
-#define __NR_syscalls 441
#define __NR_syscalls 442系统调用声明 
新增系统调用需要先声明否则内核编译时会报错找不到新增系统调用声明。 
//file: include/linux/syscalls.h
....
asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior);
asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec,size_t vlen, int behavior, unsigned int flags);
....
asmlinkage long sys_mytest(int id); //新增系统调用声明系统调用实现 
实现系统调用时不能像实现普通函数一样需要使用SYSCALL_DEFINE宏如系统调用madvise,定义时使用SYSCALL_DEFINE3宏宏展开后就是sys_madvise。 
//file: mm/madvise.c
SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
{                       return do_madvise(current-mm, start, len_in, behavior);
}int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int behavior)
{        ....//实际功能实现....
}SYSCALL_DEFINE 宏定义 
//file: include/linux/syscalls.h
#ifndef SYSCALL_DEFINE0         
#define SYSCALL_DEFINE0(sname)                                  \SYSCALL_METADATA(_##sname, 0);                          \asmlinkage long sys_##sname(void);                      \ALLOW_ERROR_INJECTION(sys_##sname, ERRNO);              \asmlinkage long sys_##sname(void)
#endif /* SYSCALL_DEFINE0 */#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__)系统调用最多支持6个参数1个参数使用SYSCALL_DEFINE12个参数使用SYSCALL_DEFINE2以此类推。 
新系统调用 
// file: mm/madvise.c ,随便找了一个文件保存代码
SYSCALL_DEFINE1(mytest, int, id)
{return id; //测试将id返回
}应用层测试 
编译并运行新内核后可运行应用层程序验证。 
int main(void) 
{int id  0;id  syscall(441, 100);printf(result : %d\n, id);return 0;
}~# ./mytest 
result : 100标准C库 
程序中调用的syscall来自标准C库根据源码可知应用层系统调用接口是封装的syscall。当前使用的标准C库muslsyscall源码如下 
//file: musl-1.2.1/arch/riscv64/syscall_arch.h
...
#define __asm_syscall(...) \__asm__ __volatile__ (ecall\n\t \: r(a0) : __VA_ARGS__ : memory); \return a0; \static inline long __syscall0(long n)
{register long a7 __asm__(a7)  n;register long a0 __asm__(a0);__asm_syscall(r(a7))
}
...
static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
{register long a7 __asm__(a7)  n;register long a0 __asm__(a0)  a;register long a1 __asm__(a1)  b;register long a2 __asm__(a2)  c;register long a3 __asm__(a3)  d;register long a4 __asm__(a4)  e;register long a5 __asm__(a5)  f;__asm_syscall(r(a7), 0(a0), r(a1), r(a2), r(a3), r(a4), r(a5))
}可知 
riscv64最终使用汇编指令ecall,触发同步异常切换至内核态执行。使用通用寄存器a7 存储系统调用编号和内核定义一致syscall支持0 ~ 6个参数__syscall0 ~ __syscall6 使用寄存器a0 ~ a5传递参数。 
ARM64实现原理也是一样不同的只是触发异常的指令svc以及通用寄存器的使用如下 
#define __asm_syscall(...) do { \__asm__ __volatile__ ( svc 0 \: r(x0) : __VA_ARGS__ : memory, cc); \return x0; \} while (0)static inline long __syscall0(long n)
{register long x8 __asm__(x8)  n;register long x0 __asm__(x0);__asm_syscall(r(x8));
}
...
static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
{register long x8 __asm__(x8)  n;register long x0 __asm__(x0)  a;register long x1 __asm__(x1)  b;register long x2 __asm__(x2)  c;register long x3 __asm__(x3)  d;register long x4 __asm__(x4)  e;register long x5 __asm__(x5)  f;__asm_syscall(r(x8), 0(x0), r(x1), r(x2), r(x3), r(x4), r(x5));
}总结 
系统调用是安全的执行时应用层没有访问内核空间。系统调用执行时应用层暂停切换至内核空间执行。系统调用执行时是通过平台相关的特定汇编指令触发同步异常riscv64是使用ecallaarch64是使用svc 0Intel CPU由中断0x80实现。 
CPU会跳转到对应的异常处理源码如下 
//file: arch/riscv/kernel/entry.S
....
ENTRY(handle_exception) //对应的异常处理....
check_syscall_nr:/* Check to make sure we dont jump to a bogus syscall number. */li t0, __NR_syscallsla s0, sys_ni_syscall/** Syscall number held in a7.* If syscall number is above allowed value, redirect to ni_syscall.*/bgeu a7, t0, 3f
#ifdef CONFIG_COMPATREG_L s0, PT_STATUS(sp)srli s0, s0, SR_UXL_SHIFTandi s0, s0, (SR_UXL  SR_UXL_SHIFT)li t0, (SR_UXL_32  SR_UXL_SHIFT)sub t0, s0, t0bnez t0, 1f/* Call compat_syscall */la s0, compat_sys_call_tablej 2f
1:
#endif/* Call syscall */la s0, sys_call_table
2:slli t0, a7, RISCV_LGPTRadd s0, s0, t0REG_L s0, 0(s0)
3:jalr s0ret_from_syscall:
....内核态调用对应的系统调用函数执行完后会退出内核态切换至用户态如上 ret_from_syscall。 
此过程 aarch64平台是由eret汇编指令实现和arm trustzone机制 bl31切换至非安全worldREE以及切换至安全worldbl32实现流程是一样的riscv64 平台具体指令暂不明。