Fomos: Rust में बनाया गया एक प्रयोगात्मक ऑपरेटिंग सिस्टम
(github.com/Ruddle)- Fomos Rust में बनाया गया एक प्रयोगात्मक OS है, और यह Non-Unix OS विचारों तथा exo-kernel पैटर्न की चुनौतियों को समझने के लिए एक प्रोजेक्ट है
- यह ग्राफ़िक्स आउटपुट, डायनेमिक अलोकेशन, कई ऐप्स को एक साथ लोड और रन करना, Virtio माउस·कीबोर्ड समर्थन, और cooperative scheduling प्रदान करता है
- ऐप्स को
pub extern "C" fn _start(ctx: &mut Context) -> i32के रूप में एक single function की तरह संभाला जाता है, और standard library के बिना OS फीचर्सContextके माध्यम से दिए जाते हैं Contextएक struct है जिसमें लॉग, PID, framebuffer,calloc,cdalloc,store, इनपुट जैसी क्षमताएँ और state शामिल हैं, और नई क्षमताएँ पीछे जोड़कर पुराने ऐप्स के साथ compatibility बनाए रखी जाती है- कोई system call नहीं है; ऐप
returnके ज़रिए नियंत्रण OS को वापस देता है, और बाद मेंstartfunction फिर से कॉल होता है — यह एक cooperative execution model है - ऐप state को
Context.storeमें सहेजा जा सकता है, और kernel loop ऐप सूची पर क्रम से चलते हुए हर ऐप के_start(Context::new(...))को कॉल करने वाली एक सरल संरचना है - चूँकि सभी फीचर्स और side effects
Contextके माध्यम से पास होते हैं, इसलिएContextकी functions को replace या wrap करके sandboxing, instrumentation, और debugging बनाने की परिकल्पना है - अभी security लागू नहीं है, और ऐप्स दूसरी ऐप्स की RAM देख सकते हैं; context switch और per-app virtual memory stack के बिना data security लागू करने की योजना है
- अभी जिन चीज़ों की कमी है उनमें persistent storage, GPU support, networking, और ऐप्स के बीच data व functionality share करने के लिए abstraction शामिल हैं; Virgl पर काम चल रहा है
- बिल्ड
./build.shसे चलती है, और इसके लिएrust nightly,gcc, तथा Virgl और SDL flags के साथqemuकी आवश्यकता हो सकती है
1 टिप्पणियां
Hacker News की रायें
जो बात पसंद नहीं आई वह यह है कि preemptive system में
while (true)सिस्टम को धीमा कर सकता है, लेकिन cooperative system में जैसे ही वह control वापस नहीं करता, मशीन व्यावहारिक रूप से रुक जाती हैsecurity के नज़रिए से भी ऐसे सिस्टम में denial-of-service attack बहुत आसान हो जाता है, और किसी एक app का bug पूरे सिस्टम में फैल सकता है
मैं operating system developer नहीं हूँ, इसलिए अगर गलत हूँ तो सुधार दें
operating systems ने cooperative multitasking इसलिए नहीं छोड़ी कि उन्होंने “बेकाबू resource usage” की सभी समस्याएँ हमेशा के लिए हल कर दी थीं, बल्कि इसलिए कि UI thread block होना या अनजाने में infinite loop जैसे साधारण app-level errors पूरे सिस्टम की state बिगाड़ देते हैं
ऐसे सिस्टम में जो मनमाने programs चलाने के लिए design किया गया हो, यह काफी गंभीर है
apps को cooperatively scheduling करने की अनुमति देते हुए भी
while(true){}को सिस्टम को अनंत समय तक पकड़े रखने से रोका जा सकता हैउदाहरण के लिए, हर app के लिए time limit रखी जा सकती है, या और experimental तरीके से loops detect करके generous time limit दी जा सकती है
programmers के लिए सुविधा तब होती है जब unrelated programs के बीच isolation अधिकतम हो और related programs के बीच isolation न्यूनतम हो, लेकिन users के लिए तब सुविधा होती है जब compute resources कड़े isolation में हों और storage या permissions जैसी चीजें कम बाधित हों
असल में एक complex scheduling चाहिए जो cooperative से ज्यादा preemptive के करीब हो, लेकिन थोड़ा cooperative भी हो
Linux में भी fork bomb जैसे malicious programs, खासकर swap enabled हो तो, सिस्टम को रोक देना मुश्किल नहीं बनाते; और preemptive scheduler होने पर भी अगर एक program system threads के 99% पर कब्ज़ा कर ले, तो व्यावहारिक रूप से ज्यादातर वही program चलता है
scheduling एक spectrum है, और मौजूदा operating systems preemptive हैं, लेकिन कुछ हद तक cooperative भी हैं
app तय कर सकता है कि control yield करना है या नहीं
उल्टा, operating system को cooperative रखा जा सकता है, और resource usage threshold या timer interrupt होने पर कभी-कभार failure mode के रूप में context switch करके preemptive बना जा सकता है, app को terminate करने के बाद फिर cooperative mode में लौटा जा सकता है
इसे optimistic cooperative, pessimistic preemptive कहा जा सकता है
while(true)loop single-core system को गिरा देता है, लेकिन multi-core system में जरूरी नहीं कि ऐसा होजिस दौर में आज इस्तेमाल होने वाले operating systems की मूल संरचना बनी थी और आज के बीच trade-offs बदल गए हो सकते हैं
खासकर “Fomos में app बस एक function है” वाला हिस्सा अच्छा लगा
Unix या Windows executables, standalone functions की तुलना में बहुत जटिल होते हैं, इसलिए इस तरह लिखे गए kernel की कल्पना करना भी मुश्किल है कि कितना शानदार होगा
जानना चाहता हूँ कि Smalltalk/Squeak भी इसी तरह है या नहीं, और उम्मीद है कि लेखक file system, task manager, safe memory stack, resource sharing तक इसे आगे बढ़ाएँगे
बेशक minimum proof-of-concept requirement के तौर पर DOOM चलाना भी जरूरी है
Lisp machine operating system इससे ज्यादा करीब है; शुरू में object system के बिना standalone functions एक-दूसरे को call करते थे, और बाद में argument class के लिए specialized generic functions बन गए
लगता है designers ने अभी यह discover नहीं किया है कि दूसरे operating systems को वे चीजें क्यों चाहिए पड़ीं जो इसमें अभी छोड़ी गई हैं
int main() { … }function होता है, यह जानना चाहूँगाRust में बना एक और उदाहरण https://github.com/hermit-os/hermit-rs है
idea ठीक है, लेकिन पुराने entries के साथ compatible रहने के लिए Context struct में नए functions लगातार जोड़ते रहना backward compatibility hell की ओर जाने वाला रास्ता है
यह अपने-आप को ऐसे फँसाने जैसा है कि पुराने या deprecated entries को Context struct से हटाया ही न जा सके
बेहतर तरीका operating system और apps के बीच semantic versioning लाना लगता है
अगर app declare करे कि वह किस operating system version के लिए build हुआ है या किस पर depend करता है, तो operating system compatibility check करके उसी के अनुसार Context struct का version pass कर सकता है
backward compatibility की ज्यादातर समस्याएँ रहेंगी, लेकिन kernel के अंदर major/minor versions के हिसाब से कई structs रखकर Context struct को साफ रखा जा सकता है
अच्छा idea है, लेकिन मुझे runtime interface सिर्फ एक होने की simplicity भी पसंद है
वैसे भी अगर operating system को सभी versions handle करने ही हैं, तो future apps padding का इस्तेमाल करके इसे “साफ” महसूस करा सकते हैं
struct Context{ padding: [u8;256], // old stuff ctx: ContextV42 }हालांकि यह लिखकर देखता हूँ तो थोड़ा गलत-सा लगता है
app का अपना version declare करना ELF जैसे executable formats पहले ही solve करते हैं, ऐसा लगता है, इसलिए मैं alternatives आज़मा रहा हूँ
“sleep या async wait कैसे करें? बस return कर दें” वाला हिस्सा थोड़ा अजीब है
io_uringजैसी asynchronous I/O शानदार होगी, लेकिन यह model मानो ऐसी चीजों को exclude करता है, इसलिए उचित performance निकालना मुश्किल हो सकता हैasync support न होना भी अजीब है, क्योंकि उसे natural suspension points से जोड़ा जा सकता है
हालांकि ऐसा करने के लिए application state को disk पर explicitly save और load करने वाले design का काफी हिस्सा छोड़ना पड़ेगा, और cost बड़ी लगती है
networking भी इसी तरह की वजहों से, कम से कम efficiently करना मुश्किल हो सकता है
यह function arbitrary state को parameter के रूप में लेने वाले event loop के end point जैसा दिखता है, इसलिए event loop जो काम करता है उसे काफी हद तक generalize किया जा सकता है
हालांकि language-level coroutines और async support छोड़ना पड़ेगा
दिया गया उदाहरण बहुत बनावटी है
preemptive operating system में ऐप आम तौर पर thread deadlock या infinite loop जैसी स्थितियों में रुकता है, न कि पूरे सिस्टम को cooperative बना देने वाले तरीके से
साथ ही, preemptive system में अगर कोई ऐप बहुत ज़्यादा threads या files बना दे, या बहुत ज़्यादा memory इस्तेमाल करे, तो उसे असल में cooperative बनने से बहुत पहले terminate किया जा सकता है
हमारा system बस थोड़ा ज़्यादा उदार है
ऊपर से, एक तरफ़ कहा गया है कि “अगर premise मान लें तो sandboxing free है”, और दूसरी तरफ़ “कोई भी ऐप दूसरे ऐप की RAM आसानी से देख सकता है, और यह हल करने में मुश्किल समस्या है”, तो sandboxing free नहीं है
फिर भी यह बढ़िया idea है और उम्मीद है लेखक सफल हों
इस समस्या का हल शायद functions, यानी applications को wrap करने वाला closure बनाना हो सकता है, जो ऐप के अपने Context की तरह काम करे
सोचता हूँ, अगर कोई ऐप किसी दूसरे ऐप को खोल सके, या कोई ऐप किसी दूसरे ऐप के लिए operating system बन सके, तो कैसा होगा
मज़बूत sandboxing के लिए सही तरह से design किया गया system हर resource पर limits लगाता है, और limit पहुंचने पर request को reject कर देता है
मैं जानना चाहूंगा कि Fomos process और executable file में फर्क कैसे करता है
Linux में process kernel का internal data होता है, जिसमें argv/envp pointers, stack, heap, signal mask, file handle table, signal handlers, executable memory सहित virtual address space और uid, gid जैसी चीज़ें शामिल होती हैं
executable file वह file है जिसमें
execvesystem call के समय loader द्वारा उस address space को भरने लायक bits होती हैंexecutable file के बिना भी
clone3याforkसे process बनाया जा सकता है, और kernel ELF इस्तेमाल करता है व user space का बड़ा हिस्सा GLIBC के RTLD loader को इस्तेमाल करता है, लेकिन किसी खास executable file format से process बनाने के लिए ये दोनों अनिवार्य नहीं हैंposition-independent code के बिना static-linked executable assembler के नज़रिए से “सिर्फ़ function” जैसा होता है, लेकिन ASLR के बिना runtime symbols resolve करने पर dependency function addresses पता चलने की स्थिति में यह buffer overflow attack के लिए vulnerable हो जाता है
मैं glibc की कमियों और Posix process model के विकल्प चाहता हूँ, लेकिन Unix executable की complexity का बड़ा हिस्सा मुझे मूलभूत लगता है
runtime symbol resolution कठिन है लेकिन उपयोगी है, arbitrary interpreters allow करना झंझट भरा है फिर भी यही वह चीज़ है जिसमें Linux, Windows और MacOS से मज़बूत है, और stable system calls के ज़रिए kernel interface देना Linux की ताकत है
जैसे Mac में Mach-O, Windows में PE और Linux में ELF है, लेकिन execution/linking formats का विविध ecosystem न होने की कोई वजह नहीं है
code load करने का model बहुत simple रखने वाला operating system ऐसे experiments के लिए अच्छी जगह है
stable ABI Linux की कोई unique बात भी नहीं है और उस decision की usefulness भी काफी questionable लगती है, लेकिन driver support शानदार है और इसे नकारना मुश्किल है
यह काफी दिलचस्प है
समझ नहीं आता कि untrusted cooperative apps के साथ किस हद तक security और safety हासिल की जा सकती है
कोई भी ऐप CPU को अनिश्चित काल तक पकड़े रखकर kernel और दूसरे apps को रोक सकता है
हम preemptive scheduling operating system इसलिए इस्तेमाल करते हैं, क्योंकि खराब व्यवहार कर रहे ऐप को बाकी system को बिगाड़े बिना रोका जा सकता है
उसने preemptive multitasking इस्तेमाल की थी, लेकिन memory protection enforce नहीं किया था; इसके बजाय compiler को system service के रूप में रखा था ताकि केवल system compiler द्वारा बनाए और sign किए गए executable files ही चल सकें
compiler build time पर memory protection guarantee करता था, इसलिए system calls और inter-process communication बहुत सस्ते हो गए थे
थोड़ा अधिक sophisticated compiler ज़रूरी जगहों पर
yieldcalls insert करके cooperative multitasking को भी इसी तरह enforce कर सकता हैgeneral halting problem हल नहीं हो सकता, लेकिन static analysis से जिन program classes के terminate या yield करने को prove किया जा सके, वे फिर भी मौजूद हैं
सिर्फ़ उन programs को अलग से handle करना होगा जिन्हें prove नहीं किया जा सकता, और watchdog timer से misbehaving programs को अपने-आप रोक भी सकते हैं
untrusted code को “main” image में नहीं चलाया जाता, बल्कि ऐसी VM में चलाया जाता है जिसे फेंका जा सके
यहां भी hypervisor का इस्तेमाल करके वही model लागू किया जा सकता है, लेकिन कोई भी सिर्फ़ Smalltalk system अकेले इस्तेमाल नहीं करता और कुछ infrastructure की ज़रूरत होती है
बिना पूरी तरह redesign किए, यानी मौजूदा operating systems ने जो काम पहले ही कर लिया है उसे दोबारा किए बिना, इस operating system में security लागू की जा सकती है या नहीं—यह जानने की उत्सुकता है
एक ही hardware पर चलने वाली applications की security enforce करने के दो तरीके मुझे पता हैं
एक तरीका runtime पर virtual memory से processes को isolate करना है, और दूसरा load time पर loader द्वारा यह verify करना है कि code arbitrary memory access करता है या नहीं
दूसरा तरीका आम तौर पर JVM या Smalltalk जैसी virtual machine से enforce किया जाता है, जो pointer operations के बिना सीमित instruction set वाले bytecode को ही allow करती है
Fomos के author context switching और memory isolation वगैरह नहीं चाहते, और Rust compiler bytecode नहीं बनाता—तो क्या कोई और तरीका है?
मेरी समझ के अनुसार, certified compiler में
unsafeपर रोक जैसे rules enforce किए जाते हैं, इसलिए यहाँ source code वास्तव में bytecode की भूमिका निभाता हैपहली नज़र में यह Midori से बहुत मिलता-जुलता लगता है, लेकिन implementation details काफी अलग हैं
Theseus में drivers और applications वगैरह ELF objects होते हैं, और सभी को एक ही executable, यानी kernel, में dynamically link किया जाता है; hot upgrades जैसी दिलचस्प techniques भी हैं
https://github.com/theseus-os/Theseus
https://www.theseus-os.com/
runtime पर segmentation fault होने पर caller को उस page तक access कर call करने का अधिकार है या नहीं, यह किसी security token जैसी चीज़ से check किया जाए
असल में यह कितना practical होगा, पता नहीं
cooperative multitasking होने पर भी, आज cores बहुत ज़्यादा हैं, इसलिए यह Classic MacOS के दौर जैसा नहीं होगा
yield न करने वाली एक-दो processes ज़रूरी नहीं कि पूरे system को ही रोक दें
अगर कोई function गलत behave करके return नहीं करता, तो system सभी cores इस्तेमाल हो जाने पर उसे terminate भी कर सकता है
cooperative multitasking का मतलब जरूरी नहीं कि खराब performance हो
time-sharing मूल रूप से एक विशाल single CPU को कई users में बाँटने का तरीका था, लेकिन अब single-user multi-core CPUs आम हैं, इसलिए cores का उपयोग करने के दूसरे तरीकों पर सोचने का समय काफी पहले आ चुका है
यह project मौजूद है, यह बात सचमुच उत्साहित करती है
इस model में context switching नहीं है, इसलिए उल्टा performance बेहतर होने की संभावना है
इसलिए सोचता हूँ कि Linux की timeslice को 10 seconds जैसी बेतुकी value तक बढ़ा दें तो क्या होगा
security plan के बारे में और विस्तार से सुनना चाहूँगा
कुल मिलाकर, मुझे लगता है कि ऐसे experiments दिखाते हैं कि operating system को greenfield design से बेहतर बनाया जा सकता है
Mirage OS की थोड़ी याद आती है: https://mirage.io/