🔗 تشريح العقد المقدس: qos_api/src/lib.rs
أهلاً بك في المنطقة المحرمة! هذه هي نقطة التماس الوحيدة بين برامج المستخدم العادية (التي تعيش في فضاء المستخدم) وبين النواة الجبارة (التي تسكن في Ring 0).
هنا لا توجد مكتبات libc ضخمة ولا استدعاءات معقدة. هنا العقد المقدس الذي يتفق عليه الجميع.
1. أرقام النداءات الثابتة (Syscall Numbers)
pub const SYS_EXIT: u64 = 0;
pub const SYS_WRITE: u64 = 1;
pub const SYS_SEND: u64 = 2;
pub const SYS_RECV: u64 = 3;
pub const SYS_SPAWN: u64 = 5;
pub const SYS_GET_SENDER: u64 = 6;
pub const SYS_DRAW: u64 = 7;
pub const SYS_READ_KEY: u64 = 8;هذه الأرقام هي شفرة التخاطب بين البرامج والنواة. لا يمكن تغيير هذه الأرقام أبداً لأنها محفورة في ذاكرة كل برنامج تنفيذي يتم بناؤه لهذا النظام!
2. لغة التخاطب الصامتة (اتفاقية الاستدعاء ABI)
النواة والبرامج لا يتحدثون عبر متغيرات عادية، بل عبر "مسجلات" المعالج (Registers) مباشرة:
#[inline(always)]
pub fn syscall3(num: u64, arg1: u64, arg2: u64, arg3: u64) -> u64 {
let ret: u64;
unsafe {
core::arch::asm!(
"syscall",
in("rax") num,
in("rdi") arg1,
in("rsi") arg2,
in("rdx") arg3,
lateout("rax") ret,
...نأخذ المعاملات من المستخدم ونحشرها في rdi و rsi و rdx، بينما نضع رقم النداء (مثلاً 1 للطباعة) في المسجل الملكي rax، ثم نُطلق صرخة syscall! المعالج هنا سينتزع السيطرة من البرنامج فوراً ويسلمها للنواة.
3. الدوال عالية المستوى (السحر اللطيف)
لأن المبرمج العادي لا يحب التعامل مع الـ Assembly، قمنا بتغليف هذه الاستدعاءات القاسية بدوال ناعمة جداً:
pub fn print(s: &str) -> u64 {
write(s.as_bytes()) // يحول السلسلة النصية إلى بايتات ويستدعي النواة
}
pub fn spawn(elf_data: &[u8]) -> Result<u64, u64> {
let ret = syscall2(SYS_SPAWN, elf_data.as_ptr() as u64, elf_data.len() as u64);
if ret >= u64::MAX - 10 { Err(ret) } else { Ok(ret) }
}عندما يكتب المبرمج qos_api::print("Hello");، فإنه فعلياً يعبر من هذا الجسر الآمن مباشرة إلى قلب النظام!