Penulisan OS: Multitasking

gambar
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); //Low outportb(0x40, (unsigned char)(hz >> 8) & 0xFF); //Hight, about 10026 times per second 


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:

 //PIC#0; port 0x20 #define IRQ_HANDLER(func) char func = 0x90;\ __asm__(#func ": \npusha \n call __"#func " \n movb $0x20,\ %al \n outb %al, $0x20 \n popa \n iret \n");\ void _## func() 


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;//4 unsigned int ebx;//8 unsigned int ecx;//12 unsigned int edx;//16 unsigned int ebp;//20 unsigned int esp;//24 unsigned int esi;//28 unsigned int edi;//32 unsigned int eflags;//36 unsigned int state;//40 void * startAddr;//44 void * currentAddr;//48 void * stack;//52 unsigned int sse[4 * 8];// unsigned int mmx[2 * 8];//244 unsigned int priority;//248 unsigned int priorityL;//252 void * elf_process;//256 char ** argv;//260 unsigned int argc;//264 unsigned int runnedFrom;//268 char * workingDir;//272 unsigned int cs;//276 - pop is 4 byte in IRET unsigned int ds;//280 } Process; 


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 .:
gambar
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!

Source: https://habr.com/ru/post/id426807/


All Articles