Quellanalyse von Linux (Kernel): Systemaufruf

Anzahl der zu untersuchenden Versionen

linux-4.2(mainline)

Wo soll man anfangen

Überprüfen Sie zum Aufrufen der Kernelquelle den Verarbeitungsprozess des Kernels, wenn Sie einen Systemaufruf aufrufen. (Fortsetzung des vorherigen Artikels (Ich habe mir den Systemaufrufeintrag angesehen. (Linux-Quellanalyse))

Worauf soll ich achten? Durchsuchen Sie die Quelle vorerst anhand der im vorherigen Artikel beschriebenen Systemaufruf-Eintragstabelle (sys_call_table) als Schlüssel. Natürlich gab es Assembler-Quellen usw., und es war schwierig, sie zu trennen, also suchte ich im Netz nach Informationen, die der Ausgangspunkt sein würden. Ich entschied mich, dies zu überprüfen, indem ich mich auf die folgende Seite bezog (als Schnitt).

Weitere Informationen finden Sie auf der folgenden Website.

Unten im Artikel gibt es einen Ort, an dem Sie "search" eingeben können, um die Quelle zu durchsuchen. Dies ist jedoch eine Funktion, die in ".bashrc" erstellt wurde. Die Definition ist wie folgt.

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

Registrierungsprozess für Systemaufrufbearbeitungsfunktionen

Das erste, was ich überprüft habe, war das Registrieren der Verarbeitungsfunktion des Systemaufrufs, der eine Referenzstelle ist. In "Assembly Programming Linux (Systemaufruf)" in der Quelle /usr/src/linux/arch/i386/kernel/traps.c

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

Ich habe gehört, dass es einen solchen Code gibt, also habe ich nach einer Forschungsquelle gesucht. Da jedoch die Version von Linux (Linux-2.2.16) im Artikel auf der Website alt ist Es gibt kein Verzeichnis wie arch / i386. Es sollte einen Prozess geben, für den die Interrupt-Behandlung von "int 0x80" registriert ist. Suchen Sie daher mit dem Schlüsselwort 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)

Auch hier hat sich der Name ein wenig geändert. IA32_SYSCALL_VECTOR. Überprüfen Sie das Innere von ./arch/x86/kernel/traps.c in den obigen Suchergebnissen. Die Funktion trap_init wurde sofort gefunden. Bei IA32_SYSCALL_VECTOR gab es einen Ort, an dem der Bearbeitungsprozess des Zielsystemaufrufs registriert wurde.

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
	//Folgendes wird weggelassen ...

Ich habe den Inhalt von entry_INT80_compat nicht bestätigt, aber ich habe die umgebenden Quellen durch Drücken von entry_INT80_32 bestätigt.

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)

Ich hatte den folgenden Code in ./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:
	#Folgendes wird weggelassen ...

Wenn Sie den obigen Verarbeitungscode verstehen, wissen Sie, wie Sie die benutzerdefinierten Argumente erhalten, wenn Sie einen Systemaufruf aufrufen. Bevor ich den Code des Assemblers las, über den ich nicht viel Know-how habe, suchte ich im Internet nach Informationen zu Interrupts. Die Seite, in der ich gefunden habe, ist die nächste.

Das Folgende ist ein Zitat von der obigen Seite. Dieser Artikel scheint auch eine alte Version des Kernels zu haben, aber er ist sehr leicht verständlich geschrieben.

2.11 Wie Systemaufrufe in i386 implementiert werden?

    lcall7/lcall27 call gate
int 0x80 Software-Interrupt

Anderes UNIX-basiertes Betriebssystem(Solaris,Unixware 7 usw.)Die Binärdatei verwendet den lcall7-Mechanismus, native Linux-Programme verwenden jedoch int 0x80.'lcall7'Der Name ist ein historischer Fehler. Das ist lcall27(Zum Beispiel Solaris/x86)Wird auch verwendet, aber die Handlerfunktion ist lcall7_Weil es func heißt.

Funktionsbogen, der die IDT beim Systemstart festlegt/i386/kernel/traps.c:trap_init()Wird genannt,(type 15,dpl 3)Vektor 0x80, Bogen/i386/kernel/entry.Stellen Sie dies ein, um die Systemanrufeintragsadresse von S anzugeben.

Wenn eine User Space-Anwendung einen Systemaufruf ausführt, fügen Sie das Argument in ein Register und ein'int 0x80'Führen Sie die Anweisung aus. Dies wird im Kernel-Modus abgefangen und der Prozessor ist ein Eintrag.Springt zum Systemanruf-Einstiegspunkt in S. Es macht folgendes:


    1.Speichern Sie das Register.
    2. %mit ds%es zu KERNEL_Auf DS einstellen. Daher alle referenzierten Daten(Und externe Segmente)Wird zum Kernel-Adressraum.
    3.wenn%Der Wert von eax ist NR_syscalls(Derzeit 256)Wenn es größer ist, schlägt es mit einem ENOSYS-Fehler fehl.
    4.Wenn die Aufgabe verfolgt wurde(tsk->ptrace & PF_TRADESYS )Manchmal wird eine spezielle Verarbeitung durchgeführt. Das ist Strace(Fachwerk in SVR4(1))Dies dient zur Unterstützung von Programmen und Debuggern wie z.
    5. sys_call_table+4*(%eax syscall_number)Anrufen. Diese Tabelle ist dieselbe Datei(arch/i386/kernel/entry.S)Es wird mit jedem Systemaufruf-Handler initialisiert und zeigt auf diesen. Unter Linux enthalten Handler normalerweise beispielsweise sys._offen und sys_sys wie exit_Wird mit dem Präfix angehängt. Diese C-Systemaufruf-Handler sind SAVE_Suchen Sie das Argument aus dem von ALL gespeicherten Stapel.
    6. ``system call return path''hinein gehen. lcall7 sowie int 0x80,Da es auch in lcall27 verwendet wird, hat es eine andere Bezeichnung. das ist,(Untere Hälfte)Umgang mit Aufgaben und Zeitplan()Ob Sie brauchen(tsk->need_resched != 0)Es bezieht sich auf das Überprüfen auf Signale, das Überprüfen, ob Signale anstehen, und das Verarbeiten dieser Signale.

In dem obigen Artikel heißt es: "Wenn eine User Space-Anwendung einen Systemaufruf ausführt, fügt sie ein Argument in ein Register ein und führt die Anweisung 'int 0x80' aus."

Der Vorgang des "Speicherns des Registers" scheint in SAVE_ALL des Codes von entry_SYSENTER_32 des Assemblers durchgeführt zu werden. Am Ende der Seite der Referenzseite Assembly Programming Linux (Systemaufruf), die am Anfang dieses Artikels geschrieben wurde, befand sich die folgende Beschreibung. ..

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

Das Folgende ist zum besseren Verständnis als Zusammenführung der Inhalte von SAVE_ALL und entry_INT80_32 oben geschrieben.

Es ist schwer zu verstehen, also System_Der Anruf wird vereinfacht und unten gezeigt. Die in den Registern der Assembly festgelegten Argumente werden auf den Stapel geladen, sodass das in C geschriebene System als Systemaufruf aufgerufen wird._Es ist ein Argument der XXXX-Funktion.

  ENTRY(system_call)
          pushl %eax                      # save orig_eax
          cld;
          pushl %es; 
          pushl %ds; 
          pushl %eax; 
          pushl %ebp; 
          pushl %edi;                     #5. Argument
          pushl %esi;                     #4. Argument
          pushl %edx;                     #Drittes Argument
          pushl %ecx;                     #2. Argument
          pushl %ebx;                     #1. 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) 
                                          #Entspricht eax-Systemaufrufen
                                          #Rufen Sie eine Funktion auf
          movl %eax,EAX(%esp)             #Legen Sie den Rückgabewert für eax auf dem Stapel fest
          popl %ebx; 
          popl %ecx; 
          popl %edx; 
          popl %esi; 
          popl %edi; 
          popl %ebp; 
          popl %eax;                      #Rückgabewert eingestellt
          popl %ds; 
          popl %es; 
          addl $4,%esp;                   #Erster Drücker%Werfen Sie den eax weg
          iret;                           #Rückkehr vom Systemaufruf

Der Code von SAVE_ALL der Quelle, die ich untersuche, lautet wie folgt. (Auszug aus 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

Das Speichern der als Argumente für den oben beschriebenen Systemaufruf angegebenen Register im Stapel ist ähnlich. Übrigens, wie ich im vorherigen Artikel geschrieben habe, ist die Definition des Systemaufrufeintrags Asmlinkage ist definiert als "asmlinkage long sys_fork (void)" Ich dachte, es wäre ./arch/x86/include/asm/linkage.h:10:#define asmlinkage CPP_ASMLINKAGE __attribute__ ((regparm (0))). Ich dachte, dass "regparm (0)" von register übergeben wurde, aber wenn man die obige Erklärung betrachtet, scheint es, dass es wie ein normaler Funktionsaufruf von stack übergeben wird. Bedeutet das, dass es in "regparm (0)" eine "0" gibt, dass es nicht im Register übergeben wird? (Obwohl ich im vorherigen Artikel geschrieben habe, werde ich regparm separat bestätigen)

Es fühlt sich so an, als hätte ich eine allgemeine Vorstellung davon, was zu tun ist, wenn ein Systemaufruf unterbrochen wird. Danach untersuchen wir jeden Systemaufruf, jeden Scheduler und jede Speicherverwaltung. (Prozessverwaltung, Scheduler zuerst, Speicherverwaltung danach)

Recommended Posts

Quellanalyse von Linux (Kernel): Systemaufruf
Linux (Kernel) -Quellenanalyse: Definition der Systemaufruf-Eingabefunktion
Ich habe mir den Systemaufrufeintrag angesehen. (Linux-Quellenanalyse)
[Linux] [Grundeinstellungen] Systemeinstellungen
Informationen zu Linux-Kernelparametern
Linux Kernel Release 5.x (2/4)
Überprüfen Sie die Linux-Kernelversion
Linux Kernel Release 5.x (3/4)
Quellenanalyse für Django - INSTALLED_APPS
Linux Kernel Release 5.x (4/4)
Linux-Systemarchitektur [Run Level]
Linux Kernel Release 5.x (1/4)
Linux Kernel Build für DE10nano
Selbst erstellter Linux-Kernel mit Clang
Verstehen Sie das Linux-Audit-System Audit
Was ist ein Systemaufruf?
Linux-Hauptpaketverwaltungssystem
Hack Linux Fork Systemaufrufe
[LINUX-Kernel neu erstellen] Upgrade (4.18.0 → 5.8.8)