Skip to content

🧠 العقل المدبر: تشريح ملف core/lib.rs قطعه بقطعه ​

أهلاً بك في غرفة العمليات الرئيسية لنظام QOS! هذا الملف ليس مجرد ملف برمجي عادي، بل هو النبضة الأولى التي تحول مجموعة من الدوائر الإلكترونية الميتة إلى نظام تشغيل ينبض بالحياة.

لقد طلبت "تفصيخ" الكود، وهذا بالضبط ما سنفعله. سنأخذ الكود كتلة بكتلة ونحلل كل سطر فيه، ولماذا كتبناه بهذا الشكل. استعد لرحلة هندسية عميقة!


1. التمرد على النظام (التجريد الكامل) ​

في بداية الملف، ستلاحظ هذه الأوامر الغريبة:

rust
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]

ماذا تعني هذه الأسطر؟ ​

  • #![no_std]: هذا السطر هو إعلان استقلال! في البرمجة العادية، لغة Rust تعتمد على نظام التشغيل ليوفر لها ميزات مثل (الملفات، الشبكات، الشاشة). ولكن هنا... نحن هم نظام التشغيل! لذلك نخبر المترجم: "لا تحضر مكتباتك القياسية، ليس لدينا نظام تشغيل نستند إليه، نحن سنبني كل شيء من الصفر".
  • #![no_main]: في البرامج العادية، نقطة البداية هي دالة main. هنا، نحن نخبر المترجم أن نقطة البداية سيحددها لنا "محمل الإقلاع" (Bootloader) عبر دالة خاصة تسمى kernel_main.

2. الدالة الأعظم: kernel_main ​

بمجرد أن ينتهي محمل الإقلاع (مثل GRUB) من تحميل النظام في الـ RAM، يقوم بالقفز مباشرة إلى هذا السطر:

rust
#[no_mangle]
pub extern "C" fn kernel_main(mb2_ptr: u64) -> ! {
  • #[no_mangle]: تمنع مترجم Rust من تغيير اسم الدالة، لكي يجدها GRUB بسهولة.
  • extern "C": نستخدم طريقة لغة C في استدعاء الدوال لأنها المعيار العالمي للـ Bootloaders.
  • mb2_ptr: u64: هدية من GRUB! هذا المؤشر يحمل خريطة كنز (Memory Map) تخبرنا أين الذاكرة المتاحة للعمل وأين الذاكرة المحجوزة.
  • -> !: تعني أن هذه الدالة يستحيل أن تنتهي! نظام التشغيل لا يتوقف أبداً إلا إذا انقطعت الكهرباء أو انهار النظام (Panic).

3. المرحلة الأولى: إيقاظ العتاد والذاكرة ​

rust
    // 1. تهيئة الشاشة المبدئية
    let mut out = arch::x86_64::VgaTextOutput;
    out.clear();
    out.write_colored(">>> Booting QOS Kernel Microkernel... <<<
", Color::LightGreen, Color::Black);

    // 2. تفعيل المعمارية (GDT, IDT, PIC, PIT, Syscall)
    unsafe { arch::init(); }

    // 3. قراءة خريطة الذاكرة وتشغيل الـ PMM والـ VMM
    unsafe { mm::init(mb2_ptr); }

    // 4. تفعيل المقاطعات (السماح للوحة المفاتيح والمؤقت بالعمل)
    unsafe { core::arch::asm!("sti"); }

لماذا هذا الترتيب تحديداً؟ الهندسة الصارمة: ​

  1. لا يمكننا طباعة أي شيء قبل تنظيف شاشة VGA عبر VgaTextOutput.
  2. لا يمكننا التعامل مع الذاكرة قبل استدعاء arch::init لأنه يجهز الجداول الآمنة GDT.
  3. لا يمكننا تفعيل المقاطعات sti قبل تشغيل مدير الذاكرة mm::init، لأن المجدول Scheduler يحتاج الذاكرة لكي يبدأ بتبديل المهام! أي خطأ في هذا الترتيب يعني Restart فوري للجهاز.

4. المرحلة الثانية: خلق الإنسان الأول (Process PID 1) ​

في أنظمة الـ Microkernel، النواة لا تدير الملفات ولا الشاشة. يجب أن نطلق "برنامج المستخدم" الأول ليقوم بهذه المهام.

rust
    // Phase 2: إطلاق عملية init (PID 1)
    let recv_prog = build_recv_program(0x8000_0000); // توليد أسمبلي يدوي
    let init_pid = unsafe { sched::spawn_from_elf(&recv_prog).unwrap_or(1) };
  • نستخدم دالة تاريخية build_recv_program لتوليد كود برمجي بسيط (ينتظر وصول رسالة ثم يطبعها).
  • نعطي هذا الكود لدالة spawn_from_elf التي ستبني له فضاء ذاكرة معزول (Ring 3)، وتضعه في قائمة المهام تحت اسم PID 1.

5. المرحلة الثالثة والرابعة: إطلاق خوادم النظام (Services) ​

الآن، النواة تستدعي أهم خادمين في النظام: vfs_server (مدير الملفات) و display_server (مدير الشاشة).

rust
    // ⚠️ يجب بناء services/vfs_server --release أولاً
    static VFS_ELF: &[u8] = include_bytes!(
        "../../../services/vfs_server/target/x86_64-qos-kernel-user/release/vfs_server"
    );

    match unsafe { sched::spawn_from_elf(VFS_ELF) } {
        Ok(vfs_pid) => {
            // ... نجاح التشغيل
            unsafe { sched::kernel_grant_port_pair(init_slot, vfs_slot); }
        }
        Err(reason) => { ... }
    }

تحليل العبقرية الأمنية هنا: ​

  • include_bytes!: بما أن النواة لا تملك "نظام ملفات" بعد، كيف ستقرأ ملف الـ vfs_server من القرص الصلب؟ لا تستطيع! لذا نقوم بـ "حشر" ملف الـ ELF الخاص بالخادم داخل النواة نفسها أثناء التجميع (Compile time)!
  • kernel_grant_port_pair (السر الأمني CVE-N5): بعد أن أطلقنا مدير الملفات، كيف سيتحدث معه برنامج init؟ في نظام QOS لا نسمح بالتحدث العشوائي. النواة تقوم شخصياً بمد "سلك هاتف مباشر" (IPC Port) بين init و vfs_server. بدون هذا السطر، الخادمان لا يستطيعان رؤية أو سماع بعضهما أبداً!

(تتكرر نفس هذه العملية تماماً لإطلاق خادم الشاشة display_server)


6. السكون المطلق (The HLT Loop) ​

بعد أن رتبت النواة كل شيء، وأطلقت البرامج في وضع المستخدم... ماذا تفعل النواة؟ تنام!

rust
    out.write_colored("
>>> QOS Kernel Microkernel — Active! <<<

", Color::LightCyan, Color::Black);

    loop { unsafe { core::arch::asm!("hlt"); } }
}
  • تعليمة hlt (Halt): هذه التعليمة السحرية تخبر المعالج: "توقف عن استهلاك الطاقة، نم ولا تستيقظ إلا إذا حدثت مقاطعة (Interrupt)".
  • عندما ينبض المؤقت الزمني كل 1 ميلي ثانية، يستيقظ المعالج، ينفذ كود المجدول sched::tick للتبديل بين البرامج، ثم يعود لتعليمة hlt لينام مجدداً! هذا يجعل نواة QOS سريعة جداً وموفرة للطاقة.

7. دوال الطوارئ (Panic & Alloc Error) ​

ماذا لو حدث شيء كارثي ولم تستطع النواة التعامل معه؟

rust
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    let mut out = VgaTextOutput;
    out.write_colored("
[KERNEL PANIC] ", Color::White, Color::Red);
    // ... يطبع السطر الذي حدث فيه الانهيار
    loop { unsafe { core::arch::asm!("hlt"); } }
}
  • دالة panic_handler إجبارية في بيئة no_std. إذا كتبت في النواة unwrap() وانفجرت، سينتقل الكود فوراً إلى هنا.
  • الشاشة ستتوقف، ويُطبع النص باللون الأحمر، ويدخل المعالج في غيبوبة أبدية hlt لحماية القرص الصلب والذاكرة من أي بيانات تالفة.

🎯 الخلاصة ​

ملف core/lib.rs يجسد فلسفة الـ Microkernel بأبهى صورها: الكثير من التنظيم، القليل جداً من التنفيذ الفعلي. النواة هنا مجرد حكم ساحة، تجهز الملعب (الذاكرة)، تسمي اللاعبين (الخوادم)، تضع قواعد اللعب (IPC Ports)، ثم تقف لتتفرج بسلام!

تم تطويره بحب بواسطة مجتمع Qtoom.