从linux 系统调用 学习可变参数宏的用法
2016-12-06 18:20
856 查看
转载自http://blog.chinaunix.net/uid-23069658-id-4106015.html?page=2
截取了作者原文中,自己比较关系你的一部分
这段时间又碰到系统调用这个家伙,结果在我当前用的3.0.x内核里全变样了。为了将这个问题弄明白,还得自己动手才行,这里非常感谢CSDN的"海风林影"兄弟提供的博文和相关参考文献,还是那句话“成果和荣耀归于前辈”。
很多人也都发现,在2.6.28及其之后的内核源码里,系统调用的写法发生了比较大的变化,出现了大量宏定义的代码。在源代码里,以前的诸如open()系统调用的sys_open()函数,现在仅仅能找到其声明,而其定义却“找不到”了。如果你把系统调用的宏展开后就会发现,以前的sys_open()依然安然无恙地躺在哪里。
这里我们仍然以open系统调用为例。在内核源码(当然这里的内核版本要大于等于2.6.28)fs目录下的open.c源文件里,我们会看到下面这样的代码:
点击(此处)折叠或打开
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
当我们用gcc -E参数把该定义所涉及的所有宏就地扩展之后,在我的x86_32平台上就会得到下面这样的结果:
点击(此处)折叠或打开
asmlinkage long sys_open(const char __user * filename, int flags, int mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
那么SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)是如何转换成asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode)形式的呢?其实就是宏的一些基本用法而已,我们来简单分析一下。在include/linux/syscall.h里有下面一组宏:
点击(此处)折叠或打开
#define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(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__)
这里每个SYSCALL_DEFINE后面的数字说明了,名为name的系统调用接收几个输入参数。这些宏都由一个基础宏SYSCALL_DEFINEx来实现:
点击(此处)折叠或打开
#define SYSCALL_DEFINEx(x, sname, ...) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
继续找__SYSCALL_DEFINEx宏的定义:
点击(此处)折叠或打开
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
关于__SC_DECL##x最后也被展开,其定义如下:
点击(此处)折叠或打开
#define __SC_DECL1(t1, a1) t1 a1
#define __SC_DECL2(t2, a2, ...) t2
a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3
a3, __SC_DECL2(__VA_ARGS__)
#define __SC_DECL4(t4, a4, ...) t4
a4, __SC_DECL3(__VA_ARGS__)
#define __SC_DECL5(t5, a5, ...) t5
a5, __SC_DECL4(__VA_ARGS__)
#define __SC_DECL6(t6, a6, ...) t6
a6, __SC_DECL5(__VA_ARGS__)
__SC_DECLx宏实现了系统调用输入参数类型和参数名的结合,因为宏定义时是无法确定参数类型的。我们来逐步分解一下open系统调用的实现:
截取了作者原文中,自己比较关系你的一部分
这段时间又碰到系统调用这个家伙,结果在我当前用的3.0.x内核里全变样了。为了将这个问题弄明白,还得自己动手才行,这里非常感谢CSDN的"海风林影"兄弟提供的博文和相关参考文献,还是那句话“成果和荣耀归于前辈”。
很多人也都发现,在2.6.28及其之后的内核源码里,系统调用的写法发生了比较大的变化,出现了大量宏定义的代码。在源代码里,以前的诸如open()系统调用的sys_open()函数,现在仅仅能找到其声明,而其定义却“找不到”了。如果你把系统调用的宏展开后就会发现,以前的sys_open()依然安然无恙地躺在哪里。
这里我们仍然以open系统调用为例。在内核源码(当然这里的内核版本要大于等于2.6.28)fs目录下的open.c源文件里,我们会看到下面这样的代码:
点击(此处)折叠或打开
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
当我们用gcc -E参数把该定义所涉及的所有宏就地扩展之后,在我的x86_32平台上就会得到下面这样的结果:
点击(此处)折叠或打开
asmlinkage long sys_open(const char __user * filename, int flags, int mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
那么SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)是如何转换成asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode)形式的呢?其实就是宏的一些基本用法而已,我们来简单分析一下。在include/linux/syscall.h里有下面一组宏:
点击(此处)折叠或打开
#define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(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__)
这里每个SYSCALL_DEFINE后面的数字说明了,名为name的系统调用接收几个输入参数。这些宏都由一个基础宏SYSCALL_DEFINEx来实现:
点击(此处)折叠或打开
#define SYSCALL_DEFINEx(x, sname, ...) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
继续找__SYSCALL_DEFINEx宏的定义:
点击(此处)折叠或打开
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
关于__SC_DECL##x最后也被展开,其定义如下:
点击(此处)折叠或打开
#define __SC_DECL1(t1, a1) t1 a1
#define __SC_DECL2(t2, a2, ...) t2
a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3
a3, __SC_DECL2(__VA_ARGS__)
#define __SC_DECL4(t4, a4, ...) t4
a4, __SC_DECL3(__VA_ARGS__)
#define __SC_DECL5(t5, a5, ...) t5
a5, __SC_DECL4(__VA_ARGS__)
#define __SC_DECL6(t6, a6, ...) t6
a6, __SC_DECL5(__VA_ARGS__)
__SC_DECLx宏实现了系统调用输入参数类型和参数名的结合,因为宏定义时是无法确定参数类型的。我们来逐步分解一下open系统调用的实现:
步骤 | 宏扩展内容 |
1 | SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) |
2 | SYSCALL_DEFINEx(3, _open, __VA_ARGS__) |
3 | __SYSCALL_DEFINEx(3, _open, __VA_ARGS__) |
4 | asmlinkage long sys_open(__SC_DECL3(__VA_ARGS__)) |
5 | asmlinkage long sys_open(const char __user *filename, __SC_DECL2(__VA_ARGS__)) |
6 | asmlinkage long sys_open(const char __user *filename, int flags, __SC_DECL1(__VA_ARGS__)) |
7 | asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode) |
相关文章推荐
- (转载) 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- Linux系统调用fork()用法详解
- linux mkfs命令参数及用法详解---linux格式化文件系统命令(包括swap分区)
- linux chkconfig命令参数及用法详解--linux系统服务设置命令
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- linux date命令参数及用法详解(linux查看修改系统时间)
- linux quotacheck命令参数及用法详解---Linux系统管理
- 【转载】在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- linux驱动系统调用ioctl()的cmd参数详解【字段划分图】
- (cc)2.6版本Linux上替换系统调用函数实现隐藏文件学习
- 学习Linux:用read()系统调用读取文件
- LINUX系统调用sys_mkdir()中所得的pathname参数不是全路径,要将其转为全路径的解决办法。
- Linux中last的用法及参数,查看登陆系统用户的信息
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 【转载】在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- linux date命令参数及用法详解--linux查看修改系统时间
- 2.6版本Linux上替换系统调用函数实现隐藏文件学习
- linux uname命令参数及用法详解(linux查看系统信息命令)
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- Linux内核学习之系统调用