حان الوقت لكتابة أول برنامج منفصل لنواة لدينا - قذيفة. سيتم تخزينه في ملف .elf منفصل وسيتم تشغيله بواسطة عملية init عند بدء تشغيل kernel.
هذا هو المقال الأخير في دورة تطوير نظام التشغيل لدينا.
جدول المحتويات
بناء نظام (جعل ، مجلس التعاون الخليجي ، الغاز). التمهيد الأولي (متعدد التمهيد). إطلاق (qemu). مكتبة C (strcpy ، memcpy ، strext).
مكتبة C (sprintf ، strcpy ، strcmp ، strtok ، va_list ...). بناء المكتبة في وضع kernel ووضع تطبيق المستخدم.
سجل نظام النواة. ذاكرة الفيديو الإخراج إلى المحطة (kprintf ، kpanic ، kassert).
الذاكرة الديناميكية ، الكومة (kmalloc ، kfree).
تنظيم الذاكرة والتعامل مع المقاطعة (GDT ، IDT ، PIC ، syscall). الاستثناءات.
الذاكرة الظاهرية (دليل الصفحة وجدول الصفحة).
العملية. المجدول. تعدد المهام. مكالمات النظام (القتل ، الخروج ، ملاحظة).
برامج تشغيل الأجهزة الشخصية. مكالمات النظام (ioctl ، fopen ، fread ، fwrite). مكتبة C (fopen ، fclose ، fprintf ، fscanf).
نظام ملفات kernel (initrd) ، قزم ، وداخله. مكالمات النظام (exec). شل كبرنامج كامل للنواة.وضع حماية المستخدم (ring3). قسم حالة المهمة (tss).
شل كبرنامج نواة كاملة
في مقال سابق ، نظرنا في برامج تشغيل الأجهزة الشخصية وكتبنا برنامج تشغيل طرفي.
الآن لدينا كل ما نحتاج إليه لإنشاء تطبيق وحدة التحكم الأول.
سنكتب تطبيق وحدة التحكم نفسها ، والتي سنقوم بتجميعها في قزم منفصل.
void start() { u_int errno; stdio_init(); errno = main(); stdio_deinit(); exit(errno); }
سنحتاج إلى تهيئة المكتبة القياسية ونقل التحكم إلى الوظيفة الرئيسية المألوفة.
int main() { char cmd[255]; while (1) { printf(prompt); flush(); scanf(cmd); if (!execute_command(cmd)) { break; } } return 0; }
علاوة على ذلك في الحلقة ، نقرأ ببساطة السطر وننفذ الأمر.
أوامر Parsim خلال strtok_r إذا كان هناك وسيطات.
static bool execute_command(char* cmd) { if (!strcmp(cmd, cmd_ps)) { struct clist_definition_t *task_list; task_list = ps(); printf(" -- process list\n"); clist_for_each(task_list, print_task_info); } else if (!strcmp(cmd, cmd_clear)) { clear(); flush(); } else if (!strncmp(cmd, cmd_kill, strlen(cmd_kill))) { char* save_ptr = null; strtok_r(cmd, " ", &save_ptr); char* str_tid = strtok_r(null, " ", &save_ptr); u_short tid = atou(str_tid); if (!kill(tid)) { printf(" There is no process with pid %u\n", tid); }; } else if (!strncmp(cmd, cmd_exit, strlen(cmd_exit))) { clear(); printf(prompt); flush(); return false; } else if (!strncmp(cmd, cmd_exec, strlen(cmd_exec))) { char* save_ptr = null; strtok_r(cmd, " ", &save_ptr); char* str_file = strtok_r(null, " ", &save_ptr); exec(str_file); } else if (!strncmp(cmd, cmd_dev, strlen(cmd_dev))) { struct clist_definition_t *dev_list; dev_list = devs(); printf(" -- device list\n"); clist_for_each(dev_list, print_dev_info); } else { printf(" There is no such command.\n Available command list:\n"); printf(" %s %s %s <pid> %s <file.elf> %s %s\n", cmd_ps, cmd_exit, cmd_kill, cmd_exec, cmd_clear, cmd_dev); } return true; }
في الواقع ، نحن فقط سحب المكالمات النظام.
اسمحوا لي أن أذكركم بتهيئة المكتبة القياسية.
في الدرس الأخير ، كتبنا الوظيفة التالية في المكتبة:
extern void stdio_init() { stdin = fopen(tty_dev_name, MOD_R); stdout = fopen(tty_dev_name, MOD_W); asm_syscall(SYSCALL_IOCTL, stdout, IOCTL_INIT); asm_syscall(SYSCALL_IOCTL, stdin, IOCTL_READ_MODE_LINE); asm_syscall(SYSCALL_IOCTL, stdin, IOCTL_READ_MODE_ECHO); }
إنه ببساطة يفتح ملفات برنامج التشغيل الطرفي الخاص للقراءة والكتابة ، والتي تتوافق مع إدخال لوحة المفاتيح وإخراجها إلى الشاشة.
بعد قيامنا بتجميع قزمنا بقذيفة ، يجب وضعه على نظام ملفات kernel الأصلي (initrd).
يتم تحميل قرص ذاكرة الوصول العشوائي الأولي من قِبل لوادر kernel كوحدة متعددة التمهيد ، لذلك نحن نعرف العنوان في ذكرى initrd لدينا.
يبقى تنظيم نظام الملفات لـ initrd ، والذي من السهل القيام به وفقًا لمقالة كتبها جيمس مولوي.
لذلك ، سيكون التنسيق كما يلي:
extern struct initrd_node_t { unsigned char magic; char name[8]; unsigned int offset; unsigned int length; }; extern struct initrd_fs_t { int count; struct initrd_node_t node[INITRD_MAX_FILES]; };
بعد ذلك ، تذكر تنسيق قزم 32 بت.
struct elf_header_t { struct elf_header_ident_t e_ident; u16 e_type; u16 e_machine; u32 e_version; u32 e_entry; u32 e_phoff; u32 e_shoff; u32 e_flags; u16 e_ehsize; u16 e_phentsize; u16 e_phnum; u16 e_shentsize; u16 e_shnum; u16 e_shstrndx; };
نحن هنا مهتمون بنقطة الدخول وعنوان جدول رؤوس البرامج.
سيكون قسم الكود والبيانات هو العنوان الأول ، وسيكون قسم المكدس هو الثاني (وفقًا لنتائج دراسة قزم من خلال objdump).
struct elf_program_header_t { u32 p_type; u32 p_offset; u32 p_vaddr; u32 p_paddr; u32 p_filesz; u32 p_memsz; u32 p_flags; u32 p_align; } attribute(packed);
هذه المعلومات كافية لكتابة محمل ملف قزم.
نحن نعرف بالفعل كيفية اختيار صفحات للعمليات المخصصة.
لذلك ، نحتاج فقط إلى تخصيص عدد كاف من الصفحات للعناوين ونسخ المحتويات فيها.
سنكتب وظيفة ستنشئ عملية بناءً على ملف العفريت المحلل.
تعرف على كيفية تحليل elfik في الفيديو التعليمي.
نحتاج إلى تنزيل رأس برنامج واحد فقط مع رمز وبيانات ، لذلك لن نعمم وسنركز على هذه الحالة.
extern void elf_exec(struct elf_header_t* header) { assert(header->e_ident.ei_magic == EI_MAGIC); printf(MSG_KERNEL_ELF_LOADING, header->e_phnum);
الشيء الأكثر إثارة للاهتمام هنا هو إنشاء دليل الصفحة وجدول الصفحة.
انتبه ، نختار أولاً الصفحات الفعلية (mm_phys_alloc_pages) ، ثم نعيّنها في الصفحات المنطقية (mmu_occupy_user_page).
هنا من المفترض أن يتم تخصيص الصفحات في الذاكرة الفعلية بشكل مستمر.
هذا كل شيء. الآن يمكنك تنفيذ shell الخاص بك للنواة الخاصة بك! شاهد فيديو تعليمي وافحص التفاصيل.
استنتاج
آمل أن تكونوا قد وجدت هذه السلسلة من المقالات مفيدة.
لم نفكر بعد في حلقات الحماية معك ، ولكن نظرًا لانخفاض درجة ارتباط الموضوع والمراجعات المختلطة ، سنستمر في كسرها.
أعتقد أنك نفسك جاهز الآن لإجراء مزيد من البحث ، ولقد درست كل الأشياء المهمة لك.
لذلك ، تشديد الحزام ، وإلى المعركة! عن طريق كتابة نظام التشغيل الخاص بك!
استغرق الأمر مني شهرًا تقريبًا (إذا أخذنا في الاعتبار الوقت الكامل لمدة 6-8 ساعات يوميًا) لتنفيذ كل ما تعلمته أنت وأنا من الصفر.
لذلك ، في غضون 2-3 أشهر ، ستكون قادرًا على كتابة نظام تشغيل كامل مع نظام ملفات حقيقي ، لم تنفذه أنت وأنا.
فقط اعلم أن qemu لا يعرف كيفية التعامل مع initrd من التنسيق التعسفي ، ويقتصر على 4 كيلو بايت ، لذلك ستحتاج إلى جعله مثل نظام Linux ، أو استخدم borsch بدلاً من qemu.
إذا كنت تعرف كيفية التغلب على هذه المشكلة ، فاكتب في خطاب شخصي ، سأكون ممتنًا جدًا لك.
هذا كل شئ! حتى الجديد لم يعد يجتمع!
مراجع
شاهد
الفيديو التعليمي لمزيد من المعلومات.
شفرة المصدر
في مستودع بوابة (تحتاج إلى فرع الدرس 9).
مراجع
1. جيمس مولوي. لفة نظام التشغيل الخاص بك UNIX استنساخ.
2. الأسنان. مجمع ل DOS ، ويندوز ، يونيكس
3. كلاشينكوف. المجمع سهل!
4. Tanenbaum. أنظمة التشغيل. التنفيذ والتطوير.
5. روبرت لوف. نواة لينكس وصف عملية التطوير.