Apakah Sistem Operasi menyuntikkan kode mesin sendiri ketika Anda membuka program?

32

Saya sedang mempelajari CPU dan saya tahu cara membaca program dari memori dan menjalankan instruksinya. Saya juga mengerti bahwa sebuah OS memisahkan program dalam proses, dan kemudian bergantian antara masing-masing begitu cepat sehingga Anda berpikir bahwa mereka berjalan pada waktu yang sama, tetapi pada kenyataannya setiap program berjalan sendiri dalam CPU. Tetapi, jika OS ini juga merupakan kumpulan kode yang berjalan di dalam CPU, bagaimana ia dapat mengatur prosesnya?

Saya telah memikirkan dan satu-satunya penjelasan yang dapat saya pikirkan adalah: ketika OS memuat sebuah program dari memori eksternal ke RAM, ia menambahkan instruksi sendiri di tengah instruksi program asli, sehingga program dijalankan, program dapat memanggil OS dan melakukan beberapa hal. Saya percaya ada instruksi bahwa OS akan menambahkan ke program, yang akan memungkinkan CPU untuk kembali ke kode OS beberapa waktu. Dan juga, saya percaya bahwa ketika OS memuat sebuah program, ia memeriksa apakah ada beberapa instruksi yang dilarang (yang akan melompat ke alamat terlarang dalam memori) dan menghilangkannya kemudian.

Apakah saya berpikir tegar? Saya bukan seorang siswa CS, tetapi pada kenyataannya, seorang siswa matematika. Jika memungkinkan, saya ingin buku yang bagus tentang ini, karena saya tidak menemukan orang yang menjelaskan bagaimana OS dapat mengatur proses jika OS juga merupakan kumpulan kode yang berjalan di CPU, dan tidak dapat berjalan pada saat yang sama waktu program. Buku-buku hanya mengatakan bahwa OS dapat mengatur berbagai hal, tetapi sekarang caranya.

Sumoda yang dihormati
sumber
7
Lihat: Sakelar konteks OS melakukan perpindahan konteks ke aplikasi. Aplikasi kemudian dapat meminta layanan OS yang melakukan konteks kembali ke OS. Ketika aplikasi berakhir konteksnya beralih kembali ke OS.
Guy Coder
4
Lihat juga "syscall".
Raphael
1
Jika komentar dan jawaban tidak menjawab pertanyaan Anda sesuai dengan pemahaman atau kepuasan Anda, maka silakan minta lebih banyak info sebagai komentar dan jelaskan apa yang Anda pikirkan atau di mana Anda terhilang, atau apa yang secara spesifik Anda butuhkan lebih detail.
Guy Coder
2
Saya pikir interrupt , hooking (of interrupt), timer hardware (dengan penjadwalan -handling hook) dan paging (jawaban parsial atas komentar Anda pada memori terlarang) adalah kata kunci utama yang Anda butuhkan. OS perlu bekerja sama dengan prosesor untuk menjalankan kodenya hanya jika diperlukan. Dengan demikian sebagian besar daya CPU dapat digunakan pada perhitungan yang sebenarnya, bukan manajemennya.
Palec

Jawaban:

35

Tidak. Sistem operasi tidak main-main dengan kode program yang menyuntikkan kode baru ke dalamnya. Itu akan memiliki sejumlah kelemahan.

  1. Ini akan memakan waktu, karena OS harus memindai seluruh yang dapat dieksekusi untuk membuat perubahan. Biasanya, bagian dari executable hanya dimuat sesuai kebutuhan. Selain itu, memasukkannya mahal karena Anda harus memindahkan banyak barang.

  2. Karena ketidakpastian masalah penghentian, tidak mungkin untuk mengetahui di mana memasukkan instruksi "Langsung kembali ke OS" Anda. Misalnya, jika kode mencakup sesuatu seperti while (true) {i++;}, Anda pasti perlu memasukkan kait di dalam loop itu tetapi kondisi pada loop ( true, di sini) bisa rumit, jadi Anda tidak bisa memutuskan berapa lama loopnya. Di sisi lain, akan sangat tidak efisien untuk memasukkan kait ke dalam setiap loop: misalnya, melompat kembali ke OS selama for (i=0; i<3; i++) {j=j+i;}akan banyak memperlambat proses. Dan, untuk alasan yang sama, Anda tidak dapat mendeteksi loop pendek untuk membiarkannya sendirian.

  3. Karena ketidakpastian masalah penghentian, tidak mungkin untuk mengetahui apakah suntikan kode mengubah arti program. Sebagai contoh, misalkan Anda menggunakan pointer fungsi dalam program C. Menyuntikkan kode baru akan memindahkan lokasi fungsi jadi, ketika Anda memanggil satu melalui pointer, Anda akan melompat ke tempat yang salah. Jika programmer cukup sakit untuk menggunakan lompatan terkomputasi, itu akan gagal juga.

  4. Itu akan sangat menyenangkan dengan sistem anti-virus, karena itu akan mengubah kode virus juga, dan membereskan semua checksum Anda.

Anda bisa menyiasati masalah penghentian masalah dengan mensimulasikan kode dan memasukkan kait dalam loop apa pun yang mengeksekusi lebih dari jumlah tetap tertentu. Namun, itu akan memerlukan simulasi yang sangat mahal dari keseluruhan program sebelum diizinkan untuk dijalankan.

Sebenarnya, jika Anda ingin menyuntikkan kode, kompiler akan menjadi tempat alami untuk melakukannya. Dengan begitu, Anda hanya perlu melakukannya sekali tetapi tetap tidak berhasil karena alasan kedua dan ketiga yang diberikan di atas. (Dan seseorang bisa menulis kompiler yang tidak cocok.)

Ada tiga cara utama bahwa OS mendapatkan kembali kendali dari proses.

  1. Dalam sistem kooperatif (atau non-preemptive), ada yieldfungsi yang dapat dipanggil proses untuk memberikan kontrol kembali ke OS. Tentu saja, jika itu satu-satunya mekanisme Anda, Anda bergantung pada proses berperilaku baik dan proses yang tidak menghasilkan akan memakan CPU sampai berakhir.

  2. Untuk menghindari masalah itu, interupsi timer digunakan. CPU memungkinkan OS untuk mendaftarkan panggilan balik untuk semua jenis interupsi yang diterapkan CPU. OS menggunakan mekanisme ini untuk mendaftarkan panggilan balik untuk penghenti waktu yang diaktifkan secara berkala, yang memungkinkannya untuk mengeksekusi kodenya sendiri.

  3. Setiap kali suatu proses mencoba membaca dari suatu file atau berinteraksi dengan perangkat keras dengan cara lain, itu meminta OS untuk melakukan pekerjaan untuk itu. Ketika OS diminta untuk melakukan sesuatu dengan suatu proses, ia dapat memutuskan untuk menunda proses itu dan mulai menjalankan yang lain. Ini mungkin terdengar agak Machiavellian tapi itu hal yang tepat untuk dilakukan: disk I / O lambat sehingga Anda mungkin juga membiarkan proses B berjalan saat proses A sedang menunggu gumpalan logam berputar untuk pindah ke tempat yang tepat. Jaringan I / O bahkan lebih lambat. Keyboard I / O glasial karena manusia bukan makhluk gigahertz.

David Richerby
sumber
5
Bisakah Anda mengembangkan lebih banyak pada 2. poin terakhir Anda? Saya ingin tahu tentang pertanyaan ini, dan saya merasa penjelasannya dilewati di sini. Sepertinya saya pertanyaannya adalah "bagaimana OS mengambil kembali CPU dari proses" dan jawaban Anda mengatakan "OS menanganinya". tapi bagaimana caranya? Ambil infinite loop dalam contoh pertama Anda: bagaimana tidak membekukan komputer?
BiAiB
3
Beberapa OS melakukannya, sebagian besar OS setidaknya mengacaukan dengan kode untuk melakukan "menghubungkan", sehingga program dapat dimuat di alamat mana pun
Ian Ringrose
1
@BiAiB Kata kuncinya di sini adalah "interrupt". CPU bukan hanya sesuatu yang memproses aliran instruksi tertentu, tetapi juga dapat diinterupsi secara asinkron dari sumber yang terpisah - yang paling penting bagi kita, I / O, dan clock interupsi. Karena hanya kode ruang-kernel yang dapat menangani interupsi, Windows dapat dipastikan dapat "mencuri" pekerjaan dari proses yang sedang berjalan kapan pun ia mau. Penangan interupsi dapat mengeksekusi kode apa pun yang mereka inginkan, termasuk "menyimpan register CPU di suatu tempat dan mengembalikannya dari sini (utas lainnya)". Sangat disederhanakan, tapi itulah konteksnya.
Luaan
1
Menambahkan ke jawaban ini; gaya multitasking sebagaimana dimaksud dalam poin 2 dan 3 disebut "preemptive multitasking", namanya mengacu pada kemampuan OS untuk mencegah proses yang sedang berjalan. Multitasking kooperatif sering digunakan pada sistem operasi lama; pada Windows setidaknya multitasking preemptive tidak diperkenalkan sampai Windows 95. Saya telah membaca setidaknya satu sistem kontrol industri yang digunakan saat ini yang masih menggunakan Windows 3.1 hanya untuk perilaku multitasking kooperatif real-time.
Jason C
3
@BiAiB Sebenarnya, Anda salah. CPU Desktop tidak menjalankan kode secara berurutan dan sinkron sejak tentang i486. Namun, bahkan CPU yang lebih lama masih memiliki input yang tidak sinkron - interupsi. Bayangkan permintaan interupsi perangkat keras (IRQ) seperti pin pada CPU itu sendiri - ketika didapat 1, CPU menghentikan apa pun yang dilakukannya, dan mulai memproses interupsi (yang pada dasarnya berarti "mempertahankan status dan lompat ke alamat dalam memori"). Penanganan interupsi itu sendiri bukan x86atau kode apa pun, itu benar-benar bawaan. Setelah melompat, ia kembali mengeksekusi x86kode (apa saja) . Thread adalah abstraksi yang jauh lebih tinggi.
Luaan
12

Sementara jawaban David Richerby adalah jawaban yang bagus, jawaban itu agak membingungkan tentang bagaimana sistem operasi modern menghentikan program yang ada. Jawaban saya harus akurat untuk arsitektur x86 atau x86_64, yang merupakan satu-satunya yang umum digunakan untuk desktop dan laptop. Arsitektur lain harus memiliki metode serupa untuk mencapai ini.

Ketika sistem operasi mulai, itu mengatur tabel interupsi. Setiap entri tabel menunjuk ke sedikit kode di dalam sistem operasi. Ketika interupsi terjadi, yang dikendalikan oleh CPU, ia melihat tabel ini dan memanggil kode. Ada berbagai interupsi, seperti membaginya dengan nol, kode tidak valid, dan beberapa yang didefinisikan sistem operasi.

Ini adalah bagaimana proses pengguna berbicara dengan kernel, seperti jika ingin membaca / menulis ke disk atau sesuatu yang lain yang dikendalikan oleh kernel sistem operasi. Sistem operasi juga akan mengatur timer yang memanggil interupsi ketika selesai, sehingga kode yang berjalan diubah secara paksa dari program pengguna ke kernel sistem operasi, dan kernel dapat melakukan hal-hal lain seperti mengantre program lain untuk dijalankan.

Dari memori, ketika ini terjadi kernel sistem operasi harus menyimpan di mana kode itu, dan ketika kernel selesai melakukan apa yang perlu dilakukan itu mengembalikan keadaan program sebelumnya. Dengan demikian program bahkan tidak tahu bahwa itu terganggu.

Proses tidak dapat mengubah tabel interupsi karena dua alasan, yang pertama berjalan di lingkungan yang dilindungi jadi jika ia mencoba memanggil kode rakitan tertentu yang dilindungi maka cpu akan memicu interupsi lainnya. Alasan kedua adalah memori virtual. Lokasi tabel interupsi berada pada 0x0 hingga 0x3FF dalam memori nyata, tetapi dengan proses pengguna lokasi itu biasanya tidak dipetakan, dan mencoba membaca memori yang tidak dipetakan akan memicu interupsi lain, jadi tanpa fungsi yang dilindungi dan kemampuan untuk menulis ke RAM nyata , proses pengguna tidak dapat mengubahnya.

Programmdude
sumber
4
Interupsi tidak ditentukan oleh sistem operasi, mereka diberikan oleh perangkat keras. Dan kebanyakan arsitektur saat ini memiliki instruksi khusus untuk memanggil sistem operasi. i386 menggunakan interupsi (yang dihasilkan perangkat lunak) untuk ini, tetapi tidak dilakukan seperti itu lagi pada penerusnya.
vonbrand
2
Saya tahu bahwa interupsi ditentukan oleh cpu, tetapi kernel mengatur pointer. Saya mungkin menjelaskannya dengan buruk. Saya juga berpikir bahwa linux menggunakan int 9 untuk berbicara dengan kernel masih, tetapi mungkin ada cara yang lebih baik sekarang.
Programmdude
Ini adalah jawaban yang cukup menyesatkan meskipun gagasan bahwa penjadwal awal didorong oleh penghenti waktu adalah benar. Pertama, perlu dicatat bahwa timer ada di perangkat keras. Juga untuk mengklarifikasi bahwa proses "save ... restore" disebut saklar konteks dan sebagian besar melibatkan menyimpan semua register CPU (yang termasuk pointer instruksi), antara lain. Selain itu, proses dapat secara efektif mengubah tabel interupsi, ini disebut "mode terlindungi", yang juga mendefinisikan memori virtual, dan telah ada sejak 286 - pointer ke tabel interupsi disimpan dalam register yang dapat ditulis.
Jason C
(Juga bahkan tabel interupsi mode nyata telah dipindahkan - tidak dikunci ke halaman pertama memori - sejak 8086).
Jason C
1
Jawaban ini melewatkan detail kritis. Ketika interupsi menyala, CPU tidak langsung beralih ke kernel. Sebagai gantinya, ia pertama-tama menyimpan register yang ada, kemudian beralih ke tumpukan lain, dan baru saat itulah kernel dipanggil. Memanggil kernel dengan tumpukan acak dari program acak akan menjadi ide yang agak buruk. Juga, bagian terakhir menyesatkan. Anda tidak akan mendapatkan interupsi "mencoba" untuk membaca memori yang belum dipetakan; itu tidak mungkin. Anda membaca dari alamat virtual dan memori yang tidak dipetakan sama sekali tidak memiliki alamat virtual.
MSalters
5

Kernel OS mendapatkan kendali kembali dari proses yang berjalan karena penangan interupsi jam CPU, bukan dengan menyuntikkan kode ke dalam proses.

Anda harus membaca tentang interupsi untuk mendapatkan lebih banyak klarifikasi tentang cara kerjanya dan bagaimana kernel OS menanganinya dan mengimplementasikan fitur yang berbeda.

Ankur
sumber
Bukan hanya interupsi jam: interupsi apa pun. Dan juga instruksi perubahan mode.
Gilles 'SANGAT berhenti menjadi jahat'
3

Ada adalah metode yang mirip dengan apa yang Anda menggambarkan: multitasking koperasi . OS tidak memasukkan instruksi, tetapi setiap program harus ditulis untuk memanggil fungsi OS yang dapat memilih untuk menjalankan proses koperasi lainnya. Ini memiliki kekurangan yang Anda jelaskan: satu program mogok membuat seluruh sistem. Windows hingga dan termasuk 3.0 bekerja seperti ini; 3.0 dalam "mode terproteksi" dan di atas tidak.

Pre-emptive multitasking (yang normal hari ini) bergantung pada sumber interupsi eksternal. Interupsi mengabaikan aliran kontrol normal dan biasanya menyimpan register di suatu tempat, sehingga CPU dapat melakukan sesuatu yang lain dan kemudian melanjutkan program secara transparan. Tentu saja, sistem operasi dapat mengubah register "ketika Anda meninggalkan interupsi melanjutkan di sini", sehingga ia melanjutkan dalam proses yang berbeda.

(Beberapa sistem lakukan petunjuk penulisan ulang dalam cara yang terbatas pada beban program, yang disebut "thunking", dan prosesor Transmeta dinamis dikompilasi ulang untuk set instruksi sendiri)

pjc50
sumber
AFAICR 3.1 juga kooperatif. Win95 adalah tempat multitasking preemptive masuk. Mode yang dilindungi terutama membawa isolasi ruang alamat (yang memang meningkatkan stabilitas, tetapi untuk alasan yang sebagian besar tidak terkait).
cHao
Thunking tidak menulis ulang atau menyuntikkan kode ke dalam aplikasi. Loader yang dimodifikasi adalah berbasis OS dan bukan produk dari aplikasi. Bahasa interpretatif yang dikompilasi seperti menggunakan kompiler JIT tidak mengubah kode, atau menyuntikkan apa pun ke dalam kode. Mereka menerjemahkan kode sumber menjadi executable. Sekali lagi, ini tidak sama dengan menyuntikkan kode ke dalam aplikasi.
Dave Gordon
Transmeta mengambil kode yang dapat dieksekusi x86 sebagai sumbernya, bukan bahasa interpretatif. Dan saya sudah memikirkan satu kasus di mana kode tersebut disuntikkan: berjalan di bawah debugger. Sistem X86 biasanya menimpa instruksi di breakpoint dengan "INT 03", yang menjebak ke debugger. Saat melanjutkan, opcode asli diaktifkan kembali.
pjc50
Debugging bukan cara orang menjalankan aplikasi; lebih dari itu dari pengembang aplikasi. Jadi saya tidak berpikir itu sangat membantu OP.
Dave Gordon
3

Multi-tasking tidak memerlukan injeksi kode. Dalam sistem operasi seperti Windows, ada komponen kode sistem operasi yang disebut scheduler yang bergantung pada interupsi perangkat keras yang dipicu oleh pengatur waktu perangkat keras. Ini digunakan oleh sistem operasi untuk beralih di antara berbagai program dan program itu sendiri, sehingga semuanya tampak bagi persepsi manusia kita untuk terjadi secara bersamaan.

Pada dasarnya, sistem operasi memprogram timer perangkat keras untuk mati sesering mungkin ... mungkin 100 kali per detik. Ketika timer mati, itu menghasilkan interupsi perangkat keras - sinyal yang memberitahu CPU untuk menghentikan apa yang dilakukannya, menyimpan statusnya di stack, mengubah modenya ke sesuatu yang lebih istimewa, dan mengeksekusi kode yang akan ditemukannya di tempat yang ditunjuk secara khusus tempatkan di memori. Kode itu kebetulan merupakan bagian dari penjadwal, yang memutuskan apa yang harus dilakukan selanjutnya. Mungkin untuk melanjutkan beberapa proses lain, dalam hal ini ia harus melakukan apa yang dikenal sebagai "saklar konteks" - menggantikan keseluruhan keadaan saat ini (termasuk tabel memori virtual) dengan yang dari proses lainnya. Dalam kembali ke suatu proses, ia harus mengembalikan semua konteks proses itu,

Tempat "yang ditunjuk secara khusus" dalam memori tidak harus diketahui oleh apa pun kecuali sistem operasi. Implementasinya bervariasi, tetapi intinya adalah bahwa CPU akan merespons berbagai gangguan dengan melakukan pencarian tabel; lokasi tabel berada pada tempat tertentu dalam memori (ditentukan oleh desain perangkat keras CPU), isi tabel diatur oleh sistem operasi (umumnya saat boot), dan "tipe" interupsi akan menentukan entri mana yang dalam tabel akan digunakan sebagai "layanan rutin interupsi".

Semua ini tidak melibatkan "injeksi kode" ... ini didasarkan pada kode yang terdapat dalam sistem operasi yang bekerja sama dengan fitur perangkat keras CPU dan sirkuit pendukungnya.

Zenilogix
sumber
2

Saya pikir contoh dunia nyata yang paling dekat dengan apa yang Anda gambarkan adalah salah satu teknik yang digunakan oleh VMware , virtualisasi penuh menggunakan terjemahan biner .

VMware bertindak sebagai lapisan di bawah satu atau lebih yang secara bersamaan menjalankan sistem operasi pada perangkat keras yang sama.

Sebagian besar instruksi yang dieksekusi (misalnya dalam aplikasi biasa) dapat divirtualisasi menggunakan perangkat keras, tetapi kernel OS itu sendiri menggunakan instruksi yang tidak dapat divirtualisasi, karena jika kode mesin dari menebak OS dieksekusi tidak dimodifikasi itu akan "pecah "dari kendali host VMware. Misalnya, OS tamu perlu dijalankan di cincin perlindungan yang paling istimewa, dan mengatur tabel interupsi. Jika diizinkan untuk melakukan itu, VMware akan kehilangan kendali atas perangkat keras.

VMware menulis ulang instruksi-instruksi tersebut dalam kode OS sebelum menjalankannya, menggantikannya dengan lompatan ke dalam kode VMware yang mensimulasikan efek yang diinginkan.

Jadi teknik ini agak analog dengan apa yang Anda gambarkan.

Daniel Earwicker
sumber
2

Ada berbagai kasus di mana sistem operasi mungkin "menyuntikkan kode" ke dalam program. Versi 68000 berbasis sistem Apple Macintosh membangun tabel dari semua titik masuk segmen (terletak tepat sebelum variabel global statis, IIRC). Ketika sebuah program dimulai, setiap entri dalam tabel terdiri dari instruksi trap diikuti oleh nomor segmen dan diimbangi ke dalam segmen. Jika jebakan dieksekusi, sistem akan melihat kata-kata setelah instruksi jebakan untuk melihat segmen dan offset apa yang diperlukan, memuat segmen (jika belum), tambahkan alamat awal segmen ke offset, dan lalu ganti perangkap dengan lompatan ke alamat yang baru dihitung tersebut.

Pada perangkat lunak PC lama, meskipun ini tidak secara teknis dilakukan oleh "OS", itu adalah umum untuk kode yang akan dibangun dengan instruksi trap daripada instruksi matematika coprocessor. Jika tidak ada coprocessor matematika yang dipasang, trap handler akan meniru itu. Jika coprocessor dipasang, pertama kali jebakan diambil, pawang akan mengganti instruksi trap dengan instruksi coprocessor; eksekusi masa depan dari kode yang sama akan menggunakan instruksi coprocessor secara langsung.

supercat
sumber
Metode FP masih digunakan pada prosesor ARM, yang tidak seperti CPU x86 masih memiliki varian FP-kurang. Tetapi jarang karena sebagian besar penggunaan ARM ada di perangkat khusus. Dalam lingkungan itu biasanya diketahui apakah CPU akan memiliki kemampuan FP.
MSalters
Dalam kedua kasus ini apakah OS menyuntikkan kode ke dalam aplikasi. Agar OS dapat menyuntikkan kode, diperlukan lisensi oleh vendor perangkat lunak untuk "memodifikasi" aplikasi yang tidak didapatkannya. OS JANGAN menyuntikkan kode.
Dave Gordon
@DaveGordon Intraps yang terperangkap dapat dikatakan sebagai kode injeksi OS ke dalam aplikasi.
Gilles 'SO- berhenti bersikap jahat'
@MSalters Intruksi yang terperangkap biasanya terjadi di mesin virtual.
Gilles 'SO- berhenti bersikap jahat'