Apakah ruang Kernel digunakan saat Kernel mengeksekusi atas nama program pengguna yaitu System Call? Atau apakah itu ruang alamat untuk semua utas Kernel (misalnya penjadwal)?
Ya dan ya.
Sebelum kita melangkah lebih jauh, kita harus menyatakan ini tentang memori.
Memori dibagi menjadi dua area berbeda:
- Ruang pengguna , yang merupakan sekumpulan lokasi tempat proses pengguna normal berjalan (yaitu segala sesuatu selain kernel). Peran kernel adalah untuk mengelola aplikasi yang berjalan di ruang ini agar tidak mengacaukan satu sama lain, dan mesin.
- Ruang kernel , yang merupakan lokasi penyimpanan kode kernel, dan dijalankan di bawah.
Proses yang berjalan di bawah ruang pengguna hanya memiliki akses ke sebagian kecil memori, sedangkan kernel memiliki akses ke semua memori. Proses yang berjalan di ruang pengguna juga tidak memiliki akses ke ruang kernel. Proses ruang pengguna hanya dapat mengakses sebagian kecil dari kernel melalui antarmuka yang diekspos oleh kernel - panggilan sistem . Jika suatu proses melakukan panggilan sistem, interupsi perangkat lunak dikirim ke kernel, yang kemudian mengirimkan penangan interupsi yang sesuai dan melanjutkan pekerjaannya setelah penangan selesai.
Kode ruang kernel memiliki properti untuk dijalankan dalam "mode kernel", yang (di komputer desktop -x86- biasa) adalah apa yang Anda panggil kode yang dijalankan di bawah dering 0 . Biasanya dalam arsitektur x86, ada 4 ring perlindungan . Dering 0 (mode kernel), Dering 1 (dapat digunakan oleh hypervisor atau driver mesin virtual), Dering 2 (dapat digunakan oleh driver, saya tidak begitu yakin tentang itu). Dering 3 adalah aplikasi tipikal yang dijalankan. Ini adalah ring yang paling tidak diistimewakan, dan aplikasi yang berjalan di atasnya memiliki akses ke subset dari instruksi prosesor. Ring 0 (ruang kernel) adalah ring yang paling istimewa, dan memiliki akses ke semua instruksi mesin. Misalnya untuk ini, aplikasi "biasa" (seperti browser) tidak dapat menggunakan instruksi perakitan x86 lgdt
untuk memuat tabel deskriptor global atau hlt
untuk menghentikan prosesor.
Jika ini yang pertama, apa artinya program pengguna biasa tidak boleh memiliki lebih dari 3GB memori (jika pembagiannya adalah 3GB + 1GB)? Juga, dalam hal ini bagaimana kernel dapat menggunakan Memori Tinggi, karena ke alamat memori virtual mana halaman dari memori tinggi akan dipetakan, karena ruang kernel 1GB akan dipetakan secara logis?
Untuk jawaban atas hal ini, silakan merujuk ke jawaban yang sangat bagus dengan wag here
CPU ring adalah perbedaan yang paling jelas
Dalam mode terlindungi x86, CPU selalu berada di salah satu dari 4 cincin. Kernel Linux hanya menggunakan 0 dan 3:
- 0 untuk kernel
- 3 untuk pengguna
Ini adalah definisi kernel vs userland yang paling keras dan cepat.
Mengapa Linux tidak menggunakan cincin 1 dan 2:https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used
Bagaimana dering saat ini ditentukan?
Dering saat ini dipilih dengan kombinasi:
-
tabel deskriptor global:tabel entri GDT dalam memori, dan setiap entri memiliki bidang
Privl
yang menyandikan cincin.Instruksi LGDT menetapkan alamat ke tabel deskriptor saat ini.
Lihat juga:http://wiki.osdev.org/Global_Descriptor_Table
-
segmen mendaftarkan CS, DS, dll., yang mengarah ke indeks entri di GDT.
Misalnya,
CS = 0
berarti entri pertama GDT sedang aktif untuk kode pelaksana.
Apa yang dapat dilakukan setiap dering?
Chip CPU dibuat secara fisik agar:
-
dering 0 dapat melakukan apa saja
-
dering 3 tidak dapat menjalankan beberapa instruksi dan menulis ke beberapa register, terutama:
-
tidak dapat mengganti cincinnya sendiri! Kalau tidak, itu bisa menyetel sendiri ke dering 0 dan dering tidak akan berguna.
Dengan kata lain, tidak dapat memodifikasi deskriptor segmen saat ini, yang menentukan cincin saat ini.
-
tidak dapat memodifikasi tabel halaman:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work
Dengan kata lain, tidak dapat memodifikasi register CR3, dan paging itu sendiri mencegah modifikasi tabel halaman.
Ini mencegah satu proses melihat memori proses lain untuk alasan keamanan/kemudahan pemrograman.
-
tidak dapat mendaftarkan penangan interupsi. Itu dikonfigurasi dengan menulis ke lokasi memori, yang juga dicegah dengan paging.
Penangan berjalan di ring 0, dan akan merusak model keamanan.
Dengan kata lain, tidak dapat menggunakan instruksi LGDT dan LIDT.
-
tidak dapat melakukan instruksi IO seperti
in
danout
, dan karenanya memiliki akses perangkat keras arbitrer.Jika tidak, misalnya, izin file tidak akan berguna jika ada program yang dapat langsung membaca dari disk.
Lebih tepatnya terima kasih kepada Michael Petch:OS sebenarnya memungkinkan untuk mengizinkan instruksi IO pada ring 3, ini sebenarnya dikendalikan oleh segmen status Tugas.
Yang tidak mungkin adalah ring 3 memberikan izin sendiri untuk melakukannya jika memang tidak memilikinya sejak awal.
Linux selalu melarangnya. Lihat juga:https://stackoverflow.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss
-
Bagaimana transisi program dan sistem operasi antar dering?
-
ketika CPU dihidupkan, itu mulai menjalankan program awal di ring 0 (agak baik, tapi ini perkiraan yang bagus). Anda dapat menganggap program awal ini sebagai kernel (tetapi biasanya bootloader yang kemudian memanggil kernel masih dalam ring 0).
-
ketika proses userland ingin kernel melakukan sesuatu untuknya seperti menulis ke file, ia menggunakan instruksi yang menghasilkan interupsi seperti
int 0x80
atausyscall
untuk memberi sinyal pada kernel. x86-64 Linux syscall halo dunia contoh:.data hello_world: .ascii "hello world\n" hello_world_len = . - hello_world .text .global _start _start: /* write */ mov $1, %rax mov $1, %rdi mov $hello_world, %rsi mov $hello_world_len, %rdx syscall /* exit */ mov $60, %rax mov $0, %rdi syscall
kompilasi dan jalankan:
as -o hello_world.o hello_world.S ld -o hello_world.out hello_world.o ./hello_world.out
GitHub upstream.
Ketika ini terjadi, CPU memanggil penangan panggilan balik interupsi yang didaftarkan oleh kernel pada saat boot. Berikut adalah contoh baremetal konkret yang mendaftarkan penangan dan menggunakannya.
Penangan ini berjalan di ring 0, yang memutuskan apakah kernel akan mengizinkan tindakan ini, melakukan tindakan, dan memulai ulang program userland di ring 3. x86_64
-
ketika
exec
panggilan sistem digunakan (atau ketika kernel akan memulai/init
), kernel menyiapkan register dan memori dari proses userland baru, kemudian melompat ke titik masuk dan mengalihkan CPU ke ring 3 -
Jika program mencoba melakukan sesuatu yang nakal seperti menulis ke register terlarang atau alamat memori (karena paging), CPU juga memanggil beberapa penangan callback kernel di ring 0.
Namun karena userland nakal, kali ini kernel mungkin menghentikan proses, atau memberinya peringatan dengan sinyal.
-
Ketika kernel mem-boot, ia mengatur jam perangkat keras dengan frekuensi tetap, yang menghasilkan interupsi secara berkala.
Jam hardware ini menghasilkan interupsi yang menjalankan ring 0, dan memungkinkannya menjadwalkan proses userland mana yang akan aktif.
Dengan cara ini, penjadwalan dapat dilakukan meskipun proses tidak melakukan panggilan sistem apa pun.
Apa gunanya memiliki banyak dering?
Ada dua keuntungan utama memisahkan kernel dan userland:
- lebih mudah membuat program karena Anda lebih yakin yang satu tidak akan mengganggu yang lain. Misalnya, satu proses userland tidak perlu mengkhawatirkan penimpaan memori program lain karena paging, atau menempatkan perangkat keras dalam keadaan tidak valid untuk proses lain.
- ini lebih aman. Misalnya. izin file dan pemisahan memori dapat mencegah aplikasi peretasan membaca data bank Anda. Ini mengandaikan, tentu saja, bahwa Anda memercayai kernel.
Bagaimana cara memainkannya?
Saya telah membuat penyiapan logam kosong yang seharusnya menjadi cara yang baik untuk memanipulasi cincin secara langsung:https://github.com/cirosantilli/x86-bare-metal-examples
Sayangnya, saya tidak memiliki kesabaran untuk membuat contoh userland, tetapi saya melakukan penyiapan paging, jadi userland harus dapat dilakukan. Saya ingin melihat permintaan penarikan.
Alternatifnya, modul kernel Linux berjalan di ring 0, sehingga Anda dapat menggunakannya untuk mencoba operasi istimewa, mis. baca register kontrol:https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers-cr0-cr2-cr3-from-a-program-getting-segmenta/7419306#7419306
Ini adalah penyiapan QEMU + Buildroot yang praktis untuk mencobanya tanpa mematikan host Anda.
Kelemahan dari modul kernel adalah kthread lain sedang berjalan dan dapat mengganggu eksperimen Anda. Namun secara teori, Anda dapat mengambil alih semua penangan interupsi dengan modul kernel Anda dan memiliki sistemnya, itu sebenarnya akan menjadi proyek yang menarik.
Dering negatif
Meskipun ring negatif sebenarnya tidak disebutkan dalam manual Intel, sebenarnya ada mode CPU yang memiliki kemampuan lebih dari ring 0 itu sendiri, sehingga cocok untuk nama "ring negatif".
Salah satu contohnya adalah mode hypervisor yang digunakan dalam virtualisasi.
Untuk detail lebih lanjut, lihat:
- https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
- https://security.stackexchange.com/questions/216527/ring-3-exploits-and-existence-of-other-rings
LENGAN
Di ARM, ring disebut sebagai Tingkat Pengecualian, tetapi ide utamanya tetap sama.
Terdapat 4 tingkat pengecualian di ARMv8, umumnya digunakan sebagai:
-
EL0:tanah pengguna
-
EL1:kernel ("supervisor" dalam terminologi ARM).
Dimasukkan dengan
svc
instruksi (Panggilan SuperVisor), sebelumnya dikenal sebagaiswi
sebelum perakitan terpadu, yang merupakan instruksi yang digunakan untuk melakukan panggilan sistem Linux. Contoh Halo dunia ARMv8:halo.S
.text .global _start _start: /* write */ mov x0, 1 ldr x1, =msg ldr x2, =len mov x8, 64 svc 0 /* exit */ mov x0, 0 mov x8, 93 svc 0 msg: .ascii "hello syscall v8\n" len = . - msg
GitHub upstream.
Uji dengan QEMU di Ubuntu 16.04:
sudo apt-get install qemu-user gcc-arm-linux-gnueabihf arm-linux-gnueabihf-as -o hello.o hello.S arm-linux-gnueabihf-ld -o hello hello.o qemu-arm hello
Berikut adalah contoh baremetal konkret yang mendaftarkan penangan SVC dan melakukan panggilan SVC.
-
EL2:hypervisor, misalnya Xen.
Dimasukkan dengan
hvc
instruksi (HyperVisor Call).Hypervisor adalah untuk OS, seperti OS untuk userland.
Misalnya, Xen memungkinkan Anda untuk menjalankan beberapa OS seperti Linux atau Windows pada sistem yang sama pada waktu yang sama, dan mengisolasi OS satu sama lain untuk keamanan dan kemudahan debug, seperti yang dilakukan Linux untuk program userland.
Hypervisor adalah bagian penting dari infrastruktur cloud saat ini:hypervisor memungkinkan beberapa server berjalan di satu perangkat keras, menjaga penggunaan perangkat keras selalu mendekati 100% dan menghemat banyak uang.
AWS misalnya menggunakan Xen hingga 2017 ketika perpindahannya ke KVM menjadi berita.
-
EL3:level lainnya. Contoh TODO.
Dimasukkan dengan
smc
instruksi (Panggilan Mode Aman)
Model Referensi Arsitektur ARMv8 DDI 0487C.a - Bab D1 - Model Pemrogram Tingkat Sistem AArch64 - Gambar D1-1 mengilustrasikan hal ini dengan indah:
Situasi ARM sedikit berubah dengan munculnya ARMv8.1 Virtualization Host Extensions (VHE). Ekstensi ini memungkinkan kernel untuk berjalan di EL2 secara efisien:
VHE dibuat karena solusi virtualisasi dalam-Linux-kernel seperti KVM telah memperoleh keunggulan atas Xen (lihat misalnya perpindahan AWS ke KVM yang disebutkan di atas), karena sebagian besar klien hanya memerlukan VM Linux, dan seperti yang dapat Anda bayangkan, semuanya dalam satu proyek, KVM lebih sederhana dan berpotensi lebih efisien daripada Xen. Jadi sekarang kernel Linux host bertindak sebagai hypervisor dalam kasus tersebut.
Perhatikan bagaimana ARM, mungkin karena keuntungan melihat ke belakang, memiliki konvensi penamaan yang lebih baik untuk level hak istimewa daripada x86, tanpa perlu level negatif:0 menjadi yang lebih rendah dan 3 tertinggi. Level yang lebih tinggi cenderung lebih sering dibuat daripada level yang lebih rendah.
EL saat ini dapat ditanyakan dengan MRS
instruksi:https://stackoverflow.com/questions/31787617/what-is-the-current-execution-mode-exception-level-etc
ARM tidak mengharuskan semua tingkat pengecualian hadir untuk memungkinkan implementasi yang tidak memerlukan fitur untuk menghemat area chip. ARMv8 "Tingkat pengecualian" menyatakan:
Implementasi mungkin tidak mencakup semua tingkat Pengecualian. Semua implementasi harus menyertakan EL0 dan EL1.EL2 dan EL3 bersifat opsional.
QEMU misalnya default ke EL1, tetapi EL2 dan EL3 dapat diaktifkan dengan opsi baris perintah:https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulating-a53-power-up
Cuplikan kode diuji di Ubuntu 18.10.
Jika ini yang pertama, apa artinya program pengguna biasa tidak boleh memiliki lebih dari 3GB memori (jika pembagiannya adalah 3GB + 1GB)?
Ya, ini yang terjadi pada sistem linux normal. Ada satu set tambalan "4G/4G" yang beredar di satu titik yang membuat ruang alamat pengguna dan kernel benar-benar independen (dengan biaya kinerja karena mempersulit kernel untuk mengakses memori pengguna) tetapi saya tidak berpikir mereka pernah digabungkan ke hulu dan minat berkurang dengan munculnya x86-64
Juga, dalam hal ini bagaimana kernel dapat menggunakan Memori Tinggi, karena ke alamat memori virtual mana halaman dari memori tinggi akan dipetakan, karena ruang kernel 1GB akan dipetakan secara logis?
Cara linux dulu bekerja (dan masih bekerja pada sistem di mana memorinya kecil dibandingkan dengan ruang alamat) adalah bahwa seluruh memori fisik dipetakan secara permanen ke bagian kernel dari ruang alamat. Hal ini memungkinkan kernel untuk mengakses semua memori fisik tanpa pemetaan ulang, tetapi jelas tidak menskalakan ke mesin 32-bit dengan banyak memori fisik.
Maka lahirlah konsep memori rendah dan tinggi. memori "rendah" secara permanen dipetakan ke dalam ruang alamat kernel. memori "tinggi" tidak.
Saat prosesor menjalankan panggilan sistem, itu berjalan dalam mode kernel tetapi masih dalam konteks proses saat ini. Sehingga dapat langsung mengakses ruang alamat kernel dan ruang alamat pengguna dari proses saat ini (dengan asumsi Anda tidak menggunakan tambalan 4G/4G yang disebutkan di atas). Ini berarti tidak ada masalah untuk memori "tinggi" dialokasikan ke proses userland.
Menggunakan memori "tinggi" untuk keperluan kernel lebih merupakan masalah. Untuk mengakses memori tinggi yang tidak dipetakan ke proses saat ini, memori tersebut harus dipetakan sementara ke dalam ruang alamat kernel. Itu berarti kode tambahan dan penalti performa.