Fomos: Sistem Operasi Eksperimental yang Dibangun dengan Rust
(github.com/Ruddle)- Fomos adalah OS eksperimental yang dibangun dengan Rust, dan merupakan proyek untuk memahami ide OS non-Unix serta tantangan pola exo-kernel
- Menyediakan output grafis, alokasi dinamis, pemuatan dan eksekusi aplikasi secara bersamaan, dukungan mouse·keyboard Virtio, serta penjadwalan kooperatif
- Aplikasi diperlakukan sebagai fungsi tunggal berbentuk
pub extern "C" fn _start(ctx: &mut Context) -> i32, dengan struktur yang menerima fungsi OS melaluiContexttanpa pustaka standar Contextadalah struktur yang memuat fungsi dan status seperti log, PID, framebuffer,calloc,cdalloc,store, input, dan lainnya; fitur baru ditambahkan di bagian belakang untuk menjaga kompatibilitas dengan aplikasi lama- Tidak ada system call; aplikasi mengembalikan kontrol ke OS dengan
return, lalu fungsistartdipanggil lagi setelahnya dalam model eksekusi kooperatif - Status aplikasi dapat disimpan di
Context.store, dan loop kernel memiliki struktur sederhana yang menelusuri daftar aplikasi lalu memanggil_start(Context::new(...))untuk masing-masing aplikasi - Karena semua fungsi dan efek samping diteruskan melalui
Context, penyandian sandbox, instrumentasi, dan debugging dapat dibangun dengan mengganti atau membungkus fungsi-fungsi diContext - Saat ini keamanan belum diimplementasikan, dan aplikasi masih dapat memeriksa RAM aplikasi lain; ada rencana untuk menerapkan keamanan data tanpa context switch dan tanpa stack memori virtual per aplikasi
- Hal yang masih belum ada meliputi penyimpanan permanen, dukungan GPU, jaringan, serta abstraksi untuk berbagi data dan fungsi antar aplikasi; Virgl sedang dalam pengerjaan
- Build dijalankan dengan
./build.sh, dan mungkin memerlukanrust nightly,gcc, sertaqemudengan flag Virgl dan SDL
1 komentar
Komentar Hacker News
Hal yang tidak saya suka adalah, dalam sistem preemptive,
while (true)mungkin bisa membuat sistem melambat, tetapi dalam sistem kooperatif, begitu ia tidak mengembalikan kontrol, mesin pada dasarnya berhentiDari sudut pandang keamanan juga, sistem seperti ini membuat serangan denial-of-service terlalu mudah, dan satu bug di sebuah aplikasi bisa merembet ke seluruh sistem
Saya bukan pengembang sistem operasi, jadi tolong koreksi kalau saya salah
Alasan sistem operasi meninggalkan multitasking kooperatif bukan karena mereka menyelesaikan semua masalah “penggunaan sumber daya yang lepas kendali” untuk selamanya, melainkan karena kesalahan sederhana di level aplikasi seperti thread UI yang terblokir atau loop tak berujung yang tidak disengaja bisa merusak keadaan seluruh sistem
Untuk sistem yang dirancang menjalankan program arbitrer, ini cukup fatal
Kita bisa mengizinkan aplikasi dijadwalkan secara kooperatif sambil tetap mencegah
while(true){}menahan sistem tanpa batasMisalnya dengan memberi batas waktu per aplikasi, atau secara lebih eksperimental mendeteksi loop dan memberi batas waktu yang longgar
Bagi programmer, nyaman ketika isolasi antarprogram yang tidak terkait maksimal dan isolasi antarprogram yang terkait minimal, tetapi bagi pengguna, nyaman ketika sumber daya komputasi diisolasi kuat sementara hal seperti penyimpanan atau izin tidak terlalu dibatasi
Dalam praktiknya, kita membutuhkan penjadwalan kompleks yang lebih dekat ke preemptive daripada kooperatif, tetapi sedikit kooperatif
Di Linux pun, program berbahaya seperti fork bomb tidak sulit membuat sistem berhenti, terutama jika swap aktif, dan meski ada scheduler preemptive, jika satu program mengambil 99% thread sistem, pada dasarnya sebagian besar yang berjalan adalah program itu
Penjadwalan adalah spektrum, dan sistem operasi saat ini juga preemptive, tetapi sampai batas tertentu juga kooperatif
Aplikasi bisa memutuskan apakah akan menyerahkan kontrol
Sebaliknya, sistem operasi bisa dibiarkan kooperatif, tetapi jika ambang penggunaan sumber daya atau interupsi timer terjadi, ia jarang beralih konteks sebagai mode kegagalan menjadi preemptive, menghentikan aplikasi, lalu kembali lagi ke kooperatif
Ini bisa disebut kooperatif optimistis dan preemptive pesimistis
Loop
while(true)meruntuhkan sistem single-core, tetapi belum tentu begitu pada sistem multicoreTrade-off sekarang mungkin berbeda dari masa ketika struktur dasar sistem operasi yang kita pakai saat ini dibuat
Saya terutama menyukai bagian “di Fomos, aplikasi hanyalah fungsi”
File executable Unix atau Windows sangat kompleks dibandingkan fungsi mandiri, jadi sulit membayangkan betapa kerennya kernel yang ditulis seperti ini
Saya penasaran apakah Smalltalk/Squeak juga menggunakan cara seperti ini, dan berharap penulis terus melanjutkannya sampai ke sistem berkas, task manager, stack memori yang aman, dan berbagi sumber daya
Tentu saja, sebagai syarat proof-of-concept minimal, menjalankan DOOM juga diperlukan
Sistem operasi mesin Lisp lebih dekat; awalnya fungsi-fungsi mandiri saling memanggil tanpa sistem objek, lalu kemudian menjadi fungsi generik yang dispesialisasi berdasarkan kelas argumen
Tampaknya para perancang belum menemukan mengapa sistem operasi lain akhirnya membutuhkan hal-hal yang sekarang mereka hilangkan
int main() { … }Contoh lain yang dibuat dengan Rust adalah https://github.com/hermit-os/hermit-rs
Idenya bagus, tetapi cara terus menambahkan fungsi baru ke struct Context agar kompatibel dengan item lama adalah jalan menuju neraka kompatibilitas mundur
Itu seperti mengurung diri sendiri sehingga item lama atau yang sudah dibuang tidak bisa dihapus dari struct Context
Cara yang lebih baik tampaknya adalah memperkenalkan semantic versioning antara sistem operasi dan aplikasi
Jika aplikasi menyatakan bahwa ia dibangun untuk, atau bergantung pada, versi sistem operasi tertentu, sistem operasi bisa memeriksa kompatibilitas dan meneruskan versi struct Context yang sesuai
Sebagian besar masalah kompatibilitas mundur tetap ada, tetapi dengan menaruh beberapa struct per versi mayor/minor di dalam kernel, struct Context bisa tetap bersih
Ide bagus, tetapi saya juga suka kesederhanaan karena hanya ada satu runtime interface
Kalau bagaimanapun sistem operasi harus menangani semua versi, aplikasi masa depan juga bisa menggunakan padding agar terasa “bersih”
struct Context{ padding: [u8;256], // old stuff ctx: ContextV42 }Namun setelah menuliskannya, rasanya agak keliru juga
Deklarasi versi oleh aplikasi terasa seperti masalah yang sudah diselesaikan format executable seperti ELF, jadi saya sedang mencoba alternatif
Bagian “bagaimana melakukan sleep atau menunggu secara async? cukup return” terasa agak aneh
I/O asinkron ala
io_uringakan sangat bagus, tetapi model ini tampaknya mengesampingkan hal seperti itu, sehingga mungkin sulit mencapai performa yang layakTidak mendukung async juga terasa aneh, karena itu bisa terhubung ke titik penghentian yang alami
Namun untuk melakukannya, sepertinya harus banyak mengorbankan desain yang secara eksplisit menyimpan dan memuat state aplikasi ke disk, dan biayanya tampak besar
Networking juga, karena alasan serupa, menurut saya bisa menjadi sulit, setidaknya jika ingin efisien
Fungsi ini tampak seperti ujung event loop yang menerima state arbitrer sebagai parameter, jadi apa yang dilakukan event loop kemungkinan besar bisa digeneralisasi
Namun dukungan coroutine dan async di tingkat bahasa harus dikorbankan
Contoh yang diberikan terasa terlalu dibuat-buat
Pada sistem operasi preemptive, aplikasi biasanya berhenti dengan cara yang tidak membuat semuanya menjadi kooperatif, seperti deadlock thread atau infinite loop
Selain itu, sistem preemptive dapat menghentikan aplikasi jauh sebelum sistem itu pada praktiknya menjadi kooperatif jika aplikasi membuat terlalu banyak thread atau file, atau memakai terlalu banyak memori
Sistem kami hanya lebih toleran
Lagi pula, meski mengatakan “sandboxing itu gratis selama Anda menerima premisnya”, mereka juga mengatakan “aplikasi mana pun dapat dengan mudah memeriksa RAM aplikasi lain, dan ini masalah yang sulit dipecahkan”, jadi sandboxing tidak gratis
Namun tetap saja ini ide yang keren, dan saya berharap penulisnya sukses
Solusi untuk masalah ini mungkin adalah membuat fungsi, yakni closure yang membungkus aplikasi, sehingga bertindak seperti Context milik aplikasi itu sendiri
Saya bertanya-tanya, bagaimana jika aplikasi bisa membuka aplikasi, atau aplikasi bisa menjadi sistem operasi bagi aplikasi lain?
Sistem yang dirancang dengan benar untuk sandboxing yang kuat akan memberi batas pada semua resource, dan menolak permintaan ketika batas tercapai
Saya penasaran bagaimana Fomos membedakan proses dan executable file
Di Linux, proses adalah data internal kernel seperti uid dan gid, serta ruang alamat virtual yang mencakup pointer argv/envp, stack, heap, signal mask, tabel file handle, signal handler, dan memori yang dapat dieksekusi
Executable file adalah file yang berisi bit-bit yang cukup bagi loader untuk mengisi ruang alamat itu saat system call
execveProses bisa dibuat tanpa executable file melalui
clone3ataufork; kernel memakai ELF dan sebagian besar userspace memakai loader RTLD dari GLIBC, tetapi keduanya tidak benar-benar diperlukan untuk membuat proses dengan format executable tertentuExecutable yang di-link statis tanpa position-independent code dari sudut pandang assembler lebih dekat ke “sekadar fungsi”, tetapi jika simbol runtime di-resolve tanpa ASLR, ia rentan terhadap serangan buffer overflow ketika alamat fungsi dependensi diketahui
Saya menginginkan alternatif untuk kekurangan glibc dan model proses Posix, tetapi menurut saya sebagian besar kompleksitas executable Unix bersifat esensial
Resolusi simbol runtime memang sulit tetapi berguna, mengizinkan interpreter arbitrer memang merepotkan tetapi merupakan salah satu keunggulan Linux dibanding Windows dan MacOS, dan menyediakan antarmuka kernel melalui system call yang stabil adalah kekuatan Linux
Mac memakai Mach-O, Windows memakai PE, Linux memakai ELF, dan seterusnya, tetapi tidak ada alasan kita tidak bisa memiliki ekosistem format eksekusi/linking yang beragam
Sistem operasi dengan model pemuatan kode yang sangat sederhana adalah tempat yang bagus untuk eksperimen semacam itu
ABI yang stabil juga bukan hal yang unik bagi Linux, dan kegunaan keputusan seperti itu pun cukup meragukan, tetapi dukungan driver memang luar biasa dan sulit dibantah
Cukup menarik
Saya tidak tahu bagaimana bisa mencapai tingkat keamanan dan keselamatan tertentu dengan aplikasi kooperatif yang tidak tepercaya
Aplikasi mana pun bisa menahan CPU tanpa batas waktu dan menghentikan kernel serta aplikasi lain
Alasan kita memakai sistem operasi dengan penjadwalan preemptive adalah karena aplikasi yang bermasalah dapat dihentikan tanpa merusak sisa sistem
Mereka memakai multitasking preemptive tetapi tidak memaksakan proteksi memori; sebagai gantinya, compiler dijadikan layanan sistem sehingga hanya executable yang dibuat dan ditandatangani oleh compiler sistem yang bisa dijalankan
Karena compiler menjamin proteksi memori pada waktu build, system call dan komunikasi antarproses menjadi sangat murah
Dengan compiler yang sedikit lebih canggih, multitasking kooperatif juga bisa dipaksakan dengan cara serupa dengan menyisipkan pemanggilan
yielddi lokasi yang diperlukanHalting problem umum memang tidak bisa dipecahkan, tetapi masih ada kelas program yang dapat dibuktikan melalui analisis statis akan berhenti atau melakukan yield
Hanya program yang tidak dapat dibuktikan yang perlu ditangani secara terpisah, dan program yang bermasalah juga bisa dihentikan otomatis dengan watchdog timer
Kode yang tidak tepercaya tidak dijalankan di image “utama”, melainkan di VM sekali pakai
Di sini pun model yang sama mungkin bisa diterapkan dengan hypervisor, tetapi tidak ada orang yang hanya memakai sistem Smalltalk saja, dan dibutuhkan sejumlah infrastruktur
Saya penasaran apakah keamanan bisa diterapkan di sistem operasi ini tanpa perancangan ulang total, dan pada dasarnya tanpa mengulang apa yang sudah dilakukan sistem operasi yang ada
Saya tahu ada dua cara untuk menegakkan keamanan aplikasi yang berjalan di perangkat keras yang sama
Salah satunya adalah mengisolasi proses dengan memori virtual saat runtime, dan yang lainnya adalah loader memverifikasi pada saat pemuatan apakah kode melakukan akses memori arbitrer
Yang terakhir biasanya ditegakkan dengan mesin virtual yang hanya mengizinkan bytecode dari set instruksi terbatas tanpa operasi pointer, seperti JVM atau Smalltalk
Penulis Fomos tidak menginginkan context switch dan isolasi memori, dan compiler Rust tidak menghasilkan bytecode; apakah ada cara lain?
Sejauh yang saya pahami, karena aturan seperti melarang
unsafeditegakkan oleh compiler tersertifikasi, di sini source code pada dasarnya setara dengan bytecodeSekilas terlihat sangat mirip dengan Midori, tetapi detail implementasinya cukup berbeda
Di Theseus, driver, aplikasi, dan lainnya adalah objek ELF, semuanya di-link secara dinamis menjadi satu berkas executable, yaitu kernel, dan ada juga teknik menarik seperti hot upgrade
https://github.com/theseus-os/Theseus
https://www.theseus-os.com/
Misalnya, saat terjadi segmentation fault pada runtime, sistem memeriksa semacam token keamanan untuk memastikan apakah pemanggil punya hak mengakses dan memanggil halaman tersebut
Saya tidak tahu seberapa praktis ini dalam kenyataan
Meski multitasking kooperatif, sekarang jumlah core sangat banyak, jadi tampaknya tidak sama seperti era Classic MacOS
Satu atau dua proses yang tidak mau menyerahkan kendali belum tentu menahan seluruh sistem
Jika sebuah fungsi berperilaku salah dan tidak pernah kembali, sistem mungkin bisa menghentikannya saat semua core sudah terpakai
Multitasking kooperatif tidak selalu berarti performa buruk
Time-sharing awalnya adalah cara membagi satu CPU raksasa untuk banyak pengguna, tetapi sekarang CPU multi-core untuk satu pengguna sudah umum, jadi sudah saatnya memikirkan cara lain memanfaatkan core
Fakta bahwa proyek ini ada benar-benar membuat saya antusias
Dalam model ini tidak ada context switch, jadi justru ada kemungkinan performanya membaik
Karena itu saya jadi penasaran apa yang akan terjadi jika timeslice Linux dinaikkan ke nilai yang tidak masuk akal seperti 10 detik
Saya ingin mendengar rencana keamanannya secara lebih rinci
Secara umum, saya pikir eksperimen seperti ini menunjukkan bahwa sistem operasi bisa diperbaiki lewat desain greenfield
Sedikit mengingatkan saya pada Mirage OS: https://mirage.io/