Ядро имеет основную функцию?

Я изучаю программирование Device Driver и Kernel. Согласно книге Джонатана Корбета, у нас нет функции main() в драйверах устройств.

#include <linux/init.h>
#include <linux/module.h>

static int my_init(void)
{
     return  0;
}

static void my_exit(void)
{
     return;
}

module_init(my_init);
module_exit(my_exit);

Здесь у меня есть два вопроса:

  • Почему нам не нужна функция main() в драйверах устройств?
  • У ядра есть функция main()?

Ответ 1

По сути, нет ничего особенного в том, что подпрограмма называется main(). Как указано выше, main() служит точкой входа для исполняемого модуля загрузки. Однако вы можете определить разные точки входа для модуля нагрузки. Фактически, вы можете определить более одной точки входа, например, обратиться к вашей любимой DLL.

С точки зрения операционной системы (ОС) все, что ей действительно нужно, это адрес точки входа кода, который будет функционировать как драйвер устройства. ОС передаст управление этой точке входа, когда драйвер устройства потребуется для ввода/вывода на устройство.

Системный программист определяет (каждая ОС имеет свой собственный метод) соединение между устройством, загрузочным модулем, который функционирует как драйвер устройства, и имя точки входа в загрузочном модуле.

Каждая ОС имеет свое собственное ядро ​​(очевидно), а некоторые могут/возможно начинаться с main(), но я бы с удивлением обнаружил ядро, которое использовало main(), а не простой, например UNIX! К тому моменту, когда вы пишете код ядра, вы давно пропустили требование назвать каждый модуль, который вы пишете как main().

Надеюсь, что это поможет?

Нашел этот фрагмент кода из ядра для Unix версии 6. Как вы можете видеть, main() - это еще одна программа, пытающаяся начать работу.

main()
{
     extern schar;
     register i, *p;
     /*
     * zero and free all of core
     */

     updlock = 0;
     i = *ka6 + USIZE;
     UISD->r[0] = 077406;
     for(;;) {
        if(fuibyte(0) < 0) break;
        clearsig(i);
        maxmem++;
        mfree(coremap, 1, i);
         i++;
     }
     if(cputype == 70) 
     for(i=0; i<62; i=+2) {
       UBMAP->r[i] = i<<12;
       UBMAP->r[i+1] = 0;
      }

    // etc. etc. etc.

Ответ 2

Несколько способов посмотреть на него:

  • Драйверы устройств не являются программами. Это модули, которые загружаются в другую программу (ядро). Таким образом, они не имеют функции main().

  • Тот факт, что все программы должны иметь функцию main(), применим только для приложений пользовательского пространства. Это не относится к ядру и драйверам устройств.

Ответ 3

С main() вы можете обозначить, что main() для программы, а именно ее "точка входа".

Для модуля init_module().

От Linux Device Driver 2nd Edition:

В то время как приложение выполняет одну задачу от начала до конца, модуль регистрируется для обслуживания будущих запросов, и его "основная" функция немедленно прекращается. Другими словами, задача функции init_module (точка входа модуля) заключается в подготовке для последующего вызова функций модуля; как будто модуль говорил: "Вот я, и это то, что я могу сделать". Вторая точка входа модуля, cleanup_module, вызывается непосредственно перед выгрузкой модуля. Он должен сказать ядру: "Меня больше нет, не просите меня сделать что-нибудь еще".

Ответ 4

start_kernel

В 4.2, start_kernel из init/main.c является значительным процессом инициализации и может быть сравнен с функцией main.

Это первый независящий от дуги код и устанавливает большую часть ядра.

Однако это не первый код ядра.

Как start_kernel вызывается в x86_64

arch/x86/kernel/vmlinux.lds.S, компоновщик script, устанавливает:

ENTRY(phys_startup_64)

и

phys_startup_64 = startup_64 - LOAD_OFFSET;

и

#define LOAD_OFFSET __START_KERNEL_map

arch/x86/include/asm/page_64_types.h определяет __START_KERNEL_map как:

#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)

который является адресом записи ядра. TODO, как именно этот адрес достиг? Я должен понять, что интерфейс Linux предоставляет загрузчикам.

arch/x86/kernel/vmlinux.lds.S устанавливает самый первый раздел загрузчика как:

.text :  AT(ADDR(.text) - LOAD_OFFSET) {
    _text = .;
    /* bootstrapping code */
    HEAD_TEXT

include/asm-generic/vmlinux.lds.h определяет HEAD_TEXT:

#define HEAD_TEXT  *(.head.text)

arch/x86/kernel/head_64.S определяет startup_64. Это самый первый код ядра x86, который запускается. Он выполняет множество настроек низкого уровня, включая сегментирование и подкачку.

Это первое, что запускается, потому что файл начинается с:

.text
__HEAD
.code64
.globl startup_64

и include/linux/init.h определяет __HEAD как:

#define __HEAD      .section    ".head.text","ax"

так же, как и самое первое из компоновщика script.

В конце он вызывает x86_64_start_kernel немного неудобно и lretq:

movq    initial_code(%rip),%rax
pushq   $0      # fake return address to stop unwinder
pushq   $__KERNEL_CS    # set correct cs
pushq   %rax        # target address in negative space
lretq

и

.balign 8
GLOBAL(initial_code)
.quad   x86_64_start_kernel

arch/x86/kernel/head64.c определяет x86_64_start_kernel, который вызывает x86_64_start_reservations, который вызывает start_kernel.

Ответ 5

Да, ядро ​​Linux имеет основную функцию, оно находится в файле arch/x86/boot/main.c. Но выполнение ядра начинается с файла arch/x86/boot/header.S и функции main() вызывается отсюда командой "calll main". Вот основная функция:

void main(void)
{
    /* First, copy the boot header into the "zeropage" */
    copy_boot_params();

    /* Initialize the early-boot console */
    console_init();
    if (cmdline_find_option_bool("debug"))
        puts("early console in setup code.\n");

    /* End of heap check */
    init_heap();

    /* Make sure we have all the proper CPU support */
    if (validate_cpu()) {
        puts("Unable to boot - please use a kernel appropriate "
             "for your CPU.\n");
        die();
    }

    /* Tell the BIOS what CPU mode we intend to run in. */
    set_bios_mode();

    /* Detect memory layout */
    detect_memory();

    /* Set keyboard repeat rate (why?) and query the lock flags */
    keyboard_init();

    /* Query Intel SpeedStep (IST) information */
    query_ist();

    /* Query APM information */
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
    query_apm_bios();
#endif

    /* Query EDD information */
#if defined(CONFIG_EDD) || defined(CONFIG_EDD_MODULE)
    query_edd();
#endif

    /* Set the video mode */
    set_video();

    /* Do the last things and invoke protected mode */
    go_to_protected_mode();
}

Ответ 6

В то время как имя функции main() является просто общим соглашением (нет реальной причины использовать его в режиме ядра), то в ядре linux есть функция main() для многих архитектур, и, конечно, linux-интерфейс linux имеет основную функция.       

Обратите внимание, что среда выполнения ОС загружает функцию main() для запуска приложения, когда загружается операционная система, нет времени выполнения, ядро ​​просто загружается на адрес загрузчиком, который загружается MBR, который загружается по аппаратным средствам. Поэтому, когда ядро ​​может содержать функцию main, она не должна быть точкой входа.

См. также:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633559%28v=vs.85%29.aspx

Источник ядра Linux:

x86: linux-3.10-rc6/arch/x86/boot/main.c

arm64: linux-3.10-rc6/arch/arm64/kernel/asm-offsets.c