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:
-
Driver perangkat bukan program. Mereka adalah modul yang dimuat ke dalam program lain (kernel). Dengan demikian, mereka tidak memiliki
main()
fungsi. -
Fakta bahwa semua program harus memiliki
main()
fungsi hanya berlaku untuk aplikasi userspace. Itu tidak berlaku untuk kernel, atau driver perangkat.