analyse de la source linux (noyau): appel système

Nombre de versions à étudier

linux-4.2(mainline)

Où commencer

Pour lire la source du noyau, examinez le processus de gestion du noyau lors de l'appel d'un appel système. (Suite de l'article précédent (J'ai regardé l'entrée d'appel système. (Analyse des sources Linux)))

Que dois-je rechercher? Pour le moment, recherchez la source en utilisant la table d'entrée des appels système (sys_call_table) écrite dans l'article précédent sous forme de clé. Bien sûr, il y avait des sources assembleurs, etc., et il était difficile de les séparer, alors j'ai cherché sur le net des informations qui seraient le point de départ. J'ai décidé de le vérifier en me référant au site suivant (en coupe).

De plus, consultez le site suivant.

Ci-dessous, dans l'article, il y a un endroit pour taper "recherche" pour rechercher la source, mais c'est une fonction créée dans .bashrc. La définition est la suivante.

function search() {
        find . \( -name \*.c -o -name \*.h -o -name \*.S \) -exec grep -n $1 {} /dev/null \;
}

Processus d'enregistrement de la fonction de gestion des appels système

La première chose que j'ai vérifiée a été le processus d'enregistrement de la fonction de gestion de l'appel système, qui est un site de référence. Dans "Assembly Programming Linux (system call)", dans la source de /usr/src/linux/arch/i386/kernel/traps.c

void __init trap_init(void)
	set_system_gate(SYSCALL_VECTOR,&system_call);

J'ai entendu dire qu'il y avait un code comme celui-ci, alors j'ai cherché une source de recherche. Cependant, parce que la version de linux (linux-2.2.16) dans l'article sur le site est ancienne Il n'y a pas de répertoire lui-même comme arch / i386. Il devrait y avoir un processus pour lequel la gestion des interruptions de "int 0x80" est enregistrée, donc recherchez avec le mot clé SYSCALL_VECTOR.

kou77@ubuntu:~/linux-4.2$ search SYSCALL_VECTOR
./arch/x86/include/asm/irq_vectors.h:49:#define IA32_SYSCALL_VECTOR             0x80
./arch/x86/kernel/traps.c:896:  set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_compat);
./arch/x86/kernel/traps.c:897:  set_bit(IA32_SYSCALL_VECTOR, used_vectors);
./arch/x86/kernel/traps.c:901:  set_system_trap_gate(IA32_SYSCALL_VECTOR, entry_INT80_32);
./arch/x86/kernel/traps.c:902:  set_bit(IA32_SYSCALL_VECTOR, used_vectors);
./arch/x86/kernel/irqinit.c:186:                /* IA32_SYSCALL_VECTOR could be used in trap_init already. */
./arch/x86/lguest/boot.c:93:    .syscall_vec = IA32_SYSCALL_VECTOR,
./arch/x86/lguest/boot.c:869:           if (i != IA32_SYSCALL_VECTOR)
./arch/m32r/include/asm/syscall.h:5:#define SYSCALL_VECTOR          "2"
./arch/m32r/include/asm/syscall.h:6:#define SYSCALL_VECTOR_ADDRESS  "0xa0"
./drivers/lguest/interrupts_and_traps.c:23:static unsigned int syscall_vector = IA32_SYSCALL_VECTOR;
./drivers/lguest/interrupts_and_traps.c:336:    /* Normal Linux IA32_SYSCALL_VECTOR or reserved vector? */
./drivers/lguest/interrupts_and_traps.c:337:    return num == IA32_SYSCALL_VECTOR || num == syscall_vector;
./drivers/lguest/interrupts_and_traps.c:354:    if (syscall_vector != IA32_SYSCALL_VECTOR) {
./drivers/lguest/interrupts_and_traps.c:369:    if (syscall_vector != IA32_SYSCALL_VECTOR)

Le nom a un peu changé ici aussi. IA32_SYSCALL_VECTOR. Vérifiez l'intérieur de ./arch/x86/kernel/traps.c dans les résultats de recherche ci-dessus. La fonction trap_init a été trouvée immédiatement. En regardant IA32_SYSCALL_VECTOR, il y avait un endroit où le processus de traitement de l'appel système cible était enregistré.

void __init trap_init(void)
{
        int i;

#ifdef CONFIG_EISA
        void __iomem *p = early_ioremap(0x0FFFD9, 4);

        if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
                EISA_bus = 1;
        early_iounmap(p, 4);
#endif

        set_intr_gate(X86_TRAP_DE, divide_error);
        set_intr_gate_ist(X86_TRAP_NMI, &nmi, NMI_STACK);
        /* int4 can be called from all */
        set_system_intr_gate(X86_TRAP_OF, &overflow);
        set_intr_gate(X86_TRAP_BR, bounds);
        set_intr_gate(X86_TRAP_UD, invalid_op);
        set_intr_gate(X86_TRAP_NM, device_not_available);
#ifdef CONFIG_X86_32
        set_task_gate(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS);
#else
        set_intr_gate_ist(X86_TRAP_DF, &double_fault, DOUBLEFAULT_STACK);
#endif
        set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun);
        set_intr_gate(X86_TRAP_TS, invalid_TSS);
        set_intr_gate(X86_TRAP_NP, segment_not_present);
        set_intr_gate(X86_TRAP_SS, stack_segment);
        set_intr_gate(X86_TRAP_GP, general_protection);
        set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug);
        set_intr_gate(X86_TRAP_MF, coprocessor_error);
        set_intr_gate(X86_TRAP_AC, alignment_check);
#ifdef CONFIG_X86_MCE
        set_intr_gate_ist(X86_TRAP_MC, &machine_check, MCE_STACK);
#endif
        set_intr_gate(X86_TRAP_XF, simd_coprocessor_error);

        /* Reserve all the builtin and the syscall vector: */
        for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)
                set_bit(i, used_vectors);

#ifdef CONFIG_IA32_EMULATION
        set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_compat);
        set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif

#ifdef CONFIG_X86_32
        set_system_trap_gate(IA32_SYSCALL_VECTOR, entry_INT80_32);
        set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif
	//Ce qui suit est omis ...

Je n'ai pas confirmé le contenu de entry_INT80_compat, mais j'ai confirmé les sources environnantes en appuyant sur entry_INT80_32.

kou77@ubuntu:~/linux-4.2/arch/x86$ search entry_INT80_32
./include/asm/proto.h:12:void entry_INT80_32(void);
./kernel/traps.c:901:   set_system_trap_gate(IA32_SYSCALL_VECTOR, entry_INT80_32);
./entry/entry_32.S:408:ENTRY(entry_INT80_32)
./entry/entry_32.S:505:ENDPROC(entry_INT80_32)

J'avais le code suivant dans ./entry/entry_32.S.

ENDPROC(entry_SYSENTER_32)

        # system call handler stub
ENTRY(entry_INT80_32)
        ASM_CLAC
        pushl   %eax                            # save orig_eax
        SAVE_ALL
        GET_THREAD_INFO(%ebp)
                                                # system call tracing in operation / emulation
        testl   $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
        jnz     syscall_trace_entry
        cmpl    $(NR_syscalls), %eax
        jae     syscall_badsys
syscall_call:
        call    *sys_call_table(, %eax, 4)
syscall_after_call:
	#Ce qui suit est omis ...

Une fois que vous aurez compris le code de gestion ci-dessus, vous verrez comment il reçoit les arguments spécifiés par l'utilisateur lors de l'appel d'un appel système. Avant de lire le code de l'assembleur, dont je n'ai pas beaucoup de savoir-faire, j'ai cherché sur le net des informations relatives aux interruptions. Le site que j'ai trouvé est le suivant.

Ce qui suit est une citation du site ci-dessus. Cet article semble également avoir une ancienne version du noyau, mais il est écrit d'une manière très facile à comprendre.

2.11 Comment les appels système sont implémentés dans i386?

    lcall7/Portail d'appel lcall27
Interruption logicielle int 0x80

Autre système d'exploitation basé sur UNIX(Solaris,Unixware 7 etc.)Le binaire utilise le mécanisme lcall7, mais les programmes Linux natifs utilisent int 0x80.'lcall7'Le nom est une erreur historique. C'est lcall27(Par exemple Solaris/x86)Est également utilisé, mais la fonction de gestionnaire est lcall7_Parce que cela s'appelle func.

Arche de fonction qui définit l'IDT au démarrage du système/i386/kernel/traps.c:trap_init()Est appelé,(type 15,dpl 3)Vecteur 0x80, arc/i386/kernel/entry.Réglez pour indiquer l'adresse d'entrée d'appel système de S.

Lorsqu'une application d'espace utilisateur effectue un appel système, mettez l'argument dans un registre et'int 0x80'Exécutez l'instruction. Ceci est piégé en mode noyau et le processeur est en entrée.Saute au point d'entrée de l'appel système de S. Il fait ce qui suit:


    1.Sauvegardez le registre.
    2. %avec ds%es à KERNEL_Réglez sur DS. Par conséquent, toutes les données référencées(Et segments externes)Devient l'espace d'adressage du noyau.
    3.si%La valeur de eax est NR_syscalls(Actuellement 256)S'il est plus grand, il échouera avec une erreur ENOSYS.
    4.Si la tâche a été tracée(tsk->ptrace & PF_TRADESYS )Parfois, un traitement spécial est effectué. C'est strace(Ferme en SVR4(1))Il s'agit de prendre en charge les programmes et les débogueurs tels que.
    5. sys_call_table+4*(%appel système eax_number)Appeler. Cette table est le même fichier(arch/i386/kernel/entry.S)Il est initialisé avec et pointe vers chaque gestionnaire d'appels système. Sous Linux, les gestionnaires incluent généralement sys, par exemple._ouvert et sys_sys comme exit_Est attaché avec le préfixe. Ces gestionnaires d'appels système C sont SAVE_Trouvez l'argument de la pile stockée par ALL.
    6. ``system call return path''entrer. lcall7 ainsi que int 0x80,Puisqu'il est également utilisé dans lcall27, il a une étiquette différente. c'est,(Moitié inférieure)Gestion des tasklets et du calendrier()Si vous avez besoin(tsk->need_resched != 0)Il s'agit de vérifier les signaux, de vérifier si des signaux sont en attente et de traiter ces signaux.

L'article ci-dessus dit: "Lorsqu'une application de l'espace utilisateur effectue un appel système, elle place un argument dans un registre et exécute l'instruction" int 0x80 "."

Le processus de "sauvegarde du registre" semble se faire dans SAVE_ALL du code de l'entrée_SYSENTER_32 de l'assembleur. Au fond de la page du site de référence Assembly Programming Linux (system call) écrit au début de cet article, il y avait la description suivante. ..

/usr/src/linux/arch/i386/kernel/entry.S<br />
    83  #define SAVE_ALL \<br />
    84          cld; \<br />
    85          pushl %es; \<br />
    86          pushl %ds; \<br />
    87          pushl %eax; \<br />
    88          pushl %ebp; \<br />
    89          pushl %edi; \<br />
    90          pushl %esi; \<br />
    91          pushl %edx; \<br />
    92          pushl %ecx; \<br />
    93          pushl %ebx; \<br />
    94          movl $(__KERNEL_DS),%edx; \<br />
    95          movl %dx,%ds; \<br />
    96          movl %dx,%es;

Ainsi, ce qui suit est écrit comme une fusion du contenu de SAVE_ALL et de entry_INT80_32 ci-dessus pour une compréhension facile.

C'est difficile à comprendre, donc le système_L'appel est simplifié et illustré ci-dessous. Les arguments définis dans les registres de l'assembly sont chargés sur la pile, de sorte que le sys écrit en C est appelé comme un appel système._C'est un argument de la fonction XXXX.

  ENTRY(system_call)
          pushl %eax                      # save orig_eax
          cld;
          pushl %es; 
          pushl %ds; 
          pushl %eax; 
          pushl %ebp; 
          pushl %edi;                     #5ème argument
          pushl %esi;                     #4ème argument
          pushl %edx;                     #Troisième argument
          pushl %ecx;                     #2ème argument
          pushl %ebx;                     #1er argument
          movl $(__KERNEL_DS),%edx; 
          movl %dx,%ds; 
          movl %dx,%es; 
          movl %esp, %ebx; 
          andl $-8192, %ebx; 
          cmpl $(NR_syscalls),%eax 
          jae badsys 
          testb $0x20,flags(%ebx)         # PF_TRACESYS 
          jne tracesys 
          call *SYMBOL_NAME(sys_call_table)(,%eax,4) 
                                          #Correspond aux appels système eax
                                          #Appeler une fonction
          movl %eax,EAX(%esp)             #Définir la valeur de retour pour eax sur la pile
          popl %ebx; 
          popl %ecx; 
          popl %edx; 
          popl %esi; 
          popl %edi; 
          popl %ebp; 
          popl %eax;                      #Valeur de retour définie
          popl %ds; 
          popl %es; 
          addl $4,%esp;                   #Premier pushl%Jeter le eax
          iret;                           #Retour de l'appel système

Le code de SAVE_ALL de la source sur laquelle j'étudie est le suivant. (Extrait de arch / x86 / entry / entry_32.S)

.macro SAVE_ALL
        cld
        PUSH_GS
        pushl   %fs
        pushl   %es
        pushl   %ds
        pushl   %eax
        pushl   %ebp
        pushl   %edi
        pushl   %esi
        pushl   %edx
        pushl   %ecx
        pushl   %ebx
        movl    $(__USER_DS), %edx
        movl    %edx, %ds
        movl    %edx, %es
        movl    $(__KERNEL_PERCPU), %edx
        movl    %edx, %fs
        SET_KERNEL_GS %edx
.endm

L'enregistrement des registres spécifiés comme arguments de l'appel système décrit ci-dessus dans la pile est similaire. À propos, comme je l'ai écrit dans l'article précédent, la définition de l'entrée d'appel système est ʻAsmlinkage long sys_fork (void) , la définition de asmlinkage est Je pensais que c'était ./arch/x86/include/asm/linkage.h:10:#define asmlinkage CPP_ASMLINKAGE attribute ((regparm (0))). Je pensais que regparm (0)était passé par registre, mais en regardant l'explication ci-dessus, il semble qu'il soit passé par pile comme un appel de fonction normal. Puisqu'il y a un "0" dansregparm (0)`, cela signifie-t-il qu'il n'est pas passé dans le registre? (Bien que j'aie écrit dans l'article précédent, je confirmerai regparm séparément)

J'ai l'impression d'avoir une idée générale de ce qu'il faut faire lorsqu'un appel système est interrompu. Après cela, nous étudierons chaque appel système, planificateur et gestion de la mémoire. (Gestion des processus, planificateur d'abord, gestion de la mémoire après)

Recommended Posts

analyse de la source linux (noyau): appel système
analyse de source linux (noyau): définition de la fonction d'entrée d'appel système
J'ai jeté un coup d'œil à l'entrée d'appel système. (analyse des sources Linux)
[Linux] [Paramètres initiaux] Paramètres système
À propos des paramètres du noyau Linux
Version du noyau Linux 5.x (2/4)
Vérifiez la version du noyau Linux
Version du noyau Linux 5.x (3/4)
Analyse de la source pour Django - INSTALLED_APPS
Version du noyau Linux 5.x (4/4)
Architecture du système Linux [niveau d'exécution]
Version du noyau Linux 5.x (1/4)
Périphérique et système de fichiers Linux
Compilation du noyau Linux pour DE10nano
Noyau Linux auto-construit avec clang
Comprendre l'audit du système d'audit Linux
Qu'est-ce qu'un appel système
Système de gestion de paquet principal Linux
Pirater les appels système de Linux
[Reconstruction du noyau LINUX] Mise à jour (4.18.0 → 5.8.8)