Apa perbedaan antara ruang pengguna dan ruang kernel?

72

Apakah ruang Kernel digunakan ketika Kernel mengeksekusi atas nama program pengguna yaitu System Call? Atau apakah itu ruang alamat untuk semua utas Kernel (misalnya penjadwal)?

Jika ini yang pertama, daripada apakah ini berarti bahwa program pengguna normal tidak dapat 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 apa halaman dari memori tinggi akan dipetakan, karena 1GB ruang kernel akan dipetakan secara logis?

Poojan
sumber

Jawaban:

93

Apakah ruang Kernel digunakan ketika 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 dapat dibagi menjadi dua area berbeda:

  • Ruang pengguna , yang merupakan sekumpulan lokasi tempat proses pengguna normal dijalankan (yaitu segala sesuatu selain kernel). Peran kernel adalah untuk mengelola aplikasi yang berjalan di ruang ini dari saling mengotak, dan mesin.
  • Ruang kernel , yang merupakan lokasi di mana kode kernel disimpan, dan dijalankan di bawah.

Proses yang berjalan di bawah ruang pengguna hanya memiliki akses ke bagian memori yang terbatas, 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 terpapar 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 kerjanya setelah penangan selesai.

Kode ruang kernel memiliki properti untuk dijalankan dalam "mode kernel", yang (dalam komputer desktop -x86- khas Anda) adalah apa yang Anda sebut kode yang dijalankan di bawah ring 0 . Biasanya dalam arsitektur x86, ada 4 cincin 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 khas yang berjalan di bawah. Ini adalah dering yang paling tidak istimewa, dan aplikasi yang berjalan di atasnya memiliki akses ke subset dari instruksi prosesor. Dering 0 (ruang kernel) adalah dering paling istimewa, dan memiliki akses ke semua instruksi mesin. Sebagai contoh untuk ini, aplikasi "biasa" (seperti browser) tidak dapat menggunakan instruksi perakitan x86lgdtuntuk memuat tabel deskriptor global atau hltmenghentikan prosesor.

Jika ini yang pertama, daripada apakah ini berarti bahwa program pengguna normal tidak dapat 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 apa halaman dari memori tinggi akan dipetakan, karena 1GB ruang kernel akan dipetakan secara logis?

Untuk jawaban atas hal ini, silakan merujuk ke jawaban yang sangat bagus oleh wag di sini

NlightNFotis
sumber
4
Jangan ragu untuk memberi tahu saya jika saya melakukan kesalahan di suatu tempat. Saya baru mengenal pemrograman kernel, dan saya mencampakkan di sini apa yang telah saya pelajari sejauh ini, bersama dengan beberapa informasi lain yang saya temukan di web. Yang berarti bahwa mungkin ada kekurangan dalam pemahaman saya tentang konsep-konsep yang dapat ditunjukkan dalam teks.
NlightNFotis
Terima kasih! Saya pikir sekarang saya memahaminya dengan lebih baik. Hanya untuk memastikan bahwa saya mendapatkannya dengan benar, saya punya satu pertanyaan lagi. Sekali lagi mengingat bahwa 3GB pertama digunakan untuk userspace dan 128MB ruang kernel digunakan untuk memori tinggi, apakah sisa 896MB (Memori Rendah) dipetakan secara statis pada saat boot?
Poojan
1
@NlightNFotis Saya mengatakan bahwa hampir 15 orang percaya bahwa apa pun yang Anda katakan, benar (atau Anda membuat kami berpikir;))
Braiam
Saya pikir cincin x86 -1adalah untuk hypervisor? en.wikipedia.org/wiki/Protection_ring
Dori
1
Perhatikan perbedaan antara memori virtual dan memori fisik. Sebagian besar yang Anda tanyakan adalah tentang memori virtual. Ini dipetakan ke memori fisik, ini menjadi rumit ketika memori fisik mendekati 3GB, dan PAE digunakan. Kemudian menjadi sederhana lagi ketika kernel 64bit digunakan, dalam hal ini alamat negatif dicadangkan untuk kernel, dan yang positif untuk ruang pengguna. Proses 32bit sekarang dapat menggunakan ruang virtual 4GB. Proses 64bit dapat menggunakan lebih banyak - biasanya bernilai 48 bit (saat ini pada x86-64).
ctrl-alt-delor
16

Cincin CPU adalah perbedaan yang paling jelas

Dalam mode terproteksi x86, CPU selalu berada dalam salah satu dari 4 dering. 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 dering 1 dan 2: https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent-used

Bagaimana cincin saat ini ditentukan?

Cincin saat ini dipilih oleh kombinasi dari:

  • tabel deskriptor global: tabel in-memory dari entri GDT, dan setiap entri memiliki bidang Privlyang menyandikan ring.

    Instruksi LGDT mengatur alamat ke tabel deskriptor saat ini.

    Lihat juga: http://wiki.osdev.org/Global_Descriptor_Table

  • segmen mendaftarkan CS, DS, dll., yang menunjuk ke indeks entri di GDT.

    Misalnya, CS = 0berarti entri pertama GDT saat ini aktif untuk kode pelaksana.

Apa yang bisa dilakukan oleh setiap dering?

Chip CPU dibangun secara fisik sehingga:

  • dering 0 dapat melakukan apa saja

  • dering 3 tidak dapat menjalankan beberapa instruksi dan menulis ke beberapa register, terutama:

    • tidak dapat mengubah cincinnya sendiri! Kalau tidak, itu bisa mengatur dirinya sendiri untuk berdering 0 dan berdering akan sia-sia.

      Dengan kata lain, tidak dapat memodifikasi deskriptor segmen saat ini , yang menentukan dering saat ini.

    • tidak dapat mengubah 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 dari melihat memori proses lain untuk keamanan / kemudahan alasan pemrograman.

    • tidak dapat mendaftarkan penangan interupsi. Itu dikonfigurasikan dengan menulis ke lokasi memori, yang juga dicegah dengan paging.

      Handler dijalankan di ring 0, dan akan merusak model keamanan.

      Dengan kata lain, tidak dapat menggunakan instruksi LGDT dan LIDT.

    • tidak dapat melakukan instruksi IO seperti indan out, dan karenanya memiliki akses perangkat keras yang sewenang-wenang.

      Kalau tidak, misalnya, izin file tidak akan berguna jika program apa pun dapat langsung membaca dari disk.

      Lebih tepatnya berkat Michael Petch : sebenarnya dimungkinkan bagi OS untuk mengizinkan instruksi IO pada cincin 3, ini sebenarnya dikendalikan oleh segmen status Tugas .

      Yang tidak mungkin adalah cincin 3 memberi izin pada dirinya sendiri jika 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 cincin?

  • ketika CPU dihidupkan, itu mulai menjalankan program awal di ring 0 (agak baik, tetapi itu adalah perkiraan yang baik). Anda dapat menganggap program awal ini sebagai kernel (tetapi biasanya bootloader yang kemudian memanggil kernel masih di ring 0).

  • ketika proses userland menginginkan kernel untuk melakukan sesuatu untuknya seperti menulis ke file, ia menggunakan instruksi yang menghasilkan interupsi seperti int 0x80atausyscall untuk memberi sinyal kernel. x86-64 Linux syscall hello world 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 hulu .

    Ketika ini terjadi, CPU memanggil pengendali panggilan balik interupsi yang didaftarkan kernel saat boot. Berikut adalah contoh baremetal konkret yang mendaftarkan pawang dan menggunakannya .

    Handler ini berjalan di ring 0, yang memutuskan apakah kernel akan mengizinkan tindakan ini, melakukan tindakan, dan memulai kembali program userland di cincin 3. x86_64

  • ketika execpanggilan sistem digunakan (atau ketika kernel akan mulai/init ), kernel menyiapkan register dan memori dari proses userland baru, kemudian melompat ke titik masuk dan mengalihkan CPU ke dering 3

  • Jika program mencoba melakukan sesuatu yang nakal seperti menulis ke register terlarang atau alamat memori (karena paging), CPU juga memanggil beberapa handler callback kernel di ring 0.

    Tetapi karena userland nakal, kernel mungkin membunuh proses kali ini, atau memberinya peringatan dengan sinyal.

  • Ketika kernel melakukan boot, ia akan mengatur jam perangkat keras dengan frekuensi tetap, yang menghasilkan interupsi secara berkala.

    Jam perangkat keras ini menghasilkan interupsi yang menjalankan dering 0, dan memungkinkannya untuk menjadwalkan pengguna mana proses untuk bangun.

    Dengan cara ini, penjadwalan dapat terjadi bahkan jika proses tidak membuat panggilan sistem apa pun.

Apa gunanya memiliki beberapa dering?

Ada dua keuntungan utama dari pemisahan kernel dan userland:

  • lebih mudah untuk membuat program karena Anda lebih yakin tidak akan mengganggu yang lain. Misalnya, satu proses userland tidak perlu khawatir tentang menimpa memori program lain karena paging, atau tentang meletakkan 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 mempercayai kernel.

Bagaimana cara bermain-main dengannya?

Saya telah membuat pengaturan logam kosong yang seharusnya menjadi cara yang baik untuk memanipulasi cincin secara langsung: https://github.com/cirosantilli/x86-bare-metal-examples

Saya tidak memiliki kesabaran untuk membuat contoh userland sayangnya, tapi saya melakukan pengaturan paging, jadi userland harus layak. Saya ingin melihat permintaan tarik.

Sebagai alternatif, modul kernel Linux berjalan di ring 0, sehingga Anda dapat menggunakannya untuk mencoba operasi yang diistimewakan, mis. Baca register kontrol: https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers -cr0-cr2-cr3-dari-program-mendapatkan-segmenta / 7419306 # 7419306

Berikut ini adalah pengaturan QEMU + Buildroot yang nyaman untuk mencobanya tanpa membunuh host Anda.

Kelemahan dari modul kernel adalah bahwa kthreads lain sedang berjalan dan dapat mengganggu percobaan Anda. Tetapi secara teori Anda dapat mengambil alih semua penangan interupsi dengan modul kernel Anda dan memiliki sistem, yang sebenarnya merupakan proyek yang menarik.

Dering negatif

Walaupun dering negatif tidak benar-benar dirujuk dalam manual Intel, sebenarnya ada mode CPU yang memiliki kemampuan lebih lanjut dari dering 0 itu sendiri, dan begitu juga cocok untuk nama "cincin negatif".

Salah satu contoh adalah mode hypervisor yang digunakan dalam virtualisasi.

Untuk perincian lebih lanjut, lihat: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1

LENGAN

Dalam ARM, cincinnya disebut Tingkat Pengecualian, tetapi gagasan utamanya tetap sama.

Ada 4 level pengecualian dalam ARMv8, yang biasa digunakan sebagai:

  • EL0: userland

  • EL1: kernel ("supervisor" dalam terminologi ARM).

    Dimasukkan dengan svcinstruksi (Panggilan SuperVisor), yang sebelumnya dikenal sebagai swi unified assembly , yang merupakan instruksi yang digunakan untuk melakukan panggilan sistem Linux. Contoh Hello world ARMv8:

    .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 hulu .

    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 ini adalah contoh nyata beton yang mendaftarkan penangan SVC dan melakukan panggilan SVC .

  • EL2: hypervisors , misalnya Xen .

    Dimasukkan dengan hvcinstruksi (Panggilan HyperVisor).

    Hypervisor adalah untuk OS, apa OS untuk userland.

    Sebagai contoh, Xen memungkinkan Anda untuk menjalankan banyak OS seperti Linux atau Windows pada sistem yang sama secara bersamaan, dan itu mengisolasi OS satu sama lain untuk keamanan dan kemudahan debug, seperti halnya Linux untuk program userland.

    Hypervisor adalah bagian penting dari infrastruktur cloud saat ini: mereka memungkinkan banyak server untuk berjalan pada satu perangkat keras, menjaga penggunaan perangkat keras selalu mendekati 100% dan menghemat banyak uang.

    AWS misalnya menggunakan Xen hingga 2017 ketika kepindahannya ke KVM menjadi berita .

  • EL3: tingkat lain. Contoh TODO.

    Dimasukkan dengan smcinstruksi (Panggilan Mode Aman)

The ARMv8 Arsitektur Model Referensi DDI 0487C.a - Bab D1 - The AArch64 Sistem Tingkat Programmer Model - Gambar D1-1 menggambarkan ini indah:

masukkan deskripsi gambar di sini

Perhatikan bagaimana ARM, mungkin karena manfaat dari belakang, memiliki konvensi penamaan yang lebih baik untuk tingkat hak istimewa daripada x86, tanpa perlu tingkat negatif: 0 menjadi yang lebih rendah dan 3 tertinggi. Level yang lebih tinggi cenderung dibuat lebih sering daripada level yang lebih rendah.

EL saat ini dapat ditanyakan dengan MRSinstruksi: https://stackoverflow.com/questions/31787617/what-is-the-current-execution-mode-exception-level-etc

ARM tidak memerlukan semua level pengecualian untuk hadir untuk memungkinkan implementasi yang tidak memerlukan fitur untuk menghemat area chip. ARMv8 "Level pengecualian" mengatakan:

Suatu implementasi mungkin tidak mencakup semua level Pengecualian. Semua implementasi harus menyertakan EL0 dan EL1. EL2 dan EL3 adalah 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.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber
3

Jika ini yang pertama, daripada apakah ini berarti bahwa program pengguna normal tidak dapat memiliki lebih dari 3GB memori (jika pembagiannya adalah 3GB + 1GB)?

Ya ini terjadi pada sistem linux normal. Ada satu set tambalan "4G / 4G" melayang di satu titik yang membuat ruang alamat pengguna dan kernel benar-benar independen (dengan biaya kinerja karena membuat kernel lebih sulit untuk mengakses memori pengguna) tapi saya tidak berpikir mereka pernah bergabung ke hulu dan minat berkurang dengan munculnya x86-64

Juga, dalam hal ini bagaimana kernel dapat menggunakan Memori Tinggi, karena ke alamat memori virtual apa halaman dari memori tinggi akan dipetakan, karena 1GB ruang kernel akan dipetakan secara logis?

Cara linux digunakan untuk bekerja (dan masih dilakukan pada sistem di mana memori kecil dibandingkan dengan ruang alamat) adalah bahwa seluruh memori fisik secara permanen dipetakan ke bagian kernel dari ruang alamat. Ini memungkinkan kernel untuk mengakses semua memori fisik tanpa memetakan kembali tetapi jelas itu tidak skala 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.

Ketika prosesor menjalankan panggilan sistem, prosesor berjalan dalam mode kernel tetapi masih dalam konteks proses saat ini. Sehingga ia dapat secara 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 untuk proses userland.

Menggunakan memori "tinggi" untuk keperluan kernel lebih merupakan masalah. Untuk mengakses memori tinggi yang tidak dipetakan ke proses saat ini, ia harus sementara dipetakan ke dalam ruang alamat kernel. Itu berarti kode tambahan dan penalti kinerja.

plugwash
sumber