
Selamat siang, pembaca yang budiman, kemungkinan besar, Anda melihat artikel saya sebelumnya bahwa Anda sendiri dapat menulis OS yang bisa dikerjakan dalam waktu yang cukup singkat. Nah, hari ini kita akan berbicara tentang implementasi multitasking di OS saya.
Yah, Anda mungkin tidak dapat membayangkan OS satu-tugas pada tahun 2018, jadi saya memutuskan untuk berbicara tentang implementasi multitasking di OS saya. Jadi, hal pertama - Anda perlu memutuskan jenis multitasking, saya memilih preemptive.
Seperti apa dia? Preemptive multitasking adalah sistem untuk mendistribusikan daya komputasi prosesor antar proses: masing-masing memiliki kuantum waktu sendiri, masing-masing memiliki prioritas sendiri. Dan masalah pertama adalah panjang kuantum mana yang harus dipilih, bagaimana cara menghentikan proses agar tidak berjalan setelah kuantum berakhir? Bahkan, semuanya lebih mudah dari sebelumnya! Kami akan menggunakan PIT dengan frekuensi yang awalnya ditetapkan 10026 dengan uang interupsi per detik, di sana kami memecahkan satu masalah lagi: kami sudah menghentikan proses sebelumnya. Jadi, mari kita mulai dengan PIT.
Lubang
PIT - Timer Interval yang Dapat Diprogram - penghitung yang, setelah mencapai jumlah kenaikan terprogram, memberikan sinyal. Juga, dengan menggunakan pengatur waktu ini, Anda dapat mencicit alat pencekik di komputer (benda yang mencicit setelah melewati tes perangkat). Maka, ia menghitung dengan frekuensi 1193182 hertz, yang berarti bahwa kita perlu memprogramnya pada 119 (1193182/119 kira-kira sama dengan 10026). Untuk melakukan ini, kirim 2 byte ke port generator pertama, pertama byte rendah, lalu tinggi:
unsigned short hz = 119; outportb(0x43, 0x34); outportb(0x40, (unsigned char)hz & 0xFF);
Sekarang layak memulai pemrograman interupsi dari PIT, ia memiliki IRQ 0, dan setelah remap PIC akan menjadi 0x20m. Untuk IRQ dari PIC pertama, saya menulis makro ini:
Struktur dan proses
Jadi, seperti yang Anda pahami, kami perlu mengembangkan struktur untuk setiap proses, serta struktur yang memungkinkan saya mengingat semua alokasi memori saya.
Inilah yang saya miliki:
typedef struct _pralloc { void * addr; struct _pralloc * next; } processAlloc; typedef struct { void * entry; processAlloc *allocs; } ELF_Process; typedef struct __attribute__((packed)) _E { unsigned int eax;
Untuk memulainya, kita perlu memahami hal-hal berikut: kita dapat berada di suatu tempat di alamat global, misalnya, di 0xDEAD menuliskan jumlah proses yang sedang berjalan, kemudian ketika menjalankan kode apa pun, kita dapat yakin: kita memiliki jumlah proses yang sedang berjalan, ini berarti bahwa saat mengakses malloc, kami tahu kepada siapa kami mengalokasikan memori, dan kami dapat segera menambahkan alamat memori yang dialokasikan ke daftar allocs.
void addProcessAlloc(ELF_Process * p, void * addr) { void * z = p->allocs; p->allocs = malloc_wo_adding_to_process(sizeof(processAlloc)); p->allocs->addr = addr; p->allocs->next = z; }
Nah, kami menulis struktur tabel dengan deskripsi proses, apa selanjutnya, bagaimana beralih tugas?
Untuk memulainya, saya ingin mencatat bahwa, sebagai contoh, dalam handler, variabel lokal disimpan pada stack, yang berarti bahwa setelah memasukkan handler, kompiler merusak kita esp. Untuk mencegah hal ini terjadi, buat variabel dengan alamat absolut, dan sebelum memanggil handler, kita akan meletakkan ESP di sana. Di handler, kita perlu mengirim EOI ke PIC pertama dan menemukan proses yang kita perlu beralih (saya tidak akan menjelaskan mekanisme prioritas: sederhana, seperti kemacetan lalu lintas). Berikutnya - kita perlu menyimpan semua register dan flag dari proses saat ini, jadi sebelum memasukkan ESP ke dalam variabel, kita akan menyimpan semua register (termasuk yang segment) ke stack. Dalam handler itu sendiri, kita harus dengan hati-hati menghapusnya dari tumpukan, sembari menjaga bendera dan mengembalikan alamat. Saya ingin mencatat bahwa tumpukan tumbuh (mis., ESP berkurang), yang berarti bahwa register terakhir yang Anda simpan di tumpukan akan di ESP, yang kedua dari belakang akan menjadi ESP +4, dll .:

Sekarang tinggal kita untuk meletakkan nilai register proses ke dalam register yang kita gunakan untuk beralih dan menjalankan IRET. Untung!
Proses dimulai
Saat memulai proses, cukup bagi kami untuk mengalokasikan tumpukan untuk proses, dan kemudian meletakkan argc dan argv di dalamnya, alamat fungsi yang akan diberikan kontrol setelah proses selesai. Anda juga perlu mengatur flag prosesor ke nilai yang Anda butuhkan, misalnya, untuk OS saya 0x216, Anda dapat membaca tentang register flag di Wikipedia.
Pada akhirnya saya ingin Anda sukses, segera saya akan menulis tentang bekerja dengan memori dan artikel lain yang menarik bagi Anda.
Semoga beruntung, dan peretasan etis!