私はLinuxシステムで初期化スクリプトをデバッグしようとしています。初期化シーケンスを手動で実行できるように、起動せずに起動するようにinit=/bin/sh
カーネルに渡したいと思います。sh
init
とにかくカーネルがinit
起動することがわかりました。起動中に、printkメッセージの1つはコマンドラインであり、これはその行が正しく設定されていることを示し、カーネルコマンドラインを使用して他のものにも影響を与える可能性があります。パスが存在することを確認しました。
これはビジボックスシステムであり、initはビジボックスへのシンボリックリンクなので、PIDが1のときにビジボックスが奇妙なことをしないようにするために、ビジボックス以外のプログラムもinitとして実行してみました。私が何をしても、initが実行されているようです。
この動作の原因は何ですか?
答え1
初期のいたずら
initrdまたはinitramfsを使用している場合は、次の点に注意してください。
rdinit=
代わりに使用init=
指定されていない場合、
rdinit=
試行されたデフォルトパスは、/sbin/init
とです/etc/init
。/bin/init
/bin/sh
/init
initrd を使用しない場合、
/init
最初のパスが試行され、別のパスが試行されます。
v4.15 RTFS:すべてが含まれていますhttps://github.com/torvalds/linux/blob/v4.15/init/main.c文書。
まず、次のことを学びました。
execute_comand
次に渡されます:init=
ramdisk_execute_command
次に渡されます:rdinit=
わかるように:
static int __init init_setup(char *str)
{
unsigned int i;
execute_command = str;
/*
* In case LILO is going to boot us with default command line,
* it prepends "auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("init=", init_setup);
static int __init rdinit_setup(char *str)
{
unsigned int i;
ramdisk_execute_command = str;
/* See "auto" comment in init_setup */
for (i = 1; i < MAX_INIT_ARGS; i++)
argv_init[i] = NULL;
return 1;
}
__setup("rdinit=", rdinit_setup);
__setup
コマンドライン引数を処理する魔法の方法はどこですか?
start_kernel
、カーネル「エントリポイント」はスレッドから「呼び出し」しますrest_init
。kernel_init
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
したがって、次のようkernel_init
になります。
static int __ref kernel_init(void *unused)
{
int ret;
kernel_init_freeable();
[...]
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
[...]
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/admin-guide/init.rst for guidance.");
}
そして、kernel_init_freeable
次のことを行います。
static noinline void __init kernel_init_freeable(void)
{
[...]
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
TODO:わかりましたsys_access
。
さらに、コンソール処理のように、ラム初期化と非ラム初期化との間には、より多くの違いがある。初期化を実行するための組み込みinitramfsと外部initramfsの違いは何ですか?
答え2
存在する
https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
私が見つけたもの:
通常のルートファイルシステムをデバッグするときは、「init = / bin / sh」で起動できることをお勧めします。 initramfsと同等のものは "rdinit = / bin / sh"であり、これは同じように便利です。
だからridinit = / bin / shを試してみてください。
答え3
Linuxカーネルのソースコードを見てみると、/initファイルが存在する場合、カーネルがRAMディスクブートを実行すると仮定し、常にそのファイルを実行しようとすることがわかりました。システムに/initがあることを確認してください。もしそうなら、これは問題かもしれません。
答え4
Linuxカーネルをカスタマイズして再コンパイルできます。 4.9カーネルの場合は、init / main.cで "kernel_init"関数を編集し、まず次の行を実行してみてください。
try_to_run_init_process("/bin/sh")
また、BootLoaderが渡したカーネルパラメータによっても発生する可能性があります。