JVM ที่เขียนด้วย Rust
(andreabergia.com)- โปรเจกต์ย่อย
rjvmที่ทำขึ้นเพื่อเรียนรู้ Rust เป็น JVM แบบของเล่นเพื่อการเรียนรู้ แต่ก็พัฒนาไปถึงขั้นอ่าน.classและjarจริง รวมถึงรัน Java bytecode ได้ - ฟีเจอร์ที่ไม่รองรับคือ thread, reflection, annotation, I/O, JIT compiler และ string interning แต่มี control flow, การสร้าง object, การเรียก method, exception และ garbage collection
- ตัวอย่างใช้
rt.jarจริงของ OpenJDK 7 ทำให้ class อย่างjava.lang.StackTraceElementถูกโหลดจาก JDK จริง - โค้ดแบ่งเป็น Rust crate สามตัว ได้แก่
reader,vm,vm_cliครอบคลุมตั้งแต่การ parse.classไปจนถึง call stack, native method และการ model value กับ object - milestone สุดท้ายคือ stop-the-world semispace copying collector และหลังจากบรรลุเป้าหมายแล้วโปรเจกต์ก็หยุดลง โดยไม่มีแผนแก้ bug ที่ทราบอยู่แล้ว
rjvm JVM ภาษา Rust เพื่อการเรียนรู้
rjvmคือ toy Java Virtual Machine ที่สร้างขึ้นเพื่อเรียนรู้ Rust- โค้ดเปิดเผยอยู่บน GitHub
- เป็น implementation เพื่อการเรียนรู้มากกว่าจะเป็น JVM ที่จริงจัง จึงไม่รองรับฟีเจอร์ต่อไปนี้
- thread
- reflection
- annotation
- I/O
- JIT compiler
- string interning
- generics เดิมอยู่ในรายการที่ไม่รองรับ แต่ในความเป็นจริงใช้งานได้
พัฒนาไปถึงขั้นไหนแล้ว
rjvmไม่ได้เป็นแค่ parser ธรรมดา แต่ implement พฤติกรรมของ JVM หลายอย่างเอง- control flow เช่น
if,for - primitive type และการสร้าง object
- การเรียก virtual method และ static method
- exception handling
- garbage collection
- การ resolve class จากไฟล์
jar
- control flow เช่น
- test code มีตัวอย่างที่ใช้
Throwable,Exception,StackTraceElement - เนื่องจากไม่มี I/O จริง จึงใช้ native method ชื่อ
tempPrintแทนSystem.out.println - ตัวอย่างใช้
rt.jarจริงของ OpenJDK 7 และjava.lang.StackTraceElementก็นำมาจาก JDK จริง
Rust crate สามตัว
- โปรเจกต์นี้เป็นโปรเจกต์ Rust มาตรฐาน และประกอบด้วย crate สามตัว
reader: มี type สำหรับอ่านไฟล์.classและ model เนื้อหาภายในvm: ให้ virtual machine ที่รันโค้ดได้ในรูปแบบ libraryvm_cli: มี command-line launcher แบบง่าย คล้าย executablejava
- กำลังพิจารณาแยก crate
readerออกเป็น repository ต่างหากและเผยแพร่บน crates.io
การ parse ไฟล์ .class
- Java code ถูก compile ด้วย
javacเป็นไฟล์.classและโดยทั่วไปเผยแพร่เป็นไฟล์.jarซึ่งเป็นรูปแบบzip - หากต้องการรัน Java code ต้องโหลดไฟล์
.classที่มี bytecode ซึ่ง compiler สร้างขึ้นก่อน - ไฟล์ class มีข้อมูลที่จำเป็นต่อการรันและการ resolve type อยู่ด้วยกัน
- metadata เช่นชื่อ class และชื่อ source file
- ชื่อ superclass
- interface ที่ implement
- field และ type ของ field, annotation
- method descriptor, clause
throws, annotation, ข้อมูล generics - bytecode, exception handler table, line number table
- crate
readerparse ไฟล์ class แล้วคืนค่าเป็น Rust struct ที่ model class และเนื้อหาภายใน
การรัน method และ call stack
- API หลักของ crate
vmคือVm::invokeใช้สำหรับรัน method - method แต่ละตัวที่กำลังรันมี
CallFrameหนึ่งตัวอยู่ในCallStack - เมื่อรัน
mainตอนแรก call stack จะว่าง และจะสร้าง frame ใหม่เพื่อเริ่มรัน - ทุกครั้งที่เรียก function จะเพิ่ม frame ใหม่เข้าไปใน call stack และเมื่อ method รันจบ frame นั้นจะถูกนำออก
- method ส่วนใหญ่ implement ด้วย Java bytecode แต่
rjvmรองรับ native method ด้วย- native method คือ method ที่ JVM เอง implement โดยตรง ไม่ใช่ Java bytecode
- ตัวอย่างเช่น
System::currentTimeMillis,System::arraycopy,Throwable::fillInStackTrace - ใน
rjvmimplement เป็น Rust function
- JVM เป็น stack-based virtual machine ดังนั้นคำสั่ง bytecode ส่วนใหญ่ทำงานบน value stack
- แต่ละ call frame ผูกกับ value stack และชุด local variable ที่ระบุด้วย index
การ model value และ object
Valueใช้ model ค่าที่เก็บใน local variable, stack element และ object field ได้Valueimplement ด้วย Rustenumและมี state ต่อไปนี้UninitializedInt(i32)Long(i64)Float(f32)Double(f64)Object(AbstractObject<'a>)Null
- sum type อย่าง
enumของ Rust เหมาะกับการแสดงว่าค่าหนึ่งอาจเป็นหนึ่งในหลาย type - การจัดเก็บ object ตอนแรกเริ่มจาก struct
Objectแบบง่ายที่ใช้Vec<Value>เก็บ reference ไปยัง class และค่า field - หลังจาก implement garbage collector แล้ว เปลี่ยนเป็น implementation ระดับต่ำลงที่ใช้ pointer และ cast จำนวนมาก
- ปัจจุบัน
AbstractObjectmodel object หรือ array จริง โดยเป็น pointer ไปยัง byte array ที่มี header word หลายตัวและค่า field
การรันคำสั่ง bytecode
- การรัน method คือกระบวนการประมวลผลคำสั่ง bytecode ทีละคำสั่ง
- คำสั่ง JVM มีมากกว่า 200 คำสั่ง และใน bytecode จะ encode ด้วย 1 byte
- คำสั่งจำนวนมากมี argument ตามมา และบางคำสั่งมีความยาวแบบแปรผัน
rjvmmodel คำสั่ง Java bytecode ด้วย typeInstruction- เมื่อรัน method จะรักษา value stack และ array ของ local variable ไว้ และกำหนดค่าเริ่มต้นของ program counter ซึ่งเป็น address ของคำสั่งถัดไปที่จะรันเป็น 0
- โดยทั่วไปหลังรันคำสั่งแล้ว program counter จะขยับไปคำสั่งถัดไป แต่คำสั่ง jump สามารถย้ายไปตำแหน่งอื่นได้
- คำสั่ง jump ใช้ implement control flow statement เช่น
if,for,while - มีชุดคำสั่งแยกต่างหากสำหรับเรียก method อื่น
- วิธีตัดสินใจว่าจะเรียก method ใดมีทั้ง virtual lookup และ static lookup เป็นต้น
- หลัง resolve method เป้าหมายแล้ว จะเพิ่ม frame ใหม่เข้าไปใน call stack และเริ่มรัน
- หาก return value ไม่ใช่
voidจะ push return value ลง stack แล้วกลับมารันต่อ
Exception handling
- exception ทำให้ control flow ปกติถูกรบกวน, method return ก่อนเวลา และอาจ propagate ไปตาม call stack จึง implement ได้ซับซ้อน
catchblock แต่ละตัวสอดคล้องกับ entry หนึ่งใน exception table ของ method- entry ใน exception table มีข้อมูลที่จำเป็นสำหรับค้นหา handler
- ช่วง program counter ที่มีผล
- address ของคำสั่งแรกใน
catchblock - ชื่อ class ของ exception ที่ block นั้นจับ
CallFrame::execute_instructionใช้Resultของ Rust เพื่อแสดงผลลัพธ์ของการรันคำสั่ง- ผลลัพธ์ของการรันคำสั่งแบ่งเป็นสี่ state
- สำเร็จและรัน method ปัจจุบันต่อ
- สำเร็จและเป็นคำสั่ง
returnทำให้ method ปัจจุบันจบพร้อม return value - ล้มเหลวเพราะ error ภายใน VM
- ล้มเหลวเพราะมี Java exception ถูก throw
- loop รัน method จะ parse คำสั่ง ขยับ program counter ไป address ถัดไป แล้วรันคำสั่ง
- เมื่อเกิด exception จะค้นหา exception handler ที่ตรงกับตำแหน่งคำสั่งปัจจุบัน
- หากไม่มี handler จะ propagate exception ไปยัง caller
- หากมี handler จะ push object exception กลับลง stack และรันต่อจากตำแหน่ง handler ของ
catch
Resultและ pattern matching ของ Rust เหมาะกับการแสดงพฤติกรรมนี้ในโครงสร้างโค้ด
Garbage collection
- milestone สำคัญสุดท้ายของ
rjvmคือการ implement garbage collector - อัลกอริทึมที่เลือกคือ stop-the-world semispace copying collector
- เนื่องจากไม่มี thread วิธี stop-the-world จึงเกิดขึ้นได้ตามธรรมชาติ
- implementation เป็นรูปแบบที่เรียบง่ายกว่าของ Cheney's algorithm และโค้ดอยู่ใน gc.rs
- วิธีนี้แบ่งหน่วยความจำที่ใช้ได้ออกเป็น semispace สองส่วน
- ส่วนหนึ่งเป็น active area สำหรับ allocate object
- อีกส่วนเหลือไว้เป็นพื้นที่ที่ยังไม่ใช้
- เมื่อ active area เต็ม จะ copy object ที่ยังมีชีวิตไปยัง semispace อีกฝั่ง
- อัปเดต reference ทั้งหมดของ object ให้ชี้ไปยังสำเนาใหม่
- สลับบทบาทของ semispace ทั้งสอง
- ขั้นตอนนี้ถูกเปรียบว่าเป็นวิธีที่คล้ายกับ blue-green deployment
- ข้อดีข้อเสียของอัลกอริทึมนี้ชัดเจน
- ใช้หน่วยความจำสูงสุดได้เพียงครึ่งเดียว จึง สิ้นเปลืองหน่วยความจำ มาก
- การ allocate เร็วมาก เพราะใช้วิธีเพิ่ม pointer
- เนื่องจาก copy และ compact object จึงไม่ต้องจัดการ memory fragmentation
- การ compact object อาจเพิ่ม performance จากการใช้ cache line ได้ดีขึ้น
- Java VM จริงโดยทั่วไปใช้ generational garbage collector ที่ซับซ้อนกว่า เช่น G1 หรือ parallel GC
จุดสิ้นสุดของโปรเจกต์
- ระหว่างสร้าง
rjvmได้เรียนรู้ Rust และการ implement virtual machine ไปมาก โดยเฉพาะพอใจกับการ implement garbage collector ที่ใช้งานได้จริง - garbage collector ยังไม่ได้สมบูรณ์มากนัก แต่ใช้งานได้จริง
- เนื่องจากบรรลุเป้าหมายเดิมแล้ว โปรเจกต์จึงหยุดไว้ที่นี่
- มี bug ที่ทราบอยู่แล้ว แต่ไม่มีแผนจะแก้ไข
- Rust เป็นภาษาที่ใช้งานอย่างสนุกในการ implement JVM และในบทความต่อๆ ไปจะเจาะรายละเอียด implementation ของ
rjvmและวิธีทำงานของ JVM มากขึ้น
1 ความคิดเห็น
ความเห็นจาก Hacker News
ส่วนที่ยากในการทำ garbage collection คือการทำให้แน่ใจว่าการอ้างอิงทั้งหมดถูกจับเป็น root อย่างถูกต้อง โดยเฉพาะกับตัวเก็บกวาดแบบย้ายตำแหน่งวัตถุ
เมธอด
do_garbage_collectionถูกทำเครื่องหมายเป็นunsafe[1] แต่ไม่มีคำอธิบายว่าฝั่งที่เรียกต้องรับประกันอะไรบ้างจึงจะเรียกได้อย่างปลอดภัยเลยสงสัยว่ารับประกันได้อย่างไรว่ารีเฟอเรนซ์ทั้งหมดไปยัง heap จะถูกจับเป็น root ซึ่งนี่ไม่ใช่ปัญหาเล็กน้อยเลย[2][3][4]
อีกอย่าง พอลอง clone รีโพซิทอรีแล้วรัน
cargo testดู ปรากฏว่าทุกเทสล้มเหลวด้วยข้อผิดพลาดshould be able to add entries to the classpath: InvalidEntry(".../vm/rt.jar"):vm/tests/integration/real_code_tests.rs:15:10[1] https://github.com/andreabergia/rjvm/blob/be9c54066c64a82879...
[2] https://manishearth.github.io/blog/2021/04/05/a-tour-of-safe...
[3] https://without.boats/blog/shifgrethor-iii/
[4] https://coredumped.dev/2022/04/11/implementing-a-safe-garbag...
ดังนั้นจึงสามารถไล่ดูสแตกนั้นเพื่อหาอาร์กิวเมนต์และตัวแปรภายใน แล้วใช้เป็น root ได้
วิธีนี้มีต้นทุนด้านประสิทธิภาพ แต่ทำให้การติดตามของ garbage collection ง่ายขึ้นมาก และยังทำให้ฟีเจอร์พื้นฐานด้านการทำงานพร้อมกันและการควบคุมโฟลว์อย่าง coroutine หรือ continuation ทำได้ง่ายขึ้นด้วย
เป็นโปรเจ็กต์ที่ดีและขอแสดงความยินดี
แต่ส่วนที่ว่า “สิ่งที่ยังไม่รองรับ: generics” ฟังดูแปลกนิดหน่อย
เลยสงสัยว่าบน JVM นั้นการรองรับ generics ควรหมายถึงอะไร
ในระดับไบต์โค้ด เพราะมี type erasure อยู่แล้ว ไม่ใช่ว่าจะมองทุกอย่างเป็น
Objectหรือก็คือ reference type ได้เลยหรือ? หรือว่าหมายถึงตัวพาร์สคำนิยามคลาส? ถึงอย่างนั้นก็ดูเหมือนไม่มีตรรกะสำหรับตรวจสอบว่าคลาสไฟล์ถูกต้องหรือไม่ นอกเหนือจากไวยากรณ์พื้นฐานสิ่งที่ต้องทำจริง ๆ ก็คงแค่อ่าน Signature attribute ที่เก็บข้อมูล generics ของคลาส เมธอด และฟิลด์ (https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.ht...)
เพิ่งลองเทสดูเมื่อกี้ โค้ดด้านล่างก็ทำงานได้ :-)
public class Generic { public static void main(String[] args) { List strings = new ArrayList(10); strings.add("hey"); strings.add("hackernews"); for (String s : strings) { tempPrint(s); } } private static native void tempPrint(String value); }checkcastก็ได้: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.ht...ตัวอย่างเช่นจะถูกสร้างเมื่อเขียนโค้ดแบบ
final Main value = list.get(0);http://henrikeichenhardt.blogspot.com/2013/05/how-are-java-g...
ในทางกลับกัน การไม่มี string interning นี่แปลกมาก เพราะมันก็ไม่ได้ยากอะไรนักในการทำ และถ้าไม่มีสิ่งนี้ก็คงเรียกว่าเป็น JVM ได้ยาก
การที่สตริงเท่ากันในเชิง reference นั้นสำคัญและเป็นส่วนหนึ่งของ JLS
ส่วนการไม่มีเธรดก็ทำให้ความพยายามทั้งหมดนี้ยังอยู่ในระดับโปรเจ็กต์ของเล่น
เจ๋งมาก ตอนที่ผมเข้าร่วมงาน Java ในปี 1992 มันยังใช้ชื่อว่า Oak และกลุ่มที่ผมอยู่ก็มองไปในทิศทางของการเขียนระบบปฏิบัติการทั้งตัวด้วย Java
แนวคิดคือถ้าเหลือไว้แค่ส่วนขั้นต่ำที่จำเป็นใน “ภาษาเครื่อง” หรือเมธอดเนทีฟ ก็จะช่วยลดพื้นผิวการโจมตีของระบบปฏิบัติการแบบฝังตัวได้
เดิมที Java มีเป้าหมายให้รันบนอุปกรณ์อย่างทีวีหรือเครื่องใช้ไฟฟ้า และในตอนนั้นเมธอดเนทีฟก็ทำด้วย C ไม่ใช่ Rust
JVM ที่เขียนด้วย Rust เพิ่ม ความปลอดภัยของหน่วยความจำ ให้กับกระบวนการทั้งหมดนี้ได้อย่างมาก
ตรรกะของระบบปฏิบัติการจำนวนมากเขียนด้วย Java หรือ Kotlin แต่ขณะเดียวกันก็ผสมกับ system service ที่เป็น native code อยู่มาก และเชื่อมต่อกันด้วย Binder IPC ที่ทั้งโด่งดังและมีชื่อเสีย
[0] - https://en.wikipedia.org/wiki/Singularity_(operating_system)
[1] - https://en.wikipedia.org/wiki/Midori_(operating_system)
ยังมี implementation ของ JVM 17 ที่เขียนด้วย Go อย่าง https://jacobin.org/ ที่น่าดูเหมือนกัน
Jacobins คือชมรมการเมืองปฏิวัติในช่วงการปฏิวัติฝรั่งเศสยุคทศวรรษ 1790 และยังเป็นชื่อของนิตยสารที่ https://jacobin.com ด้วย
สงสัยว่าติดข้อจำกัดเพราะ พารามิเตอร์ lifetime ใน signature นี้หรือเปล่า
fn execute_instruction(&mut self, vm: &mut Vm<'a>, call_stack: &mut CallStack<'a>, instruction: Instruction) -> Result, MethodCallFailed<'a>>ถ้าเพิ่ม lifetime เข้าไปใน
Errvariant ของResultและ lifetime นั้นเป็น invariant ซึ่งในที่นี้ดูเหมือนจะเป็นเช่นนั้นเพราะvmกับcall_stackปกติแล้วจะทำให้ใช้ตัวดำเนินการ?หรือ early return ไม่ได้[1]ซึ่งทำให้การจัดการข้อผิดพลาดยืดยาวและอ่านยากขึ้น เลยสงสัยว่าในทางปฏิบัติก็เจอประสบการณ์แบบนั้นหรือไม่
[1] https://users.rust-lang.org/t/nll-and-early-return-not-allow...
'aไม่ได้ถูกใช้กับ mutable reference ของvmหรือcall_stackปัญหาไม่ได้อยู่ที่ความเป็น invariant โดยตัวมันเอง แต่อยู่ที่วิธีที่ Rust อนุมาน lifetime ของ mutable reference ซึ่งโค้ดนี้หลบประเด็นนั้นไปได้
ถ้าอย่างนั้นผมก็ไม่แน่ใจว่า
'aที่ติดอยู่กับVMและCallStackมีความหมายอะไรถ้าสามารถสร้างมันด้วย lifetime ใดก็ได้ที่ไม่มีข้อจำกัด แม้แต่
'static[1][2][3] ก็เท่ากับว่าไม่ได้บังคับข้อจำกัดอะไรเลยเลยสงสัยว่าพารามิเตอร์ lifetime นี้ทำหน้าที่อะไรอยู่ตรงนี้ และทำไมถึงไม่เอาออก
[1] https://github.com/andreabergia/rjvm/blob/be9c54066c64a82879...
[2] https://github.com/andreabergia/rjvm/blob/be9c54066c64a82879...
[3] https://github.com/andreabergia/rjvm/blob/be9c54066c64a82879...
เป็นโปรเจกต์สำหรับการเรียนรู้ที่ยอดเยี่ยม และดีใจที่ผู้เขียนกำลังสนุกกับมัน
การทำ VM ตั้งแต่ศูนย์นั้นสนุกมากจริง ๆ และผมก็ได้เรียนรู้อะไรเยอะจากการทำแบบนั้นในอดีต
ถ้าอยากลองใส่ garbage collection เข้าไป อาจลองดู MMTk ได้ (https://www.mmtk.io/)
มันมีอัลกอริทึมตัวเก็บกวาดคุณภาพสูงที่ออกแบบมาให้เสียบใช้กับ VM ได้หลายตัว และเขียนด้วย Rust
ผมเคยจะใช้กับโปรเจกต์เล่น ๆ แต่สุดท้ายต้องยอมแพ้เพราะใช้ Mac
ทำออกมาได้ดีมาก
การสร้าง VM เป็นเรื่องสนุกเสมอ และเมื่อรวมกับระบบชนิดข้อมูลของ Rust ก็น่าจะเป็นประสบการณ์การเรียนรู้ที่น่าสนใจ
ถ้ากำลังหางานอยู่ ลองติดต่อมาทาง Twitter, Mastodon หรืออีเมลบริษัทก็ได้ น่าจะหาได้จาก user ID ตรงนี้
พอเห็นโปรเจ็กต์เจ๋ง ๆ แบบนี้ก็รู้สึกท่วมท้นขึ้นมา
ถ้าผู้เขียนต้นฉบับช่วยอธิบายหน่อยก็คงดีว่าเริ่มต้น Rust อย่างไร และต้องฝึกพื้นฐานถึงระดับไหนถึงจะลองทำอะไรแบบนี้ได้
ไม่อยากออกนอกประเด็นมากนัก แต่ช่วงนี้ส่วนตัวก็กำลังต่อสู้กับความรู้สึกแบบนี้อยู่พอสมควร
ฉันทำงานเป็นนักพัฒนาซอฟต์แวร์มืออาชีพมาเกือบ 10 ปี และเมื่อดูจากตำแหน่งงานปัจจุบันกับความสามารถในการส่งมอบผลิตภัณฑ์จริง ก็รู้ว่าตัวเองมีความสามารถและไม่ใช่นักพัฒนาจอมปลอม
แต่ช่วงนี้พออ่านบล็อกของนักพัฒนา ก็รู้สึกท่วมท้นเหมือนตัวเองยังรู้น้อยเกินไปและไม่ใช่นักพัฒนา “ตัวจริง”
น่าจะเป็นเพราะฉันสร้างภาพนักพัฒนาในอุดมคติไว้ในหัว แล้วเอาตัวเองไปเทียบกับมาตรฐานในจินตนาการนั้น
ฉันชื่นชมคนที่มีความรู้ลึกและถ่ายทอดได้ชัดเจนกระชับ แต่ก็อดคิดไม่ได้ว่าทำไมฉันถึงไม่เป็นแบบนั้น
พอเลิกงานแล้วต้องดูแลครอบครัว ก็แทบไม่เหลือพลังจะทำอะไรต่อ ถึงจะรู้ว่าชีวิตไม่ได้มีแค่การเขียนโปรแกรม แต่ก็ยังอยากเรียนรู้และเติบโตมากขึ้น
รู้ว่ามันไม่ทั้งดีต่อสุขภาพและไม่สมเหตุสมผล แต่ช่วงนี้เป็นความรู้สึกที่สลัดออกได้ยาก
เมื่อก่อนเคยมีประสบการณ์กับ VM อยู่บ้าง และเมื่อหลายปีก่อนก็เคยเขียนบล็อกเป็นซีรีส์สั้น ๆ เกี่ยวกับเรื่องนี้
ที่ทำงานเก่าก็เคยลองไปยุ่งกับ JVM bytecode นิดหน่อยเพื่อแก้ปัญหาเฉพาะทางมาก ๆ ของลูกค้า
อีกทั้งเมื่อหลายปีก่อนก็ได้อ่าน https://craftinginterpreters.com/ ที่ยอดเยี่ยมมาก และได้ไอเดียจากตรงนั้น
แต่โปรเจ็กต์นี้ใหญ่และซับซ้อนมากอย่างไม่ต้องสงสัย
มันใช้เวลานาน และก็ถูกปล่อยทิ้งไว้เป็นช่วง ๆ เหมือน side project หลายตัวของฉัน แต่ก็ดีใจที่ทำจนเสร็จ :-)
ไม่นานมานี้ฉันเพิ่งลงลึกกับ socket ซึ่งเมื่อ 2 สัปดาห์ก่อนยังมีแค่ความเข้าใจระดับสูงที่ได้จากการอ่าน man page, เอกสาร, บล็อกโพสต์ ฯลฯ แบบคร่าว ๆ
ฉันอยากเข้าใจพื้นฐานของระบบเครือข่าย เลยตัดสินใจอ่านให้ได้มากที่สุด และหลังจากนั้นหนึ่งสัปดาห์ก็เรียนรู้จนเขียนโค้ด socket ด้วย Python และ C ได้
ฉันรู้จัก Python ค่อนข้างดีอยู่แล้ว ดังนั้นพอได้ลงลึกแล้วกลับมาดูไลบรารี
socketsก็เข้าใจได้ดีขึ้นถ้าอยากเก่งเทคโนโลยี A ในภาษา X มากขึ้น ฉันแนะนำให้อ่านหรือดูเกี่ยวกับเทคโนโลยี A ให้มากที่สุด แล้วลองสร้างอะไรสักอย่างด้วยภาษา Y
จากนั้นพอกลับมาที่ภาษา X คุณก็จะคุ้นกับแนวคิดรอบ ๆ เทคโนโลยี A ไปมากพอสมควรแล้ว
VM ของภาษาง่าย ๆ ตัวหนึ่งก็น่าจะมีการแทนอ็อบเจ็กต์ในหน่วยความจำ, bytecode interpreter, ตัวเก็บขยะอย่างง่าย, และตัวโหลด
bytecode interpreter มองได้ว่าเป็นสแตก วิธีแทนฟังก์ชันบนสแตกนั้น และลูปที่ตีความแต่ละ bytecode พร้อมขยับ program counter
ถ้าเป็น 0 ชั่วโมง แน่นอนว่าไม่ใช่เรื่องให้ตำหนิ เพราะก็มีเรื่องอื่นให้โฟกัสได้มากมาย แต่ถ้าใครทำ side project เฉลี่ยสัปดาห์ละ 10~20 ชั่วโมงต่อเนื่องเป็นปี ๆ จะมีผลงานน่าประทับใจออกมาก็ไม่ใช่เรื่องน่าแปลกใจ
ขอโปรโมตโปรเจ็กต์คล้าย ๆ กันแบบหน้าด้าน ๆ หน่อย: https://github.com/tenaf0/rust-jvm3
ฉันกำลังทำระบบปฏิบัติการฟรีสำหรับเครื่องโคลน 386(486) AT อยู่ เป็นแค่งานอดิเรก ไม่ได้ใหญ่และเป็นมืออาชีพแบบ gnu :-)
osdev.org, sandpile.org, RBIL, freevga ก็เป็นแหล่งอ้างอิงที่น่าดูเช่นกัน
ปัญหาปวดหัวที่สุดคือการรองรับฮาร์ดแวร์
ยังมีหนังสือกระดาษเก่า ๆ ดี ๆ อีกมากที่รวมสูตรอย่างพวกพอร์ต I/O ที่เชื่อถือได้หรือฮาร์ดแวร์ทริกที่ไม่มีเอกสารกำกับ
Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4
Microsoft MS-DOS Programmer's Reference ซึ่งรวมถึงการเรียก real-mode BIOS ด้วย
PC Interrupts
Undocumented PC
PC Intern
Programmer's Guide To The EGA, VGA, And Super VGA Cards
Graphics Programming Black Book Special Edition
และยังคุ้มค่าที่จะลองสำรวจความก้าวหน้าในการพัฒนาระบบปฏิบัติการหลังยุค single kernel, microkernel, และ hybrid ด้วย
สถาปัตยกรรมแบบ capability-based อย่าง seL4 มีข้อดีโดยเนื้อแท้หลายอย่างทั้งด้านประสิทธิภาพและความปลอดภัย รวมถึง capability และ IPC ที่ยอดเยี่ยม
ชั้นความเข้ากันได้กับ POSIX ก็สำคัญเช่นกัน ระบบปฏิบัติการแบบ embedded ที่ไม่มีแนวคิดเรื่อง thread หรือ process ก็ยังสามารถทำ POSIX ได้
hypervisor สามารถเพิ่มได้ง่ายกว่ามากถ้ามี Intel VT-[xd] และถ้าไม่มีก็ถอยไปใช้การจำลองแทนได้ การจำลองแบบแปลงคำสั่งให้ประสิทธิภาพดีมาก
คุณต้องชำนาญในการทำ interrupt handler ให้เป็นแบบทั่วไปและรวดเร็ว หลีกเลี่ยง race condition และใช้แพตเทิร์นแบบ lock-free
รวมถึงต้องเขียนใหม่หรือดัก trap สำหรับคำสั่งที่ยังไม่รองรับ เช่น x87 และ MMX
เหตุผลที่ microkernel แบบบริสุทธิ์ล้มเหลวก็เพราะความซับซ้อนในการจัดลำดับและจัดการทรัพยากรหลายชนิดแบบ transactional เพิ่มสูงขึ้น
สถาปัตยกรรม microkernel มีข้อดีด้านความปลอดภัยและการปฏิบัติการอยู่มากในเชิงทฤษฎี แต่ในรูปแบบที่บริสุทธิ์ก็ไม่ได้แพร่หลายอย่างกว้างขวาง