SYSCALL_DEFINEx宏源碼解析

1.定義
顯然SYSCALL_DEFINE1是系統調用的入口,其中1表示函數參數的個數,name表示系統調用函數的名字,同理下面的2,3,4,5,6表示參數個數。其具體定義位於linux-4.13.16\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__)

後面的展開中##是C語言定義的替換符號或連接符,_##name即將name變爲_name以作爲未來系統調用名稱的存根;__VA_ARGS__是可變參數宏,此處即代指…
2.SYSCALL_DEFINEx

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

顯然Marco SYSCALL_DEFINEx展開爲兩個宏,依次解析:

[1] SYSCALL_METADATA(sname, x,__VA_ARGS__)
#ifdef CONFIG_FTRACE_SYSCALLS
...                                                 \
#define SYSCALL_METADATA(sname, nb, ...)			\
...                                                 \
	static struct syscall_metadata __used			\
	  __syscall_meta_##sname = {				\
		.name 		= "sys"#sname,			\
		.syscall_nr	= -1,	/* Filled in at boot */	\
		.nb_args 	= nb,				\
		.types		= nb ? types_##sname : NULL,	\
		.args		= nb ? args_##sname : NULL,	\
		.enter_event	= &event_enter_##sname,		\
		.exit_event	= &event_exit_##sname,		\
		.enter_fields	= LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \
	};							\
	static struct syscall_metadata __used			\
	  __attribute__((section("__syscalls_metadata")))	\
	 *__p_syscall_meta_##sname = &__syscall_meta_##sname;
#else
#define SYSCALL_METADATA(sname, nb, ...)
#endif

易得該宏依賴於CONFIG_FTRACE_SYSCALLS內核配置選項。若該內核配置選項開啓,宏SYSCALL_METADATA 執行頭文件include/trace/syscall.h中syscall_metadata結構的初始化,該結構中包含多種有用字段例如系統調用的名稱,系統調用表中的編號、參數個數、參數類型列表等,本質是爲了添加跟蹤信息;若內核配置時 CONFIG_FTRACE_SYSCALLS 未開啓,此時宏 SYSCALL_METADATA擴展爲空字符串。

[2] __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

在正式解析該marco之前,先簡單介紹幾個Marco定義如下:

#define __SC_DECL(t, a)	t a         //該宏可以用作類型和參數組合
...
#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)//函數有幾個參數就調用對應的組合函數依次調用對應組合函數最終可得所有參數及其類型

下面是__SYSCALL_DEFINEx的實現:

#define __SYSCALL_DEFINEx(x, name, ...)					\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
    ...                         \
    ...                         \
	static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

只需關注第一項展開即可,其餘展開函數是__SYSCALL_DEFINEx生成的,此處無需深究。研究第一項函數asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)),其中__VA_ARGS__應當表示發起系統調用的函數的參數類型及值,比如說write函數的__VA_ARGS__應當有6項,包括int,fd, const void,*buf, size_t,count6項。而經過對上面__MAP宏的分析可以知道,其會將這6個參數重新組合成爲int fd, const void *buf, size_t count 這三項,進而作爲系統調用函數sys_write()的參數。
以上就是對系統調用入口宏SYSCALL_DEFINEx的剖析,下面就以read函數爲例進行實踐。
3.read函數
在linux-4.13.16\fs\read_write.c文件中其系統調用如下所示:
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count);
經過對宏展開後可得:
sys_read(unsigned int fd, char __user * buf, size_t count);

發佈了39 篇原創文章 · 獲贊 7 · 訪問量 4398
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章