Entwicklung einer monolithischen Unix-ähnlichen OS-C-Bibliothek (2)

In einem früheren Artikel haben wir gelernt, wie der Hello World-Kernel ausgeführt wird, und einige Funktionen für die Arbeit mit Zeichenfolgen geschrieben. Jetzt ist es Zeit, die C-Bibliothek zu erweitern, damit Sie kprintf und andere notwendige Funktionen implementieren können. Lass uns gehen!

Inhaltsverzeichnis


  1. Build-System (make, gcc, gas). Erster Start (Multiboot). Starten Sie (qemu). C-Bibliothek (strcpy, memcpy, strext).
  2. C-Bibliothek (sprintf, strcpy, strcmp, strtok, va_list ...). Erstellen der Bibliothek im Kernelmodus und im Benutzeranwendungsmodus.
  3. Das Kernel-Systemprotokoll. Videospeicher Ausgabe an das Terminal (kprintf, kpanic, kassert).
  4. Dynamischer Speicher, Heap (kmalloc, kfree).
  5. Organisation der Speicher- und Interrupt-Behandlung (GDT, IDT, PIC, Syscall). Ausnahmen
  6. Virtueller Speicher (Seitenverzeichnis und Seitentabelle).
  7. Prozess. Planer Multitasking. Systemaufrufe (kill, exit, ps).
  8. Das Dateisystem des Kernels (initrd), elf und seiner Interna. Systemaufrufe (exec).
  9. Zeichengerätetreiber. Systemaufrufe (ioctl, fopen, fread, fwrite). C-Bibliothek (fopen, fclose, fprintf, fscanf).
  10. Shell als komplettes Programm für den Kernel.
  11. Benutzerschutzmodus (Ring3). Aufgabenstatussegment (tss).

Bibliothek C.


Zuerst müssen Sie Typen mit expliziten Dimensionen implementieren.

Da werden wir unter einer Plattform sammeln, bis unsere Definitionen und Implementierungen korrekt sind. Es ist nicht universell, aber deshalb ist es lesbar und einfach. Ich gehe davon aus, dass es manchmal besser ist, etwas zu tun, das nicht universell ist, aber den aktuellen Anforderungen entspricht, da die Unterstützung universeller Lösungen äußerst schwierig ist.

typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned int u_long; 

Es tut nicht weh, den Booleschen Typ einzuführen.

 #pragma once /* types */ typedef int bool; #define true 1 #define false 0 #define null 0 

Auch ein paar Makros für die Arbeit mit Bytes werden uns nicht schaden.

 typedef unsigned long size_t; #define HIGH_WORD(addr) ((addr & 0xffff0000) >> 16) #define LOW_WORD(addr) ((addr & 0xffff)) #define LOW_BYTE(addr) ((addr & 0x00ff)) #define HIGH_BYTE(addr) ((addr & 0xff00) >> 8) 

Die folgenden Makros werden benötigt, um mit einer variablen Anzahl von Argumenten zu arbeiten. Dieser Ansatz funktioniert nur, wenn die Funktion der C-Sprachaufrufkonvention folgt, bei der die Funktionsargumente ab dem letzten durch den Stapel geleitet werden.

 typedef size_t* va_list; #define va_start(l, a) (l = (void*)((size_t)&a) + sizeof(a)) #define va_end(l) (l = (void*)0) #define va_arg(l, s) (*(s*)(l++)) 

Natürlich könnte man den anderen Weg gehen. Versuchen Sie, anstatt Ihre eigenen Funktionen zu definieren, die integrierte Bibliothek zu verwenden und Funktionen zu ersetzen, die über LD_PRELOAD auf den Kernel zugreifen. Aber ich mag es, den Prozess vollständig zu steuern, also lassen wir diese Option als Idee für diejenigen, die anfangen, ihr Betriebssystem in diesem Tutorial zu schreiben.

Im Video-Tutorial werden wir außerdem die Implementierung der folgenden Bibliotheksfunktionen betrachten. Die Implementierung erhebt keinen Anspruch auf Optimalität und Vollständigkeit, aber ich denke, sie erhebt Anspruch auf Einfachheit und Lesbarkeit. Ich stelle nur fest, dass wir eine thread-sichere Implementierung der strtok-Funktion verwenden, die strtok_r heißt. Und wir haben uns in der letzten Lektion die Strinv- und Strext-Funktionen selbst ausgedacht. Wenn Sie mit der C-Sprache vertraut sind, werden Sie wahrscheinlich mit fast allen unten aufgeführten Funktionen vertraut sein.

 extern int strlen(const char* s); extern char* strcpy(char* s1, const char* s2); extern char* strncpy(char* s1, const char* s2, u_int n); extern void* memcpy(void* buf1, const void* buf2, u_int bytes); extern void* memset(void* buf1, u8 value, u_int bytes); extern int strcmp(const char* s1, const char* s2); extern int strncmp(const char* s1, const char* s2, u_int n); extern char* strcat(char* s1, const char* s2); extern char* strext(char* buf, const char* str, char sym); extern int strspn(char* str, const char* accept); extern int strcspn(char* str, const char* rejected); char* strchr(const char* str, char ch); extern char* strtok_r(char* str, const char* delims, char** save_ptr); extern char* memext(void* buff_dst, u_int n, const void* buff_src, char sym); extern char* itoa(unsigned int value, char* str, unsigned int base); extern unsigned int atou(char* str); extern char* strinv(char* str); extern unsigned int sprintf(char* s1, const char* s2, ...); extern unsigned int snprintf(char* s1, u_int n, const char* s2, ...); extern unsigned int vsprintf(char* s1, const char* s2, va_list list); extern unsigned int vsnprintf(char* s1, unsigned int n, const char* s2, va_list list); 

Die Routine entfällt. Das Ende des Boilerplate-Codes. Die nächste Lektion wird viel komplizierter und interessanter. Wenn Sie ein Projekt sponsern möchten, können Sie die optimale Implementierung von Bibliotheksfunktionen anbieten.

Referenzen


Entwicklung eines monolithischen Unix-ähnlichen Betriebssystems - Erste Schritte
Video-Tutorial zu diesem Artikel
Quellcode (Sie benötigen einen Lektion2-Zweig)

Referenzliste


  1. James Molloy. Rollen Sie Ihr eigenes UNIX-Klon-Betriebssystem.
  2. Zubkov. Assembler für DOS, Windows, Unix
  3. Kalaschnikow. Assembler ist einfach!
  4. Tanenbaum. Betriebssysteme. Implementierung und Entwicklung.
  5. Robert Love. Linux-Kernel Beschreibung des Entwicklungsprozesses.

Source: https://habr.com/ru/post/de466709/


All Articles