Semua CPU modern memiliki kapasitas untuk menginterupsi instruksi mesin yang sedang dijalankan. Mereka menyimpan cukup keadaan (biasanya, tetapi tidak selalu, di tumpukan) untuk memungkinkan untuk melanjutkan eksekusi nanti, seolah-olah tidak ada yang terjadi (biasanya instruksi yang terputus akan dimulai dari awal). Kemudian mereka mulai mengeksekusi pengendali interupsi , yang hanya kode mesin lebih banyak, tetapi ditempatkan di lokasi khusus sehingga CPU tahu di mana itu di muka. Interrupt handler selalu menjadi bagian dari kernel sistem operasi: komponen yang berjalan dengan hak istimewa terbesar dan bertanggung jawab untuk mengawasi pelaksanaan semua komponen lainnya. 1,2
Interupsi dapat bersifat sinkron , artinya dipicu oleh CPU itu sendiri sebagai respons langsung terhadap sesuatu yang dilakukan oleh instruksi yang saat ini dijalankan, atau asinkron , yang berarti bahwa itu terjadi pada waktu yang tidak dapat diprediksi karena peristiwa eksternal, seperti data yang tiba di jaringan Pelabuhan. Beberapa orang mencadangkan istilah "interupsi" untuk interupsi asinkron, dan memanggil interupsi sinkron "traps", "fault", atau "exception" sebagai gantinya, tetapi kata-kata itu semuanya memiliki arti lain sehingga saya akan tetap menggunakan "interrupt sinkron".
Sekarang, sebagian besar sistem operasi modern memiliki gagasan tentang proses . Pada dasarnya, ini adalah mekanisme di mana komputer dapat menjalankan lebih dari satu program pada saat yang sama, tetapi juga merupakan aspek kunci tentang bagaimana sistem operasi mengkonfigurasi perlindungan memori , yang merupakan fitur dari sebagian besar (tetapi, sayangnya, masih belum semua ) CPU modern. Ini berjalan bersama dengan memori virtual, yang merupakan kemampuan untuk mengubah pemetaan antara alamat memori dan lokasi aktual dalam RAM. Perlindungan memori memungkinkan sistem operasi untuk memberikan masing-masing proses potongan RAM sendiri, yang hanya dapat diakses olehnya. Hal ini juga memungkinkan sistem operasi (bertindak atas nama beberapa proses) untuk menetapkan wilayah RAM sebagai read-only, executable, dibagi di antara sekelompok proses yang bekerja sama, dll. Juga akan ada sejumlah memori yang hanya dapat diakses oleh inti. 3
Selama setiap proses mengakses memori hanya dengan cara yang dikonfigurasi CPU untuk memungkinkan, perlindungan memori tidak terlihat. Ketika suatu proses melanggar aturan, CPU akan menghasilkan interupsi sinkron, meminta kernel untuk menyelesaikan masalah. Secara teratur terjadi bahwa proses tidak benar - benar melanggar aturan, hanya kernel yang perlu melakukan beberapa pekerjaan sebelum proses dapat diizinkan untuk melanjutkan. Sebagai contoh, jika halaman memori proses perlu "diusir" ke file swap untuk membebaskan ruang dalam RAM untuk sesuatu yang lain, kernel akan menandai halaman itu tidak dapat diakses. Saat berikutnya proses mencoba menggunakannya, CPU akan menghasilkan interupsi perlindungan memori; kernel akan mengambil halaman dari swap, meletakkannya kembali di tempatnya, tandai itu dapat diakses kembali, dan melanjutkan eksekusi.
Tapi anggaplah proses itu benar-benar melanggar aturan. Itu mencoba mengakses halaman yang tidak pernah memiliki RAM yang dipetakan ke sana, atau mencoba untuk mengeksekusi halaman yang ditandai sebagai tidak mengandung kode mesin, atau apa pun. Keluarga sistem operasi umumnya dikenal sebagai "Unix" semua menggunakan sinyal untuk menghadapi situasi ini. 4 Sinyal mirip dengan interupsi, tetapi dihasilkan oleh kernel dan diteruskan oleh proses, bukannya dihasilkan oleh perangkat keras dan diteruskan oleh kernel. Proses dapat menentukan penangan sinyaldi kode mereka sendiri, dan beri tahu kernel di mana mereka berada. Penangan sinyal tersebut kemudian akan mengeksekusi, mengganggu aliran kontrol normal, bila perlu. Semua sinyal memiliki nomor dan dua nama, satu di antaranya adalah akronim samar dan yang lain frase sedikit lebih samar. Sinyal yang dihasilkan ketika proses melanggar aturan perlindungan memori adalah (dengan konvensi) nomor 11, dan namanya adalah SIGSEGV
"Segmentasi kesalahan". 5,6
Perbedaan penting antara sinyal dan interupsi adalah bahwa ada perilaku default untuk setiap sinyal. Jika sistem operasi gagal menentukan handler untuk semua interupsi, itu adalah bug di OS, dan seluruh komputer akan macet ketika CPU mencoba memanggil handler yang hilang. Tetapi proses tidak berkewajiban untuk mendefinisikan penangan sinyal untuk semua sinyal. Jika kernel menghasilkan sinyal untuk suatu proses, dan sinyal itu telah dibiarkan pada perilaku standarnya, kernel hanya akan melanjutkan dan melakukan apa pun defaultnya dan tidak mengganggu proses. Sebagian besar perilaku default sinyal adalah "tidak melakukan apa-apa" atau "menghentikan proses ini dan mungkin juga menghasilkan dump inti." SIGSEGV
adalah salah satu yang terakhir.
Jadi, untuk rekap, kami memiliki proses yang melanggar aturan perlindungan memori. CPU menghentikan proses dan menghasilkan interupsi sinkron. Kernel menerjunkan yang mengganggu dan menghasilkan SIGSEGV
sinyal untuk proses. Mari kita asumsikan prosesnya tidak mengatur penangan sinyal SIGSEGV
, jadi kernel melakukan perilaku default, yaitu untuk menghentikan proses. Ini memiliki semua efek yang sama dengan _exit
panggilan sistem: file yang terbuka ditutup, memori tidak dapat dialokasikan, dll.
Sampai saat ini tidak ada yang mencetak pesan yang dapat dilihat manusia, dan shell (atau, lebih umum, proses induk dari proses yang baru saja dihentikan) belum terlibat sama sekali. SIGSEGV
pergi ke proses yang melanggar aturan, bukan induknya. Namun, langkah selanjutnya dalam urutan ini adalah memberi tahu proses induk bahwa anaknya telah dihentikan. Hal ini dapat terjadi dalam beberapa cara yang berbeda, dari yang paling sederhana adalah ketika orang tua sudah menunggu pemberitahuan ini, menggunakan salah satu wait
panggilan sistem ( wait
, waitpid
, wait4
, dll). Dalam hal ini, kernel hanya akan menyebabkan panggilan sistem kembali, dan menyediakan proses induk dengan nomor kode yang disebut status keluar. 7 Status keluar memberitahu orang tua mengapa proses anak dihentikan; dalam hal ini, ia akan belajar bahwa anak itu dihentikan karena perilaku default dari suatu SIGSEGV
sinyal.
Proses induk kemudian dapat melaporkan acara tersebut ke manusia dengan mencetak pesan; program shell hampir selalu melakukan ini. Anda crsh
tidak menyertakan kode untuk melakukan itu, tetapi itu tetap terjadi, karena rutin pustaka C system
menjalankan shell berfitur lengkap /bin/sh
,, "di bawah tenda". crsh
adalah kakek - nenek dalam skenario ini; notifikasi proses induk dipotong oleh /bin/sh
, yang mencetak pesannya yang biasa. Kemudian /bin/sh
itu sendiri keluar, karena itu tidak ada lagi yang harus dilakukan, dan implementasi C perpustakaan dari system
menerima bahwa pemberitahuan keluar. Anda dapat melihat pemberitahuan keluar itu dalam kode Anda, dengan memeriksa nilai balik darisystem
; tetapi itu tidak akan memberi tahu Anda bahwa proses cucu meninggal dengan segfault, karena itu dikonsumsi oleh proses cangkang perantara.
Catatan kaki
Beberapa sistem operasi tidak mengimplementasikan driver perangkat sebagai bagian dari kernel; namun, semua penangan interupsi masih harus menjadi bagian dari kernel, dan begitu juga kode yang mengkonfigurasi perlindungan memori, karena perangkat keras tidak mengizinkan apa pun kecuali kernel untuk melakukan hal-hal ini.
Mungkin ada program yang disebut "hypervisor" atau "manajer mesin virtual" yang bahkan lebih istimewa daripada kernel, tetapi untuk keperluan jawaban ini dapat dianggap sebagai bagian dari perangkat keras .
Kernel adalah sebuah program , tetapi itu bukan proses; itu lebih seperti perpustakaan. Semua proses menjalankan bagian dari kode kernel, dari waktu ke waktu, selain kode mereka sendiri. Mungkin ada sejumlah "utas kernel" yang hanya mengeksekusi kode kernel, tetapi mereka tidak menjadi perhatian kami di sini.
Satu-satunya OS yang mungkin harus Anda hadapi lagi yang tidak dapat dianggap sebagai implementasi Unix, tentu saja, Windows. Itu tidak menggunakan sinyal dalam situasi ini. (Memang, itu tidak memiliki sinyal; pada Windows <signal.h>
antarmuka sepenuhnya dipalsukan oleh perpustakaan C.) Ia menggunakan sesuatu yang disebut " penanganan pengecualian terstruktur " sebagai gantinya.
Beberapa pelanggaran perlindungan memori menghasilkan SIGBUS
("Bus error") alih-alih SIGSEGV
. Garis antara keduanya tidak ditentukan dan bervariasi dari satu sistem ke sistem lainnya. Jika Anda telah menulis sebuah program untuk mendefinisikan handler SIGSEGV
, mungkin ide yang baik untuk mendefinisikan handler yang sama SIGBUS
.
"Segmentasi fault" adalah nama interupsi yang dihasilkan untuk pelanggaran perlindungan memori oleh salah satu komputer yang menjalankan Unix asli , mungkin PDP-11 . " Segmentasi " adalah jenis perlindungan memori, tetapi saat ini istilah " kesalahan segmentasi " mengacu secara umum pada segala jenis pelanggaran perlindungan memori.
Semua cara lain proses orang tua mungkin diberitahu tentang anak yang telah diakhiri, berakhir dengan panggilan orang tua wait
dan menerima status keluar. Hanya saja sesuatu terjadi lebih dulu.
crsh
adalah ide bagus untuk eksperimen semacam ini. Terima kasih telah memberi tahu kami tentang hal itu dan gagasan di baliknya.