我们来学习一下 reboot
这个系统调用。它是一个底层的 Linux 系统调用,用于重启、关闭或挂起你的计算机 。
1. 函数介绍
reboot
系统调用(System Call)提供了直接与 Linux 内核交互以控制机器状态(如重启、关机)的接口 。顾名思义,它的主要作用是重新启动机器,但根据提供的参数,它也能执行关闭电源、挂起系统等操作 。这与用户通常在命令行使用的 reboot
命令不同,后者是一个更高级别的程序,内部可能会调用这个系统调用 。
2. 函数原型
#include <unistd.h>
#include <linux/reboot.h> // 包含定义的常量
// 注意:实际的系统调用接口通常带有前缀下划线
int __reboot(int magic, int magic2, unsigned int cmd, void *arg);
注意:直接使用 __reboot
系统调用比较复杂且不推荐。通常,标准 C 库(glibc)会提供一个更方便的包装函数 reboot
。
更常用的 glibc 包装函数原型:
#include <sys/reboot.h> // glibc 封装函数的头文件
int reboot(int cmd);
3. 功能
这个系统调用的核心功能是根据 cmd
参数的值来执行不同的系统级操作,主要包括重启系统、关闭系统电源、挂起系统或启用/禁用 Ctrl+Alt+Del 组合键的功能 。
4. 参数
对于 glibc 封装的 reboot(int cmd)
函数:
cmd
:int
类型。这个参数指定了要执行的具体操作。常见的值定义在sys/reboot.h
头文件中,例如:LINUX_REBOOT_CMD_RESTART
: 立即重启系统。LINUX_REBOOT_CMD_HALT
: 停止操作系统,但不关闭电源(挂起)。LINUX_REBOOT_CMD_POWER_OFF
: 停止操作系统并关闭电源(如果硬件支持)。LINUX_REBOOT_CMD_RESTART2
: 重启系统,并传递一个可选的命令行参数给内核(通过arg
参数,但这在 glibc 包装函数中不直接暴露)。LINUX_REBOOT_CMD_CAD_ON
: 允许使用 Ctrl+Alt+Del 组合键重启系统。LINUX_REBOOT_CMD_CAD_OFF
: 禁止使用 Ctrl+Alt+Del 组合键。
对于底层的 __reboot(int magic, int magic2, unsigned int cmd, void *arg)
系统调用:
magic
:int
类型。第一个“魔数”,必须是LINUX_REBOOT_MAGIC1
。这是为了防止意外调用系统调用 。magic2
:int
类型。第二个“魔数”,必须是LINUX_REBOOT_MAGIC2
或LINUX_REBOOT_MAGIC2A
/B
/C
中的一个 。同样是为了防止误操作 。cmd
:unsigned int
类型。指定要执行的操作,与 glibcreboot
的cmd
参数类似,但可能使用内核空间定义的常量(如LINUX_REBOOT_CMD_*
)。arg
:void *
类型。一个指向缓冲区的指针,用于传递附加参数,例如传递给内核的命令行字符串(当cmd
为LINUX_REBOOT_CMD_RESTART2
时)。
5. 返回值
- 成功: 对于
reboot(int cmd)
,如果调用成功,通常不会返回,因为系统会立即执行指定的操作(如重启或关机)。 - 失败: 返回 -1,并设置全局变量
errno
来指示错误原因,最常见的失败原因是权限不足(需要 root 权限)。
6. 相似函数或关联函数
reboot
命令: 用户空间的标准命令行工具,用于重启系统,内部可能调用reboot()
系统调用 。shutdown
命令: 更通用的关机/重启命令,允许指定时间、广播消息等,最终也可能调用reboot()
系统调用。poweroff
命令: 用于关闭系统电源,通常也是通过系统调用实现。halt
命令: 停止系统,行为取决于实现和参数,有时与poweroff
类似。sync
系统调用: 在执行reboot
之前,通常会先调用sync()
将所有未写入磁盘的数据刷新到磁盘,以防数据丢失。
7. 示例代码
注意: 运行此代码需要 root 权限,因为它执行的是特权操作。系统会立即重启,所以请确保保存所有工作。
#include <stdio.h>
#include <unistd.h> // 包含 sync
#include <sys/reboot.h> // 包含 reboot 函数和命令常量
#include <errno.h>
#include <string.h>
int main() {
printf("准备重启系统...\n");
// 1. 强烈建议在重启前先同步文件系统
// 这会将所有缓存中的数据写入磁盘,防止数据丢失
printf("正在同步文件系统...\n");
sync(); // 执行同步操作
// sync() 通常没有返回值用于检查错误,它尽力而为
// 2. 调用 reboot 系统调用 (通过 glibc 包装)
// 使用 LINUX_REBOOT_CMD_RESTART 命令重启系统
printf("正在调用 reboot 系统调用...\n");
if (reboot(LINUX_REBOOT_CMD_RESTART) == -1) {
// 如果返回 -1,表示调用失败
perror("reboot"); // 打印具体的错误原因
fprintf(stderr, "错误: 无法重启系统。请确保以 root 权限运行此程序。\n");
return 1; // 返回错误码
}
// 如果 reboot 调用成功,下面的代码通常不会被执行,
// 因为系统已经开始重启过程。
printf("系统调用已执行,但程序仍在运行?这很奇怪。\n");
return 0; // 理论上不会到达这里
}
编译和运行:
# 假设代码保存在 reboot_example.c 中
# 编译
gcc -o reboot_example reboot_example.c
# 运行 (需要 root 权限)
sudo ./reboot_example
再次提醒: 执行此程序会使您的计算机立即重启,请谨慎操作!