Linux下新增和使用系統調用

關鍵詞:__SYSCALL()、SYSCALL_DEFINEx()、syscall()等等。git

1. 爲何使用syscall

內核和用戶空間數據交換有不少種方式:sysfs、proc、信號等等。api

可是syscall效率要高於這些方式,使用起來也更加簡單。函數

缺點是可移植性差,對於新增的系統調用,須要內核和用戶空間同步。spa

2. 如何添加syscall

每一個系統調用都有一個系統調用號,這個系統調用號對應sys_call_table[]下標。code

經過sys_call_table[syscallid]就能夠對應到此係統調用的函數。blog

在include/uapi/asm/unistd.h中添加__NR_basetime系統調用號,關聯繫統調用號和系統調用函數。ci

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[]中的系統調用號和系統調用函數關聯起來。get

#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()的實現:string

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\n", timestamp);
        usleep(1000);
    }
}

用戶空間經過syscall()函數,觸發系統調用,使系統由用戶態陷入到內核態。

在系統個調用異常裏面獲取到系統調用號,以及必須的參數。根據系統調用號和sys_call_table[]找到對應系統調用函數。

以syscall()其他部分參數爲入參,執行相關結果。完成後返還給用戶空間。

相關文章
相關標籤/搜索