【问题标题】:Linux reboot system call returns error despite correct arguments尽管参数正确,Linux 重启系统调用仍返回错误
【发布时间】:2026-02-24 07:00:01
【问题描述】:

我正在尝试使用原始的reboot 系统调用关闭我的机器。这是我的代码:

#include <unistd.h>
#include <linux/reboot.h>
#include <stdio.h>
#include <errno.h>

int main(void) {
    int ret = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
    if (ret == -1) {
        printf("error: %d\n", errno);
    }
    return 0;
}

根据手册页,“只有超级用户可以调用 reboot()”。所以我以root身份运行程序并得到以下输出:

alex@laptop:~/code$ sudo ./a.out 
[sudo] password for alex: 
error: 22

什么是错误 #22?那将是EINVAL,根据重新启动手册页,它表示“错误的幻数或 cmd”。当我使用#include &lt;linux/reboot.h&gt; 提供的定义时,我无法想象这些值是不正确的,但以防万一,我还尝试对手册页中提供的幻数值进行硬编码,但无济于事(同样的错误)。

但是,如果我尝试由&lt;sys/reboot.h&gt; 提供的glibc 的包装函数reboot(LINUX_REBOOT_CMD_POWER_OFF),它可以正常工作(立即关闭机器)。

我想这样做并没有实际的理由,只是在玩系统调用并尝试使其工作。如果有帮助,这里有一些关于我正在运行的系统的详细信息:

操作系统: Ubuntu 20.04
内核: Linux 版本 5.4.0-53-generic
编译器: gcc 版本9.3.0

如果有任何其他信息可能有用,请告诉我!

【问题讨论】:

  • 你真的需要阅读 glibc 包装器的源代码来了解如何做到这一点 :) 一切都在那里。或者查看该系统调用的内核源代码。
  • EINVAL 表示“无效参数”

标签: c linux


【解决方案1】:

我从这个链接得到这个代码:Reboot with and without glibc

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#ifdef NO_GLIBC
#include <linux/reboot.h>
#else
#include <sys/reboot.h>
#endif

#ifdef NO_GLIBC
#ifndef LINUX_REBOOT_MAGIC1
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#endif

#ifndef LINUX_REBOOT_MAGIC2
#define LINUX_REBOOT_MAGIC2 0x28121969
#endif

#ifndef LINUX_REBOOT_MAGIC2A
#define LINUX_REBOOT_MAGIC2A 0x05121996
#endif

#ifndef LINUX_REBOOT_MAGIC2B
#define LINUX_REBOOT_MAGIC2B 0x16041998
#endif

#ifndef LINUX_REBOOT_MAGIC2C
#define LINUX_REBOOT_MAGIC2C 0x20112000
#endif
#endif /* NO_GLIBC */

#ifndef LINUX_REBOOT_CMD_RESTART
#define LINUX_REBOOT_CMD_RESTART 0x1234567
#endif

#ifndef LINUX_REBOOT_CMD_HALT
#define LINUX_REBOOT_CMD_HALT 0xcdef0123
#endif

#ifndef LINUX_REBOOT_CMD_POWER_OFF
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
#endif

#ifndef LINUX_REBOOT_CMD_RESTART2
#define LINUX_REBOOT_CMD_RESTART2 0xa1b3c3d4
#endif

#ifndef LINUX_REBOOT_CMD_CAD_ON
#define LINUX_REBOOT_CMD_CAD_ON 0x89abcdef
#endif

#ifndef LINUX_REBOOT_CMD_CAD_OFF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#endif

static void do_reboot(int command);

int
main(int argc, char *argv[]) {
  int reboot_command;

  if(argc == 1) {
    fprintf(stderr, "No command given.\n");
    fprintf(stderr, "Commands: RESTART, HALT, POWER_OFF, CAD_ON,
CAD_OFF.\n");
    return(2);
  } else if(strcasecmp(argv[1], "RESTART") == 0) {
    reboot_command = LINUX_REBOOT_CMD_RESTART;
  } else if(strcasecmp(argv[1], "HALT") == 0) {
    reboot_command = LINUX_REBOOT_CMD_HALT;
  } else if(strcasecmp(argv[1], "POWER_OFF") == 0) {
    reboot_command = LINUX_REBOOT_CMD_POWER_OFF;
  } else if(strcasecmp(argv[1], "RESTART2") == 0) {
    fprintf(stderr, "RESTART2 not supported. Try RESTART.\n");
    return(1);
  } else if(strcasecmp(argv[1], "CAD_ON") == 0) {
    reboot_command = LINUX_REBOOT_CMD_CAD_ON;
  } else if(strcasecmp(argv[1], "CAD_OFF") == 0) {
    reboot_command = LINUX_REBOOT_CMD_CAD_OFF;
  } else {
    fprintf(stderr, "%s not supported.\n", argv[1]);
    fprintf(stderr, "Commands: RESTART, HALT, POWER_OFF, CAD_ON,
CAD_OFF.\n");
    return(2);
  }

  do_reboot(reboot_command);

  // Not reached, unless command was CAD_ON or CAD_OFF.
  return(0);
}

static void
do_reboot(int cmd) {
  int reboot_status = 0;

  // Flush filesystem buffers before rebooting.
  sync();

  #ifdef NO_GLIBC
  // Old libc.
  reboot_status = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd,
NULL);
  #else
  // glibc uses a wrapper around the system call.
  reboot_status = reboot(cmd);
  #endif

  if(reboot_status == -1) {
    if(errno == EPERM) {
      fprintf(stderr, "Permission denied.  Are you root?\n");
    }
  }
}

从这段代码我们可以了解到rebootoldlibc中的格式是:
int reboot(int magic, int magic2, unsigned int cmd, void *arg);
glibcreboot 的格式为:int reboot(int cmd);

错误 22 的原因:此错误暗示“无效参数”(EINVAL)。 glibc 需要 reboot(int cmd) 函数调用中的单个参数(glibc 使用系统调用的包装器),但我们提供了多个参数。这就是错误的原因。

请看上面代码中的函数调用:

#ifdef NO_GLIBC
  // Old libc.
  reboot_status = reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd,
NULL);
  #else
  // glibc uses a wrapper around the system call.
  reboot_status = reboot(cmd);
  #endif

【讨论】:

  • 我明白了,所以有一个旧的 libc 包装器,带有我正在尝试使用的签名,但这不起作用,因为我实际上正在使用 glibc?这是有道理的。谢谢!
【解决方案2】:

虽然我无法使 int reboot(int magic, int magic2, int cmd, void *arg); 系统调用 API 工作,但我最终能够使用较低级别的通用系统调用 API 执行此系统调用,如下所示:

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

int main(void) {
    syscall(169, 0xfee1dead, 672274793, 0x01234567);
    return 0;
}

可以使用手册页man reboot.2 解释幻数。这是等效的 x86_64 程序集:

section .text

global _start

_start:
  mov rax, 169
  mov rdi, 0fee1deadh
  mov rsi, 672274793
  mov rdx, 001234567h
  syscall

  ; The code below will never execute
  mov rax, 60
  mov rdi, 0
  syscall

我最初的目标是从程序集中执行这个系统调用,所以虽然我仍然很好奇为什么重启功能不起作用,但我对我所拥有的感到满意。

【讨论】:

  • 也就是说,如果有人知道为什么我的重启电话失败了,我仍然很想听听!
  • 您写道“glibc 的包装函数 ... 工作正常”。这意味着reboot 解析为包装器,因为在 C 中不能有两个同名的函数。另一个是系统调用,您必须将其作为系统调用调用。
  • @dxiv 我明白你的意思了。我试图使用的函数只是具有不同签名的旧版本的包装器。谢谢!
【解决方案3】:

所以看起来需要实际值。

正如下面的手册页所述,您希望按如下方式使用幻数;

重启((int) 0xfee1dead, 672274793, 0x4321fedc); // 就像你上面所说的那样关闭电源

https://man7.org/linux/man-pages/man2/reboot.2.html

【讨论】:

  • 嗨尼克,欢迎来到 *!使用您的更改运行代码,我得到完全相同的错误。我在原始帖子中使用的定义评估为您说要使用的相同幻数。出于好奇,您的代码在运行时是否适合您?
最近更新 更多