SYSCALL_DEFINEx宏源码解析

流过昼夜 提交于 2020-01-22 19:34:10

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);

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!