Linux 内核函数 returns 1 而不是 EINVAL

Linux kernel function returns 1 instead of EINVAL

我正在尝试向 linux 内核添加一个新的系统调用:

asmlinkage long sys_set_status(int status) {
    if ((status != 0) && (status != 1))
        return -EINVAL; //-22
    current->status = status;
    return 0;
}

在syscall_64.tbl中声明:

334 common  set_status      sys_set_status

在syscalls.h中声明:

asmlinkage long sys_set_status(int status);

但是当我测试 return 值时:

int set_status(int status) {
    long r = syscall(334, status);
    return (int)r;
}

int main() {
    int res = set_status(-1); //illegal status, should return -EINVAL whish is -22
    cout << "return value is: " << res << endl;
    return 0;
}

我得到:

return value is: -1

long r = syscall(334, status);

来自man syscall

The return value is defined by the system call being invoked. In general, a 0 return value indicates success. A -1 return value indicates an error, and an error number is stored in errno.

不是直接调用系统调用,你是通过libcsyscall包装器调用它,它大致执行以下操作:

int syscall(num, ...)
{
  /* architecture-specific code to put system call number and args into
     appropriate registers */
  /* arch-specific code to execute the system call instruction */
  int rc = /* arch-specific code to get the result of the system call */ ;
  if (rc < 0) { errno = -rc; return -1; }
  return 0;
}

如果您不希望发生这种翻译,则必须自己执行 architecture-specific 部分(在汇编中),然后您将得到 actual 系统调用 return 值.

正如 Employed Russian 中指出的那样,来自系统调用的负 return 值被转换为 return 值-1 由 libcerrno 中的系统调用包装函数设置。所以 return 值 -1 是预期的,应该检查 errno 的值。

很可能 errno 将被设置为 ENOSYS 而不是 EINVAL 因为内核系统调用代码是用在大多数 64 位上工作的旧格式编写的内核。系统调用的内核代码应更新为使用 SYSCALL_DEFINE1 包装器宏(因为系统调用有 1 个参数),如下所示:

#include <linux/syscalls.h>
SYSCALL_DEFINE1(set_status, int, status)
{
    if ((status != 0) && (status != 1))
        return -EINVAL; //-22
    current->status = status;
    return 0;
}

编辑:实际上,如果这是问题所在,内核应该由于未定义的符号而无法 link。但是无论如何您都应该使用新格式(如果可能的话,对于 2009 年之前发布的内核可能不适用)。

我不确定这个系统调用的意义是什么。当系统调用返回用户空间时,任务状态应该设置回 0。