命令行调用流程
- 在系统中去使用
poweoff
,shutdown
,halt
等命令关机,其实都是是指向systemctl的软链接。
uos@uos-PC:~/Desktop$ file /usr/sbin/poweroff
/usr/sbin/poweroff: symbolic link to /bin/systemctl
uos@uos-PC:~/Desktop$ file /usr/sbin/shutdown
/usr/sbin/shutdown: symbolic link to /bin/systemctl
uos@uos-PC:~/Desktop$ file /usr/sbin/halt
/usr/sbin/halt: symbolic link to /bin/systemctl
- 通过dbus调用到
systemd-logind
进程中的某个方法
tatic int logind_schedule_shutdown(void) {
.....
r = sd_bus_call_method(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ScheduleShutdown",
&error,
NULL,
"st",
action,
arg_when);
.....
}
-
在
systemd-logind
中会去执行execute_shutdown_or_sleep
-
然后再通过dbus调用1号进程的
method_start_unit
方法,参数是poweroff.target
r = sd_bus_call_method(
m->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
error,
&reply,
"ss", unit_name, "replace-irreversibly");
- 以上执行完后会退出1号进程,执行
systemd-shutdown
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_OBJECTIVE_MAX] = {
[MANAGER_EXIT] = "exit",
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
[MANAGER_KEXEC] = "kexec",
};
log_notice("Shutting down.");
*ret_reexecute = false;
*ret_retval = m->return_value;
assert_se(*ret_shutdown_verb = table[m->objective]);
*ret_fds = NULL;
*ret_switch_root_dir = *ret_switch_root_init = NULL;
return 0;
}
static int become_shutdown(
const char *shutdown_verb,
int retval) {
.....
execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
.....
}
调用该二进制systemd-shutdown
,它的源码shutdow.c
log_info("Sending SIGTERM to remaining processes...");
broadcast_signal(SIGTERM, true, true, arg_timeout);
log_info("Sending SIGKILL to remaining processes...");
broadcast_signal(SIGKILL, true, false, arg_timeout);
/* Unmount all mountpoints, swaps, and loopback devices */
for (;;) {
bool changed = false;
if (use_watchdog)
watchdog_ping();
/* Let's trim the cgroup tree on each iteration so
that we leave an empty cgroup tree around, so that
container managers get a nice notify event when we
are down */
if (cgroup)
cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
if (need_umount) {
log_info("Unmounting file systems.");
r = umount_all(&changed, umount_log_level);
if (r == 0) {
need_umount = false;
log_info("All filesystems unmounted.");
} else if (r > 0)
log_info("Not all file systems unmounted, %d left.", r);
else
log_error_errno(r, "Failed to unmount file systems: %m");
}
if (need_swapoff) {
log_info("Deactivating swaps.");
r = swapoff_all(&changed);
if (r == 0) {
need_swapoff = false;
log_info("All swaps deactivated.");
} else if (r > 0)
log_info("Not all swaps deactivated, %d left.", r);
else
log_error_errno(r, "Failed to deactivate swaps: %m");
}
if (need_loop_detach) {
log_info("Detaching loop devices.");
r = loopback_detach_all(&changed, umount_log_level);
if (r == 0) {
need_loop_detach = false;
log_info("All loop devices detached.");
} else if (r > 0)
log_info("Not all loop devices detached, %d left.", r);
else
log_error_errno(r, "Failed to detach loop devices: %m");
}
if (need_dm_detach) {
log_info("Detaching DM devices.");
r = dm_detach_all(&changed, umount_log_level);
if (r == 0) {
need_dm_detach = false;
log_info("All DM devices detached.");
} else if (r > 0)
log_info("Not all DM devices detached, %d left.", r);
else
log_error_errno(r, "Failed to detach DM devices: %m");
}
if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
log_info("All filesystems, swaps, loop devices and DM devices detached.");
/* Yay, done */
break;
}
if (!changed && umount_log_level == LOG_INFO && !can_initrd) {
/* There are things we cannot get rid of. Loop one more time
* with LOG_ERR to inform the user. Note that we don't need
* to do this if there is a initrd to switch to, because that
* one is likely to get rid of the remounting mounts. If not,
* it will log about them. */
umount_log_level = LOG_ERR;
continue;
}
/* If in this iteration we didn't manage to
* unmount/deactivate anything, we simply give up */
if (!changed) {
log_info("Cannot finalize remaining%s%s%s%s continuing.",
need_umount ? " file systems," : "",
need_swapoff ? " swap devices," : "",
need_loop_detach ? " loop devices," : "",
need_dm_detach ? " DM devices," : "");
break;
}
log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
need_umount ? " file systems," : "",
need_swapoff ? " swap devices," : "",
need_loop_detach ? " loop devices," : "",
need_dm_detach ? " DM devices," : "");
}
(void) reboot(cmd);
if (errno == EPERM && in_container) {
/* If we are in a container, and we lacked
* CAP_SYS_BOOT just exit, this will kill our
* container for good. */
log_info("Exiting container.");
return EXIT_SUCCESS;
}
系统调用/usr/include/x86_64-linux-gnu/sys/reboot.h
#ifndef _SYS_REBOOT_H
#define _SYS_REBOOT_H 1
#include <features.h>
/* Perform a hard reset now. */
#define RB_AUTOBOOT 0x01234567
/* Halt the system. */
#define RB_HALT_SYSTEM 0xcdef0123
/* Enable reboot using Ctrl-Alt-Delete keystroke. */
#define RB_ENABLE_CAD 0x89abcdef
/* Disable reboot using Ctrl-Alt-Delete keystroke. */
#define RB_DISABLE_CAD 0
/* Stop system and switch power off if possible. */
#define RB_POWER_OFF 0x4321fedc
/* Suspend system using software suspend. */
#define RB_SW_SUSPEND 0xd000fce2
/* Reboot system into new kernel. */
#define RB_KEXEC 0x45584543
__BEGIN_DECLS
/* Reboot or halt the system. */
extern int reboot (int __howto) __THROW;
__END_DECLS
#endif /* _SYS_REBOOT_H */
Reboot系统调用的实现位于“kernel/reboot.c”,函数如下:
/*
* Reboot system call: for obvious reasons only root may call it,
* and even root needs to set up some magic numbers in the registers
* so that some mistake won't make this reboot the whole machine.
* You can also set the meaning of the ctrl-alt-del-key here.
*
* reboot doesn't sync: do that yourself before calling this.
*/
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
struct pid_namespace *pid_ns = task_active_pid_ns(current);
char buffer[256];
int ret = 0;
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;
/* For safety, we require "magic" arguments. */
if (magic1 != LINUX_REBOOT_MAGIC1 ||
(magic2 != LINUX_REBOOT_MAGIC2 &&
magic2 != LINUX_REBOOT_MAGIC2A &&
magic2 != LINUX_REBOOT_MAGIC2B &&
magic2 != LINUX_REBOOT_MAGIC2C))
return -EINVAL;
/*
* If pid namespaces are enabled and the current task is in a child
* pid_namespace, the command is handled by reboot_pid_ns() which will
* call do_exit().
*/
ret = reboot_pid_ns(pid_ns, cmd);
if (ret)
return ret;
/* Instead of trying to make the power_off code look like
* halt when pm_power_off is not set do it the easy way.
*/
if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
cmd = LINUX_REBOOT_CMD_HALT;
mutex_lock(&reboot_mutex);
switch (cmd) {
case LINUX_REBOOT_CMD_RESTART:
kernel_restart(NULL);
break;
case LINUX_REBOOT_CMD_CAD_ON:
C_A_D = 1;
break;
case LINUX_REBOOT_CMD_CAD_OFF:
C_A_D = 0;
break;
case LINUX_REBOOT_CMD_HALT:
kernel_halt();
do_exit(0);
panic("cannot halt");
case LINUX_REBOOT_CMD_POWER_OFF:
kernel_power_off();
do_exit(0);
break;
case LINUX_REBOOT_CMD_RESTART2:
ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
if (ret < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
#ifdef CONFIG_KEXEC_CORE
case LINUX_REBOOT_CMD_KEXEC:
ret = kernel_kexec();
break;
#endif
#ifdef CONFIG_HIBERNATION
case LINUX_REBOOT_CMD_SW_SUSPEND:
ret = hibernate();
break;
#endif
default:
ret = -EINVAL;
break;
}
mutex_unlock(&reboot_mutex);
return ret;
}
reboot
该系统调用的名称。
magic1、magic2
两个int类型的“魔力数”,用于防止误操作。
arg
其它的额外参数。
函数内部流程:
- 判断调用者的用户权限,如果不是超级用户(superuser),则直接返回错误(这也是我们再用户空间执行reboot、halt、poweroff等命令时,必须是root用户的原因。
- 判断传入的magicnumber是否匹配,如果不匹配,直接返回错误。这样就可以尽可能的防止误动作发生
- 调用reboot_pid_ns接口,检查是否需要由该接口处理reboot请求。
- 如果是POWER_OFF命令,且没有注册poweroff的machine处理函数(pm_power_off),把该命令转换为HALT命令。
- 根据具体的cmd命令,执行具体的处理,包括,如果是RESTART或者RESTART2命令,调用kernel_restart。如果是CAD_ON或CAD_OFF命令,更新C_A_D的值,表示是否允许通过Ctrl+Alt+Del组合键重启系统。如果是HALT命令,调用kernel_halt。如果是POWER_OFF命令,调用kernel_power_off。如果是KEXEC命令,调用kernel_kexec接口。如果是SW_SUSPEND,调用hibernate接口。
- 返回上述的处理结果,系统调用结束。