Apa yang dapat membuat passing init = / path / ke / program ke kernel tidak memulai program sebagai init?

13

Saya mencoba men-debug skrip init pada sistem Linux; Saya mencoba meneruskan init=/bin/shke kernel untuk memulai shtanpa memulai initsehingga saya dapat menjalankan urutan init secara manual.

Apa yang saya temukan adalah kernelnya sudah mulai init. Selama bootup, salah satu pesan printk adalah baris perintah, dan itu menunjukkan bahwa baris sedang diatur dengan benar; selain itu, saya dapat mempengaruhi hal-hal lain menggunakan baris perintah kernel. Saya telah memeriksa untuk memastikan jalannya ada; ya.

Ini adalah sistem busybox, dan init adalah symlink ke busybox; jadi untuk memastikan busybox tidak melakukan sihir aneh ketika PID-nya adalah 1, saya juga mencoba menjalankan program non-busybox seperti init; itu juga tidak berhasil. Tampaknya apa pun yang saya lakukan, init dijalankan.

Apa yang bisa menyebabkan perilaku ini?

Shawn J. Goff
sumber
Untuk apa distro dasar yang menggunakan busybox init? Mereka mungkin mengabaikan baris perintah ... Anda mungkin ingin memeriksa initrd dan melihat apa yang sebenarnya dilakukan skrip.
Aaron D. Marasco
Ini bukan distro apa pun - ini adalah bangunan saya sendiri; itulah sebabnya saya mencoba men-debug skrip init.
Shawn J. Goff

Jawaban:

3

Melihat sumber kernel Linux, saya melihat bahwa jika file / init ada, kernel akan selalu berusaha untuk menjalankannya dengan asumsi bahwa ia melakukan boot ramdisk. Periksa sistem Anda untuk melihat apakah / init ada, jika ada, maka itu mungkin masalah Anda.

Kyle Jones
sumber
Sebenarnya, ia memeriksa execute_commandterlebih dahulu, yang berasal dari init=parameter baris perintah kernel . Jika tidak dapat menjalankannya, ia akan mencetak peringatan dan mencoba dijalankan initdi berbagai lokasi. Ini ada di init/main.cdalam fungsi init_post(). Saya melihat-lihat pesan printk kernel dan menemukan peringatan di output kernel saya, jadi sekarang saya harus mencari tahu mengapa itu tidak dapat memulai / bin / sh atau hal lain yang saya coba untuk mulai.
Shawn J. Goff
Kode yang saya lihat (v3.2.2 saya pikir) memeriksa set ramdisk_execute_command jika tidak disetel dan kemudian mencoba menjalankannya, jadi Anda tidak boleh seperti itu saat ini. Sayang sekali, karena saya tidak melihat hal lain yang akan menjelaskannya.
Kyle Jones
Anda harus menggunakan rdinitketika boot dari ramdisk rupanya: unix.stackexchange.com/a/430614/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
8

shenanigans initrd

Jika Anda menggunakan initrd atau initramfs, perhatikan hal berikut:

  • rdinit= digunakan sebagai ganti init=

  • jika rdinit=tidak diberikan, jalur bawaan berusaha adalah: /sbin/init, /etc/init, /bin/initdan /bin/shtapi tidak/init

    Ketika tidak menggunakan initrd, /initadalah jalan pertama dicoba, diikuti oleh yang lain.

v4.15 RTFS: semuanya ada di dalam file https://github.com/torvalds/linux/blob/v4.15/init/main.c .

Pertama kita pelajari itu:

  • execute_comand adalah apa pun yang diteruskan ke: init=
  • ramdisk_execute_command adalah apa pun yang diteruskan ke: rdinit=

seperti yang bisa dilihat dari:

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);

di mana __setupcara ajaib menangani parameter baris perintah.

start_kernel, "titik masuk" kernel, panggilan rest_init, yang "dipanggil" kernel_initdi utas:

pid = kernel_thread(kernel_init, NULL, CLONE_FS);

Lalu, kernel_initapakah:

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.");
}

dan kernel_init_freeabletidak:

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: mengerti sys_access.

Juga perhatikan bahwa ada perbedaan lebih lanjut antara init ram dan init non-ram, misalnya penanganan konsol: Perbedaan dalam pelaksanaan init dengan initramfs yang tertanam vs eksternal?

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber
0

Anda dapat mengkustomisasi kernel Linux Anda dan mengkompilasi ulangnya. Untuk kernel 4,9, Edit fungsi "kernel_init" di init / main.c dan coba jalankan baris berikut terlebih dahulu:

try_to_run_init_process("/bin/sh")

Selain itu, ini mungkin disebabkan oleh parameter kernel yang diteruskan oleh BootLoader.

banyak kamar
sumber