GNU/Linux >> Tutoriels Linux >  >> Linux

Le noyau Linux a-t-il une fonction principale ?

start_kernel

Sur 4.2, start_kernel à partir de init/main.c est un processus d'initialisation considérable et pourrait être comparé à un main fonction.

C'est le premier code indépendant de l'architecture à s'exécuter et il configure une grande partie du noyau. Tellement comme main , start_kernel est précédé d'un code de configuration de niveau inférieur (fait dans le crt* objets dans userland main ), après quoi le code C générique "principal" s'exécute.

Comment start_kernel est appelé en x86_64

arch/x86/kernel/vmlinux.lds.S , un script de l'éditeur de liens, définit :

ENTRY(phys_startup_64)

et

phys_startup_64 = startup_64 - LOAD_OFFSET;

et :

#define LOAD_OFFSET __START_KERNEL_map

arch/x86/include/asm/page_64_types.h définit __START_KERNEL_map comme :

#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)

qui est l'adresse d'entrée du noyau. TODO comment cette adresse est-elle atteinte exactement ? Je dois comprendre l'interface que Linux expose aux chargeurs de démarrage.

arch/x86/kernel/vmlinux.lds.S définit la toute première section du chargeur de démarrage comme :

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

include/asm-generic/vmlinux.lds.h définit HEAD_TEXT :

#define HEAD_TEXT  *(.head.text)

arch/x86/kernel/head_64.S définit startup_64 . C'est le tout premier code du noyau x86 qui s'exécute. Il fait beaucoup de configuration de bas niveau, y compris la segmentation et la pagination.

C'est alors la première chose qui s'exécute car le fichier commence par :

.text
__HEAD
.code64
.globl startup_64

et include/linux/init.h définit __HEAD comme :

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

donc la même chose que la toute première chose du script de l'éditeur de liens.

À la fin, il appelle x86_64_start_kernel un peu maladroitement avec et 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

et :

.balign 8
GLOBAL(initial_code)
.quad   x86_64_start_kernel

arch/x86/kernel/head64.c définit x86_64_start_kernel qui appelle x86_64_start_reservations qui appelle start_kernel .

point d'entrée arm64

Le tout premier arm64 qui s'exécute sur un noyau v5.7 non compressé est défini sur https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72 donc soit le add x13, x18, #0x16 ou b stext en fonction de CONFIG_EFI :

    __HEAD
_head:
    /*
     * DO NOT MODIFY. Image header expected by Linux boot-loaders.
     */
#ifdef CONFIG_EFI
    /*
     * This add instruction has no meaningful effect except that
     * its opcode forms the magic "MZ" signature required by UEFI.
     */
    add x13, x18, #0x16
    b   stext
#else
    b   stext               // branch to kernel start, magic
    .long   0               // reserved
#endif
    le64sym _kernel_offset_le       // Image load offset from start of RAM, little-endian
    le64sym _kernel_size_le         // Effective size of kernel image, little-endian
    le64sym _kernel_flags_le        // Informative flags, little-endian
    .quad   0               // reserved
    .quad   0               // reserved
    .quad   0               // reserved
    .ascii  ARM64_IMAGE_MAGIC       // Magic number
#ifdef CONFIG_EFI
    .long   pe_header - _head       // Offset to the PE header.

Il s'agit également du tout premier octet d'une image de noyau non compressée.

Ces deux cas passent à stext qui démarre la "vraie" action.

Comme mentionné dans le commentaire, ces deux instructions sont les 64 premiers octets d'un en-tête documenté décrit sur :https://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call -l'image-du-noyau

arm64 première instruction MMU activée :__primary_switched

Je pense que c'est __primary_switched dans head.S :

/*
 * The following fragment of code is executed with the MMU enabled.
 *
 *   x0 = __PHYS_OFFSET
 */
__primary_switched:

À ce stade, le noyau semble créer des tables de pages + peut-être se déplacer de manière à ce que les adresses du PC correspondent aux symboles du fichier vmlinux ELF. Par conséquent, à ce stade, vous devriez pouvoir voir des noms de fonction significatifs dans GDB sans magie supplémentaire.

point d'entrée du processeur secondaire arm64

secondary_holding_pen défini sur :https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691

Procédure d'entrée décrite plus en détail sur :https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691


Avec main() vous voulez probablement dire quoi main() est à un programme, à savoir son "point d'entrée".

Pour un module init_module() .

À partir de la 2e édition du pilote de périphérique Linux :

Alors qu'une application exécute une seule tâche du début à la fin, un module s'enregistre pour servir les demandes futures, et sa fonction "principale" se termine immédiatement. En d'autres termes, la tâche de la fonction init_module (le point d'entrée du module) est de préparer l'invocation ultérieure des fonctions du module ; c'est comme si le module disait :« Me voici, et voici ce que je peux faire. Le deuxième point d'entrée d'un module, cleanup_module, est invoqué juste avant le déchargement du module. Il doit dire au noyau :"Je ne suis plus là ; ne me demandez pas de faire autre chose."


Fondamentalement, il n'y a rien de spécial à ce qu'une routine soit nommée main() . Comme mentionné ci-dessus, main() sert de point d'entrée pour un module de chargement exécutable. Cependant, vous pouvez définir différents points d'entrée pour un module chargeable. En fait, vous pouvez définir plus d'un point d'entrée, par exemple, vous référer à votre dll préférée.

Du point de vue du système d'exploitation (OS), tout ce dont il a vraiment besoin est l'adresse du point d'entrée du code qui fonctionnera comme un pilote de périphérique. Le système d'exploitation passe le contrôle à ce point d'entrée lorsque le pilote de périphérique est requis pour effectuer des E/S sur le périphérique.

Un programmeur système définit (chaque système d'exploitation a sa propre méthode) la connexion entre un périphérique, un module de chargement qui fonctionne comme le pilote du périphérique et le nom du point d'entrée dans le module de chargement.

Chaque système d'exploitation a son propre noyau (évidemment) et certains pourraient/peut-être commencer par main() mais je serais surpris de trouver un noyau qui utilise main() autre que dans un simple, comme UNIX ! Au moment où vous écrivez le code du noyau, vous avez depuis longtemps dépassé l'obligation de nommer chaque module que vous écrivez comme main() .

J'espère que cela vous aidera ?

Trouvé cet extrait de code du noyau pour Unix version 6. Comme vous pouvez le voir main() est juste un autre programme, essayant de démarrer !

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.

Plusieurs façons de le voir :

  1. Les pilotes de périphériques ne sont pas des programmes. Ce sont des modules qui sont chargés dans un autre programme (le noyau). En tant que tels, ils n'ont pas de main() fonction.

  2. Le fait que tous les programmes doivent avoir un main() La fonction n'est vraie que pour les applications de l'espace utilisateur. Cela ne s'applique pas au noyau, ni aux pilotes de périphériques.


Linux
  1. Linux - Pourquoi Linux affiche-t-il à la fois plus et moins de mémoire que ce que j'ai physiquement installé?

  2. Pourquoi une fonction main sans instruction return renvoie-t-elle la valeur 12 ?

  3. Qu'est-ce que cela signifie de dire que le noyau Linux est préemptif ?

  4. Comment le noyau Linux détermine-t-il l'ordre des appels __init ?

  5. Comment le noyau Linux se compare-t-il aux architectures de micro-noyau ?

Une liste de contrôle pour soumettre votre premier correctif de noyau Linux

Commande Dmesg sous Linux

Commande Sysctl sous Linux

Linux est-il un système d'exploitation ou un noyau ?

Le noyau Linux contre. Mac noyau

Comment Linux charge-t-il l'image 'initrd' ?