DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Uncategorized»Analyse du mécanisme d’initialisation automatique – DZone
    Uncategorized

    Analyse du mécanisme d’initialisation automatique – DZone

    février 28, 2023
    Analyse du mécanisme d'initialisation automatique - DZone
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    RT-Thread est un système d’exploitation en temps réel embarqué open source avec de riches composants de niveau intermédiaire et un excellent écosystème matériel et logiciel, le tout offrant un support fantastique pour l’industrie de l’Internet des objets. Depuis sa création en 2006, RT-Thread a alimenté 1,5 milliard d’appareils, notamment des appareils portables, des appareils électroménagers intelligents, l’électronique automobile, l’électronique médicale, l’électronique grand public, l’énergie et de nombreuses autres industries.

    Le 15 mars, RT-Thread s’est associé à NXP Semiconductors, STMicroelectronics et WCHElectronics pour proposer un webinaire IoT : Power the IoT Devices. Inscrivez-vous gratuitement ici.

    Revenons au mécanisme d’auto-initialisation.

    Initialisation générale

    Lors du développement embarqué, nous adoptons principalement cette approche pour initialiser un périphérique.

    int main(int argc, char *argv[])
    {
        clk_init();
        led_init();
        beep_init();
        key_init();
        .....
        while(1)
        {
            ...
        }
    }
    

    L’ordre de cette initialisation est relativement clair, et il est assez facile de déterminer les périphériques qui ont été initialisés et l’ordre dans lequel ils sont initialisés. Cependant, le main est particulièrement lourde, surtout lorsqu’il y a beaucoup de périphériques à initialiser.

    Auto-Initialisation

    Programmation C sur l’ordinateur pour imprimer un hello world

    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        printf("hello world\r\n");
        return 1;
    }
    

    Ici, nous pouvons utiliser directement printf imprimer sans aucune étape d’initialisation ; cette idée conduit à la RT-Thread mécanisme d’initialisation automatique.

    RT-Thread Auto-Initialisation :

    int led_init()
    {
        ...
    }
    INIT_APP_EXPORT(led_init);
    int main(int argc, char *argv[])
    {
        led_on();
        rt_kprintf("hello rt thread\r\n");
        return 1;
    }
    

    L’idée centrale de l’initialisation automatique est que l’initialisation de chaque périphérique est terminée avant de s’exécuter sur le main fonction, et tous les périphériques peuvent être utilisés directement dans le main fonction. Par exemple, le programme ci-dessus utilise directement rt_kprintf pour la sortie et allumé le LED.

    API auto-initialisée

    L’auto-initialisé API intercepté de la RT-Thread code source, comme illustré ci-dessous :

    /* board init routines will be called in board_init() function */
    #define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")
    /* pre/device/component/env/app init routines will be called in init_thread */
    /* components pre-initialization (pure software initilization) */
    #define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
    /* device initialization */
    #define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
    /* components initialization (dfs, lwip, ...) */
    #define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
    /* environment initialization (mount disk, ...) */
    #define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
    /* appliation initialization (rtgui application etc ...) */
    #define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")
    

    API la liste des fonctions est présentée dans le tableau suivant

    Analyse du mécanisme

    Fonction INIT_EXPORT

    En voyant les fonctions d’initialisation, nous apprenons que leur dernier appel est le INIT_EXPORT fonction. Seuls les paramètres saisis sont différents. Voyons la définition de cette fonction :

    #define INIT_EXPORT(fn, level)                                                       \
        RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
    

    Le INIT_EXPORT() la fonction a deux paramètres ; le premier paramètre indique quelle fonction doit être initialisée, délivrant le pointeur de fonction (le nom de la fonction), et le second paramètre indique dans quel segment placer le pointeur de fonction. Passons ensuite à la macro, et il y a plusieurs conditions préalables à connaître avant de passer à la macro.

    • RT_USED
    • #définir l’attribut RT_USED((utilisé))

    Le attribute__(used) fonction a été marquée dans le fichier objet pour empêcher l’éditeur de liens de supprimer les sections inutilisées.

    • type init_fn_t
    • typedef int (*init_fn_t)(void);

    Ici, une valeur de retour de int est défini un type de pointeur de fonction avec le paramètre de fonction void et renommé en init_fn_t.

    ## appartient au langage C, et son rôle est de combiner deux symboles de langage en un seul symbole de langage

    • SECTION
    • #définir l’attribut SECTION(x)((section(x)))

    __attribute__((section(name))) place les fonctions fonctionnelles ou les données dans un segment d’entrée spécifié nommé name

    Avec la sauvegarde préliminaire ci-dessus, analysons les éléments suivants INIT_EXPORT macro. Développez la macro comme ci-dessous :

    RT_USED const init_fn_t __rt_init_fn SECTION(".rti_fn." level) = fn

    La fonction de cette macro est d’affecter le pointeur à la fonction fn à la variable __rt_init_fnce type de variable est RT_USED const init_fn_tet il est stocké dans le segment spécifié .rti_fn.level. Ainsi, une fois la fonction exportée à l’aide d’une macro d’initialisation automatique, les pointeurs vers chaque fonction d’initialisation seront stockés dans ces segments de données. Lorsque nous déréférencons, ces pointeurs seront pris au fur et à mesure que nous exécutons la fonction correspondante.

    Division des segments

    Les segments sont divisés en component.cet le code source est le suivant :

    static int rti_start(void)
    {
        return 0;
    }
    INIT_EXPORT(rti_start, "0");
    static int rti_board_start(void)
    {
        return 0;
    }
    INIT_EXPORT(rti_board_start, "0.end");
    static int rti_board_end(void)
    {
        return 0;
    }
    INIT_EXPORT(rti_board_end, "1.end");
    static int rti_end(void)
    {
        return 0;
    }
    INIT_EXPORT(rti_end, "6.end");
    

    La répartition des segments exportés ci-dessus à l’aide de la INIT_EXPORT macro est indiquée dans le tableau suivant :

    La distribution des segments exportés ci-dessus à l'aide de la macro INIT_EXPORT

    Après avoir ajouté les six segments exportés après l’initialisation automatique, la distribution de chaque segment est indiquée dans le tableau suivant :

    Après avoir ajouté les six segments qui sont exportés après l'initialisation automatique, la distribution de chaque segment est affichée

    Fonction rt_components_board_init

    Tête à vérifier sur la mise en œuvre de la rt_components_board_init fonction:

    void rt_components_board_init(void)
    {
    #if RT_DEBUG_INIT
        int result;
        const struct rt_init_desc *desc;
        for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
        {
            rt_kprintf("initialize %s", desc->fn_name);
            result = desc->fn();
            rt_kprintf(":%d done\n", result);
        }
    #else
        volatile const init_fn_t *fn_ptr;
        for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
        {
            (*fn_ptr)();
        }
    #endif
    }

    Si ce n’est pas le cas, considérez le RT_DEBUG_INITil est clair de constater que le rt_components_board_init exécute ce qui suit :

    volatile const init_fn_t *fn_ptr;
    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
    

    Le code ci-dessus définit un fn_ptr pointeur, qui est déréférencé lorsque la plage du pointeur est dans la plage de __rt_init_rti_board_start et rt_init_rti_board_end, où le pointeur est le pointeur de fonction mis lors de l’initialisation automatique, c’est donc tout à fait l’exécution de la fonction. Autrement dit, la fonction exportée par INIT_BOARD_EXPORT(fn) est exécuté.

    Fonction rt_components_init

    Code source:

    void rt_components_init(void)
    {
    #if RT_DEBUG_INIT
        int result;
        const struct rt_init_desc *desc;
        rt_kprintf("do components initialization.\n");
        for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
        {
            rt_kprintf("initialize %s", desc->fn_name);
            result = desc->fn();
            rt_kprintf(":%d done\n", result);
        }
    #else
        volatile const init_fn_t *fn_ptr;
        for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
        {
            (*fn_ptr)();
        }
    #endif
    }

    Si ce n’est pas le cas, considérez le RT_DEBUG_INITil est clair de constater que le rt_components_init exécute ce qui suit :

    volatile const init_fn_t *fn_ptr;
    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    {
        (*fn_ptr)();
    }
    

    Le code définit également un fn_ptr pointeur, qui est déréférencé lorsque la plage du pointeur est dans la plage de __rt_init_rti_board_end et __rt_init_rti_end, où le pointeur est le pointeur de fonction placé lors de l’initialisation automatique ; encore une fois, nous obtenons cette fonction exécutée. C’est-à-dire que la fonction dérivée entre le INIT_PREV_EXPORT(fn) pour INIT_APP_EXPORT(fn) les segments sont exécutés

    Exécution des fonctions auto-initialisées

    Le processus de démarrage de RT-Thread:

    Le processus de démarrage de RT-Thread

    Le rt_components_board_init() fonction et la rt_componenets_init() fonction sont exécutées.

    Goûter

    Ajoutez le code de test suivant au main.c fonction:

    int led_init(void)
    {
        return 1;
    }
    INIT_APP_EXPORT(led_init);
    

    Le compilé .map fichier est affiché comme suit :

    Le pointeur de fonction __rt_init_led_init est situé dans le .rti_fn.6 segment, et la fonction rt_components_init() déréférencera ce pointeur lors de son exécution, c’est-à-dire qu’il exécutera le led_init fonction.

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.