Linux上系统调用参数的类型是什么?

时间:2016-02-25 13:40:02

标签: c linux-kernel system-calls

我想编写一个执行系统调用的泛型函数。像

这样的东西
long my_syscall2(long number, long arg1, long arg2);

我希望它尽可能便携。对于所有体系结构,实现明显不同。功能的签名是否也需要不同?我可以使用long还是应该使用别的东西?

以下是我找到的可能解决方案:

  • 内核使用一些dark magic :( __SYSCALL_DEFINEx调用__SC_LONG来获取类型,__SC_LONG包含魔法)。我听说用户空间中的类型并不总是与内核空间相同,所以我不知道我是否可以使用它。
  • musl-libc uses long for all architectures that it supports except x32 :(在[arch] /syscall_arch.h中定义)。
  • 我可以找到我想要支持的所有处理器体系结构和编译器的文档,查找寄存器大小和整数类型大小,并选择任何与寄存器大小相同的整数类型。

所以我猜这个问题是"是否有一些规则说“'系统调用参数的类型总是long,但有一些例外,例如x32'或者我是否需要查找每个架构和编译器的文档?"

编辑:我知道有些系统调用会将指针和其他类型作为参数。我想编写可以调用任何系统调用的泛型函数,使用泛型参数类型。这些通用参数类型应足够大,以容纳任何实际参数类型。我知道这是可能的,因为syscall()功能存在。

Edit2:以下是此问题的另一个部分解决方案。

这些功能的实现目前如下:

static __inline long my_syscall2(long number, long arg1, long arg2)
{
    unsigned long ret;
    __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(number), "D"(arg1), "S"(arg2)
                      : "rcx", "r11", "memory");
    return ret;
}

有趣的部分是"=a"(ret),这意味着存储在寄存器a中的系统调用返回值应该保存到变量ret中。使用syscall而不是编写创建局部变量的函数,而是将其返回值保存到变量中并返回变量,我可以编写一个宏来进行系统调用并将结果存储到调用者提供的变量中。它看起来像这样:

#define my_syscall2(RET, NUMBER, ARG1, ARG2) \
  __asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \
                      : "rcx", "r11", "memory");

它会像这样使用:

long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);

这样我就不需要知道足够大的寄存器大小和整数类型来保存寄存器的值。

3 个答案:

答案 0 :(得分:4)

系统调用参数在寄存器中传递。因此,大小限于CPU寄存器的大小。也就是说,32位架构上的32位,64位架构上的64位。浮点数不能以这种方式传递给内核。传统上,内核不使用浮点指令(并且可能无法在进入内核时保存FPU状态),因此请尽量避免在自己的系统调用中使用浮点数。

使用较小类型零或符号的参数的系统调用会扩展它们。使用较大参数类型的系统调用可能会将参数拆分为多个寄存器。

可以通过将参数作为指向结构的指针传递来实现具有许多参数的系统调用(如mmap()),但这具有可测量的性能开销,因此避免设计具有五个以上参数的系统调用

在一天结束时,使用适合您要发送的值的类型。让libc处理将数据放在正确的位置。

答案 1 :(得分:3)

我建议您使用现有的syscall系统调用,而不是尝试自己编写一个。{它似乎完全符合你的要求。请查看手册页的“体系结构特定要求”部分,以讨论您提出的有效问题。

答案 2 :(得分:1)

没有一般解决方案。如果你想让你的代码超多任务,你可以做类似的事情:

#if ARCH_WITH_32BIT_REGS
typedef uint32_t reg_size_int_t;
#elif ARCH_WITH_64BIT_REGS
typedef uint64_t reg_size_int_t;
#elif ARCH_WITH_16BIT_REGS
typedef uint16_t reg_size_int_t;
....
#endif

reg_size_int_t syscall_1( reg_size_t nr, reg_size_t arg0);
...

但是对于大多数常用的体系结构,寄存器的大小等于很长。