GNU/Linux >> Belajar Linux >  >> Linux

Apakah kernel Linux memiliki fungsi utama?

start_kernel

Pada 4.2, start_kernel dari init/main.c adalah proses inisialisasi yang cukup besar dan dapat dibandingkan dengan main fungsi.

Ini adalah kode independen arch pertama yang dijalankan, dan menyiapkan sebagian besar kernel. Sangat mirip dengan main , start_kernel didahului oleh beberapa kode penyiapan tingkat rendah (dilakukan di crt* objek di userland main ), setelah itu kode C umum "utama" dijalankan.

Bagaimana start_kernel dipanggil di x86_64

arch/x86/kernel/vmlinux.lds.S , skrip penaut, menyetel:

ENTRY(phys_startup_64)

dan

phys_startup_64 = startup_64 - LOAD_OFFSET;

dan:

#define LOAD_OFFSET __START_KERNEL_map

arch/x86/include/asm/page_64_types.h mendefinisikan __START_KERNEL_map sebagai:

#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)

yang merupakan alamat entri kernel. TODO bagaimana tepatnya alamat itu tercapai? Saya harus memahami antarmuka yang diekspos Linux ke bootloader.

arch/x86/kernel/vmlinux.lds.S menyetel bagian bootloader pertama sebagai:

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

include/asm-generic/vmlinux.lds.h mendefinisikan HEAD_TEXT :

#define HEAD_TEXT  *(.head.text)

arch/x86/kernel/head_64.S mendefinisikan startup_64 . Itu adalah kode kernel x86 pertama yang berjalan. Itu melakukan banyak penyiapan tingkat rendah, termasuk segmentasi dan paging.

Kemudian hal pertama yang dijalankan karena file dimulai dengan:

.text
__HEAD
.code64
.globl startup_64

dan include/linux/init.h mendefinisikan __HEAD sebagai:

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

jadi sama seperti hal pertama dari skrip linker.

Pada akhirnya ia memanggil x86_64_start_kernel agak canggung dengan dan 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

dan:

.balign 8
GLOBAL(initial_code)
.quad   x86_64_start_kernel

arch/x86/kernel/head64.c mendefinisikan x86_64_start_kernel yang memanggil x86_64_start_reservations yang memanggil start_kernel .

titik masuk arm64

Arm64 pertama yang berjalan pada kernel v5.7 yang tidak terkompresi didefinisikan di https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72 jadi add x13, x18, #0x16 atau b stext tergantung pada 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.

Ini juga merupakan byte pertama dari imej kernel yang tidak dikompresi.

Kedua kasus tersebut melompat ke stext yang memulai tindakan "nyata".

Seperti disebutkan dalam komentar, kedua instruksi ini adalah 64 byte pertama dari header terdokumentasi yang dijelaskan di:https://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call -gambar-kernel

arm64 pertama yang mengaktifkan MMU instruksi:__primary_switched

Saya pikir itu adalah __primary_switched di kepala.S:

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

Pada titik ini, kernel muncul untuk membuat tabel halaman + mungkin memindahkan dirinya sendiri sedemikian rupa sehingga alamat PC cocok dengan simbol file ELF vmlinux. Oleh karena itu pada titik ini Anda seharusnya dapat melihat nama fungsi yang bermakna di GDB tanpa keajaiban tambahan.

titik masuk CPU sekunder arm64

secondary_holding_pen didefinisikan di:https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691

Prosedur entri dijelaskan lebih lanjut di:https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691


Dengan main() Anda mungkin bermaksud apa main() adalah untuk sebuah program, yaitu "titik masuk" nya.

Untuk modul yaitu init_module() .

Dari Edisi ke-2 Driver Perangkat Linux:

Sementara aplikasi melakukan satu tugas dari awal hingga akhir, modul mendaftarkan dirinya sendiri untuk melayani permintaan di masa mendatang, dan fungsi "utama" -nya segera berakhir. Dengan kata lain, tugas dari fungsi init_module (titik masuk modul) adalah mempersiapkan pemanggilan fungsi modul nanti; seolah-olah modul itu berkata, "Ini saya, dan inilah yang bisa saya lakukan." Titik masuk kedua modul, cleanup_module, dipanggil tepat sebelum modul diturunkan. Itu harus memberi tahu kernel, "Saya tidak di sana lagi; jangan minta saya melakukan hal lain."


Pada dasarnya, tidak ada yang spesial dari rutin yang diberi nama main() . Seperti disinggung di atas, main() berfungsi sebagai titik masuk untuk modul beban yang dapat dieksekusi. Namun, Anda dapat menentukan titik masuk yang berbeda untuk modul beban. Bahkan, Anda dapat menentukan lebih dari satu titik masuk, misalnya merujuk ke dll favorit Anda.

Dari sudut pandang sistem operasi (OS), yang dibutuhkan hanyalah alamat titik masuk kode yang akan berfungsi sebagai driver perangkat. OS akan meneruskan kontrol ke titik masuk tersebut saat driver perangkat diperlukan untuk melakukan I/O ke perangkat.

Pemrogram sistem menentukan (setiap OS memiliki metodenya sendiri) koneksi antara perangkat, modul beban yang berfungsi sebagai driver perangkat, dan nama titik masuk dalam modul beban.

Setiap OS memiliki kernelnya sendiri (jelas) dan beberapa mungkin/mungkin dimulai dengan main() tapi saya akan terkejut menemukan kernel yang menggunakan main() selain yang sederhana, seperti UNIX! Pada saat Anda menulis kode kernel, Anda telah lama melewati persyaratan untuk memberi nama setiap modul yang Anda tulis sebagai main() .

Semoga ini membantu?

Temukan potongan kode ini dari kernel untuk Unix Versi 6. Seperti yang Anda lihat main() hanyalah program lain, mencoba untuk memulai!

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.

Beberapa cara untuk melihatnya:

  1. Driver perangkat bukan program. Mereka adalah modul yang dimuat ke dalam program lain (kernel). Dengan demikian, mereka tidak memiliki main() fungsi.

  2. Fakta bahwa semua program harus memiliki main() fungsi hanya berlaku untuk aplikasi userspace. Itu tidak berlaku untuk kernel, atau driver perangkat.


Linux
  1. Apakah Kami Memiliki Undo Di Linux?

  2. Linux – Kernel:Dukungan Namespaces?

  3. Linux – Penerusan Ip Kernel?

  1. Linux – Bagaimana Kernel Linux Dibandingkan Dengan Arsitektur Mikrokernel?

  2. Linux – Apakah Kernel Linux/unix yang Berbeda Dapat Dipertukarkan?

  3. Mengapa fungsi utama tanpa pernyataan pengembalian mengembalikan nilai 12?

  1. Apa artinya mengatakan kernel linux bersifat preemptive?

  2. Bagaimana kernel Linux menentukan urutan panggilan __init?

  3. Memanggil fungsi userspace dari dalam modul kernel Linux