在 Linux 用户模式下退出 init 的正确方法
Correct way to exit init in Linux User Mode
我使用自定义 initrd 在用户模式下编译了 Linux 内核 5.6,创建方法如下:
mkdir initrd
cd initrd
mkdir bin dev etc home mnt proc sys usr
mknod dev/console c 5 1
与initrd/init.c
初始化文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
printf("init\n");
mount("none", "/proc", "proc", MS_MGC_VAL, "");
mount("none", "/sys", "sysfs", MS_MGC_VAL, "");
mount("none", "/dev", "devtmpfs", MS_MGC_VAL, "");
if (access("/dev/ubda", F_OK) != -1) {
printf("/dev/ubda exists\n");
} else {
printf("/dev/ubda not exists\n");
}
return EXIT_SUCCESS;
}
此程序检查 ubd0=...
选项传递的磁盘。
编译为 gcc -static -o init init.c
。
毕竟我编译了内核
make mrproper
make mrproper ARCH=um
make defconfig ARCH=um
make menuconfig ARCH=um
make linux ARCH=um
我更改了 .config
文件中的以下选项(使用 menuconfig)
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="initrd"
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_RD_LZ4=y
毕竟我尝试 运行 ./linux mem=32M
一切看起来都很好,除了退出
$ ./linux mem=32M
Core dump limits :
soft - NONE
hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking environment variables for a tempdir...none found
Checking if /dev/shm is on tmpfs...OK
Checking PROT_EXEC mmap in /dev/shm...OK
Adding 15400960 bytes to physical memory to account for exec-shield gap
Linux version 5.6.0 (root@d97bfdd0b529) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #1 Mon Apr 20 14:47:21 UTC 2020
Built 1 zonelists, mobility grouping on. Total pages: 11788
Kernel command line: mem=32M root=98:0
Dentry cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
Inode-cache hash table entries: 4096 (order: 3, 32768 bytes, linear)
mem auto-init: stack:off, heap alloc:off, heap free:off
Memory: 26524K/47808K available (2998K kernel code, 1137K rwdata, 956K rodata, 142K init, 167K bss, 21284K reserved, 0K cma-reserved)
NR_IRQS: 16
clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrating delay loop... 6966.47 BogoMIPS (lpj=34832384)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
Checking that host ptys support output SIGIO...Yes
Checking that host ptys support SIGIO on close...No, enabling workaround
devtmpfs: initialized
random: get_random_u32 called from bucket_table_alloc+0x118/0x141 with crng_init=0
umid_file_name : buffer too short
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: 0, 6144 bytes, linear)
NET: Registered protocol family 16
clocksource: Switched to clocksource timer
VFS: Disk quotas dquot_6.6.0
VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
TCP established hash table entries: 512 (order: 0, 4096 bytes, linear)
TCP bind hash table entries: 512 (order: 0, 4096 bytes, linear)
TCP: Hash tables configured (established 512 bind 512)
UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
NET: Registered protocol family 1
printk: console [stderr0] disabled
mconsole (version 2) initialized on /root/.uml/BolhBq/mconsole
Checking host MADV_REMOVE support...OK
workingset: timestamp_bits=62 max_order=13 bucket_order=0
io scheduler mq-deadline registered
io scheduler kyber registered
NET: Registered protocol family 17
9pnet: Installing 9P2000 support
Initialized stdio console driver
Console initialized on /dev/tty0
printk: console [tty0] enabled
Initializing software serial port version 1
printk: console [mc-1] enabled
Failed to initialize ubd device 0 :Couldn't determine size of device's file
epollctl add err fd 0, Operation not permitted
This architecture does not have kernel memory protection.
Run /init as init process
init
/dev/ubda not exists
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
CPU: 0 PID: 1 Comm: init Not tainted 5.6.0 #1
Stack:
6282bcb0 602f95fa 62826000 6038c6db
6006cd66 6001e201 6282bcc0 602f963f
6282bde0 6003c7ee 00412000 62a8bec0
Call Trace:
[<6006cd66>] ? printk+0x0/0x94
[<6003d5a8>] ? exit_mm+0x0/0x223
[<600214a8>] show_stack+0x13b/0x155
[<602f95fa>] ? dump_stack_print_info+0xe2/0xeb
[<6006cd66>] ? printk+0x0/0x94
[<602f963f>] dump_stack+0x2a/0x2c
[<6003c7ee>] panic+0x18c/0x3be
[<6003c662>] ? panic+0x0/0x3be
[<6008bbf6>] ? acct_collect+0x0/0x1df
[<60022177>] ? flush_tlb_page+0x12e/0x1f5
[<600672f2>] ? up_read+0x10/0x12
[<60067388>] ? __percpu_up_read+0x1a/0x1c
[<600455fd>] ? cgroup_threadgroup_change_end.isra.29+0x2e/0x30
[<6003d5a8>] ? exit_mm+0x0/0x223
[<6003de69>] do_exit+0x220/0x914
[<6003f33b>] sys_exit_group+0x0/0x16
[<6003f351>] __wake_up_parent+0x0/0x25
[<6002345b>] handle_syscall+0x79/0xa7
[<60036012>] userspace+0x483/0x510
[<600201fa>] new_thread_handler+0xb0/0xb2
如何正确退出?
init 进程绝不能退出。退出init的正确方式是shutdown。
#include <linux/reboot.h>
int main(int argc, char *argv[]) {
// ...
// return EXIT_SUCCESS;
sync();
reboot(LINUX_REBOOT_MAGIC1,
LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF, 0);
}
在 glibc 和大多数替代 libc(包括 uclibc、dietlibc、musl 和其他一些)下,一些涉及的常量已获得符号名称 RB_*,并且库调用是围绕系统调用的 1-argument 包装器:
#include <sys/reboot.h>
int main(int argc, char *argv[]) {
// ...
// return EXIT_SUCCESS;
sync();
reboot(RB_POWER_OFF);
}
对于停止或重新启动系统的 cmd 值,成功调用 reboot()
不会 return。
我使用自定义 initrd 在用户模式下编译了 Linux 内核 5.6,创建方法如下:
mkdir initrd
cd initrd
mkdir bin dev etc home mnt proc sys usr
mknod dev/console c 5 1
与initrd/init.c
初始化文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
printf("init\n");
mount("none", "/proc", "proc", MS_MGC_VAL, "");
mount("none", "/sys", "sysfs", MS_MGC_VAL, "");
mount("none", "/dev", "devtmpfs", MS_MGC_VAL, "");
if (access("/dev/ubda", F_OK) != -1) {
printf("/dev/ubda exists\n");
} else {
printf("/dev/ubda not exists\n");
}
return EXIT_SUCCESS;
}
此程序检查 ubd0=...
选项传递的磁盘。
编译为 gcc -static -o init init.c
。
毕竟我编译了内核
make mrproper
make mrproper ARCH=um
make defconfig ARCH=um
make menuconfig ARCH=um
make linux ARCH=um
我更改了 .config
文件中的以下选项(使用 menuconfig)
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="initrd"
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_RD_LZ4=y
毕竟我尝试 运行 ./linux mem=32M
一切看起来都很好,除了退出
$ ./linux mem=32M
Core dump limits :
soft - NONE
hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking environment variables for a tempdir...none found
Checking if /dev/shm is on tmpfs...OK
Checking PROT_EXEC mmap in /dev/shm...OK
Adding 15400960 bytes to physical memory to account for exec-shield gap
Linux version 5.6.0 (root@d97bfdd0b529) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #1 Mon Apr 20 14:47:21 UTC 2020
Built 1 zonelists, mobility grouping on. Total pages: 11788
Kernel command line: mem=32M root=98:0
Dentry cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
Inode-cache hash table entries: 4096 (order: 3, 32768 bytes, linear)
mem auto-init: stack:off, heap alloc:off, heap free:off
Memory: 26524K/47808K available (2998K kernel code, 1137K rwdata, 956K rodata, 142K init, 167K bss, 21284K reserved, 0K cma-reserved)
NR_IRQS: 16
clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrating delay loop... 6966.47 BogoMIPS (lpj=34832384)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
Checking that host ptys support output SIGIO...Yes
Checking that host ptys support SIGIO on close...No, enabling workaround
devtmpfs: initialized
random: get_random_u32 called from bucket_table_alloc+0x118/0x141 with crng_init=0
umid_file_name : buffer too short
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: 0, 6144 bytes, linear)
NET: Registered protocol family 16
clocksource: Switched to clocksource timer
VFS: Disk quotas dquot_6.6.0
VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
TCP established hash table entries: 512 (order: 0, 4096 bytes, linear)
TCP bind hash table entries: 512 (order: 0, 4096 bytes, linear)
TCP: Hash tables configured (established 512 bind 512)
UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
NET: Registered protocol family 1
printk: console [stderr0] disabled
mconsole (version 2) initialized on /root/.uml/BolhBq/mconsole
Checking host MADV_REMOVE support...OK
workingset: timestamp_bits=62 max_order=13 bucket_order=0
io scheduler mq-deadline registered
io scheduler kyber registered
NET: Registered protocol family 17
9pnet: Installing 9P2000 support
Initialized stdio console driver
Console initialized on /dev/tty0
printk: console [tty0] enabled
Initializing software serial port version 1
printk: console [mc-1] enabled
Failed to initialize ubd device 0 :Couldn't determine size of device's file
epollctl add err fd 0, Operation not permitted
This architecture does not have kernel memory protection.
Run /init as init process
init
/dev/ubda not exists
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
CPU: 0 PID: 1 Comm: init Not tainted 5.6.0 #1
Stack:
6282bcb0 602f95fa 62826000 6038c6db
6006cd66 6001e201 6282bcc0 602f963f
6282bde0 6003c7ee 00412000 62a8bec0
Call Trace:
[<6006cd66>] ? printk+0x0/0x94
[<6003d5a8>] ? exit_mm+0x0/0x223
[<600214a8>] show_stack+0x13b/0x155
[<602f95fa>] ? dump_stack_print_info+0xe2/0xeb
[<6006cd66>] ? printk+0x0/0x94
[<602f963f>] dump_stack+0x2a/0x2c
[<6003c7ee>] panic+0x18c/0x3be
[<6003c662>] ? panic+0x0/0x3be
[<6008bbf6>] ? acct_collect+0x0/0x1df
[<60022177>] ? flush_tlb_page+0x12e/0x1f5
[<600672f2>] ? up_read+0x10/0x12
[<60067388>] ? __percpu_up_read+0x1a/0x1c
[<600455fd>] ? cgroup_threadgroup_change_end.isra.29+0x2e/0x30
[<6003d5a8>] ? exit_mm+0x0/0x223
[<6003de69>] do_exit+0x220/0x914
[<6003f33b>] sys_exit_group+0x0/0x16
[<6003f351>] __wake_up_parent+0x0/0x25
[<6002345b>] handle_syscall+0x79/0xa7
[<60036012>] userspace+0x483/0x510
[<600201fa>] new_thread_handler+0xb0/0xb2
如何正确退出?
init 进程绝不能退出。退出init的正确方式是shutdown。
#include <linux/reboot.h>
int main(int argc, char *argv[]) {
// ...
// return EXIT_SUCCESS;
sync();
reboot(LINUX_REBOOT_MAGIC1,
LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_POWER_OFF, 0);
}
在 glibc 和大多数替代 libc(包括 uclibc、dietlibc、musl 和其他一些)下,一些涉及的常量已获得符号名称 RB_*,并且库调用是围绕系统调用的 1-argument 包装器:
#include <sys/reboot.h>
int main(int argc, char *argv[]) {
// ...
// return EXIT_SUCCESS;
sync();
reboot(RB_POWER_OFF);
}
对于停止或重新启动系统的 cmd 值,成功调用 reboot()
不会 return。