关键词:__SYSCALL()、SYSCALL_DEFINEx()、syscall()等等。
1. 为什么使用syscall
内核和用户空间数据交换有很多种方式:sysfs、proc、信号等等。
但是syscall效率要高于这些方式,使用起来也更加简单。
缺点是可移植性差,对于新增的系统调用,需要内核和用户空间同步。
2. 如何添加syscall
每个系统调用都有一个系统调用号,这个系统调用号对应sys_call_table[]下标。
通过sys_call_table[syscallid]就可以对应到此系统调用的函数。
在include/uapi/asm/unistd.h中添加__NR_basetime系统调用号,关联系统调用号和系统调用函数。
diff --git a/arch/csky/include/uapi/asm/unistd.h b/arch/csky/include/uapi/asm/unistd.h index 98e62b9..a1b6503 100644 --- a/arch/csky/include/uapi/asm/unistd.h +++ b/arch/csky/include/uapi/asm/unistd.h @@ -43,6 +43,11 @@ __SYSCALL(__NR_ugetrlimit, sys_getrlimit) #define __NR_sysfs (__NR_arch_specific_syscall + 5) __SYSCALL(__NR_sysfs, sys_sysfs) +#ifdef CONFIG_PERF_TIMER +#define __NR_basetime (__NR_arch_specific_syscall + 6) +__SYSCALL(__NR_basetime, sys_basetime) +#endif
__SYSCALL()将sys_call_table[]中的系统调用号和系统调用函数关联起来。
#undef __SYSCALL #define __SYSCALL(nr, call) [nr] = (call), #define sys_fadvise64_64 sys_csky_fadvise64_64 void * const sys_call_table[__NR_syscalls] __page_aligned_data = { [0 ... __NR_syscalls - 1] = sys_ni_syscall, #include <asm/unistd.h> };
在include/asm/syscalls.h中添加sys_basetime()引用。
#ifdef CONFIG_PERF_TIMER long sys_basetime(void); #endif
最后就是sys_basetime()的实现:
SYSCALL_DEFINE0(basetime) { return perf_timer_read_us(); }
2.1 SYSCALL_DEFINEx()
当SYSCALL_DEFINEx()的x为0时,很简单就是调用sys_##name()函数。
x为其他值时,同时定义了几个函数,并使用了别名属性。
#define SYSCALL_DEFINE0(sname) SYSCALL_METADATA(_##sname, 0); asmlinkage long sys_##sname(void) #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_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__))
3. 使用syscall
用户空间系统调用的使用通过syscall函数:
#define _GNU_SOURCE /* See feature_test_macros(7) */ #include <unistd.h> #include <sys/syscall.h> /* For SYS_xxx definitions */ long syscall(long number, ...);
第一个参数是系统调用号,后面的参数是内核对应sys_##name()函数一致的。
#include <stdio.h> #include <unistd.h> #define __NR_basetime 250 void main(void) { unsigned int timestamp = 0, i = 0; for(i = 0; i < 100; i++) { timestamp = syscall(__NR_basetime); printf("timestamp=%u ", timestamp); usleep(1000); } }
用户空间通过syscall()函数,触发系统调用,使系统由用户态陷入到内核态。
在系统个调用异常里面获取到系统调用号,以及必须的参数。根据系统调用号和sys_call_table[]找到对应系统调用函数。
以syscall()其余部分参数为入参,执行相关结果。完成后返还给用户空间。