Linux-0.11
Theorie Kurs: https://www.bilibili.com/video/bv1d4411v7u7
Nützliche Referenzen ( Linux-0.11 kommentierte Version): https://github.com/beride/linux0.11-1
Hauptzweig ist Linux-0.11 Quellcode
Zeigen Sie die Codendifferenz an: Pull Anfrage Wählen Sie den entsprechenden Zweig aus und vergleichen Sie ihn mit dem Main
✔️ LAB0 -Betriebssystem Boot (Branch Lab0)
Rewriting Bootsect.S vervollständigt hauptsächlich die folgenden Funktionen:
- Bootsect.s kann eine Meldung "xxx booten ..." auf dem Bildschirm drucken.
Umschreiben setup.s vervollständigt hauptsächlich die folgenden Funktionen:
- Bootsect.s kann das Laden von Setup.s vervollständigen und zum Setup springen. Starten Sie die Ausführungsadresse. und setup.s gibt eine Zeile "Jetzt sind wir in Setup" auf den Bildschirm aus. Setup.s können mindestens einen grundlegenden Hardwareparameter (wie Speicherparameter, Grafikkartenparameter, Festplattenparameter usw.) erhalten, in einer bestimmten Adresse im Speicher speichern und auf den Bildschirm ausgeben. Setup.s lädt den Linux -Kernel nicht mehr. Halten Sie einfach die oben genannten Informationen auf dem Bildschirm.
Experimentelles Anleitung Buch: https://www.lanqiao.cn/courses/115/labs/568/document/
✔️ Lab1 implementiert Systemaufrufe (Branch Lab1)
- Die Gesamtzahl der in Kernel/System_Call.s geänderten Systemaufrufe beträgt 74.
- Fügen Sie ein Makro in include/unistd.h hinzu, um die Positionierung der Anruffunktionstabelle anzugeben.
- Schreiben Sie zwei Funktionen (sys_iam, sys_hoami) Funktionsdefinitionen inclyde/linux/sys.h. Und fügen Sie der Funktionstabelle danach zwei neue Funktionen hinzu.
- Kernel/who.c hinzugefügt, um zwei Systemanrufe zu implementieren.
- Makefile ändern
- OBJs erhöht die WHO -Abhängigkeit (WHO.O)
- Fügen Sie die Bedingungen für die Erzeugung von Abhängigkeiten für WHO.S und WHO.O in Abhängigkeiten hinzu
- Machen Sie Execute ./Run, um das Linux -Subsystem einzugeben, und fügen Sie die Makrodefinitionen von IAM und Whoami in /usr/include/unistd.h hinzu (wie Punkt 2).
- Schreiben eines Testprogramms in den Benutzerstatusstests, ob es erfolgreich ist.
Experimentelles Anleitung Buch: https://www.lanqiao.cn/courses/115/labs/569/document/
✔️ LAB2 implementiert die Verfolgung der Trajektorie des Prozessbetriebs (Branch Lab2)
- process.c implementiert ein Szenario, das ein Hybrid-CPU-Computing und ein IO-Computer simuliert und es auf mehreren Prozessstoffe ausführt.
- Drucken Sie mehrere Zustände des Kernel -Prozessumschusses (PID -Statuszeit) auf /var/process.log aus.
- Nach der Funktion "Init () in Init/Main tritt in die Anweisung" Benutzerstatus "ein, assoziieren Sie den Dateideskriptor 3 mit /var/process.log.
- Implementieren Sie die Funktion fprintk () in Kernel/Printk, um die Ausgabe in den Prozess zu drucken.
- Suchen Sie den Status -Switching -Punkt und fügen Sie Fprintk () hinzu, um Protokolle zu schreiben.
- Prozess starten kernel/system_call.s -> sys_fork (Call Copy_process)
- Ausführen, Blockierungsstatusschalter Kernel/Sched.c -> Zeitplan sys_pause sleep_on interruptible_sleep_on wake_up
- Kernel/exit.c -> do_exit sys_waitpid beenden
- Hinweis: GCC in Linux-0.11 kann nicht kompiliert werden // kommentiert.
Experimentelles Anleitungsbuch: https://www.lanqiao.cn/courses/115/labs/570/document/
✔️ LAB3 ersetzt den ursprünglichen TSS -Vorgang in Linux0.11, um auf Stapelschaltung (Branch Lab3) zu wechseln, um zu wechseln.
Die ursprüngliche Schaltmethode erfolgt über TSS, was einem Schnappschuss des Registers entspricht. Es wird direkt vor Ort durch die von Intel bereitgestellten Anweisungen ersetzt, was langsamer ist. Stapelwechsel ist effizienter.
Kernel/Sched.C.
- Die ursprüngliche Switch_to -Methode basiert auf dem TSS -Switch und wir möchten sie in der Header -Datei kommentieren.
- New Switch_to benötigt zwei Parameter (1: Zeiger auf die nächste PCB 2: Die Position der nächsten Aufgabe im Array wird zum Schalten von LDT verwendet).
kernel/system_call.s
- Write Switch_to Assembly Implementierung
- Schalten von PCB
- Schreiben Sie die TSS -Kernel -Stapelposition neu (TSS wird zu diesem Zeitpunkt beibehalten, aber es gibt nur einen TSS auf der Welt, und es wird nicht zum Verfahrensumschalten verwendet.)
- Wechseln Sie den Kernelstapel
- LDT -Schalter
Kernel/Fork.c
- Melden Sie sich in den TSS -Einstellungen in PCB aus.
- Fügen Sie eine neue Mitgliedsvariable hinzu - Kernel -Stack zu PCB, mit der Kernel -Stack -Informationen gespeichert werden.
- Schreiben Sie hier Stapelinformationen und lassen Sie die PCB die Mitgliedsvariablen auf den Zeiger hinweisen.
Experimentelles Anleitungsbuch: https://www.lanqiao.cn/courses/115/labs/571/document/
Referenz: https://blog.csdn.net/qq_42518941/article/details/119182097
✔️ Lab4 implementiert Semaphore -Systemaufrufe in Linux0.11 (Branch Lab4)
Vor-Key-Wissenspunkte
- Kernel/Sched.c Sleep_on passieren den eingehenden Warteschlangenheader, setzen Sie den Strom auf den Blockieren und führen Sie den Zeitplan () aktiv aus. TMP speichert die ursprüngliche Blockierungswarteschlange. Beim Erwachen sind die blockierenden Warteschlangen auf Runnable eingestellt.
- Kernel/Sched.c Wake_Up weckt alle blockierten PCBs. Nur der Weckteamführer ist im Programm zu sehen, muss aber in Kombination mit Sleep_on gelesen werden. Nach der Kombination werden Sie feststellen, dass der Weckkopf anschließend alle aufwachen wird.
Schreiben Sie die Anwendung "PC.C", um das Problem des klassischen Produzentenverbrauchers zu lösen und die folgenden Funktionen auszufüllen:
- Erstellen Sie einen Erzeugerprozess, n Verbraucherprozesse (n> 1)
- Erstellen Sie einen gemeinsam genutzten Puffer mit Dateien
- Der Produzentenprozess schreibt Ganzzahlen 0, 1, 2,…, M, M> = 500 in den Puffer nacheinander
- Der Verbraucherprozess liest aus dem Puffer, liest nacheinander und löscht die Lesezahlen aus dem Puffer und gibt dann die Prozess -ID und + Nummern in die Standardausgabe aus
- Der Puffer kann nur bis zu 10 Zahlen gleichzeitig sparen
Semaphor implementieren
sem_t * sem_open ( const char * name , unsigned int value );
int sem_wait ( sem_t * sem );
int sem_post ( sem_t * sem );
int sem_unlink ( const char * name );
- Sem_open eröffnet ein Semaphor
- Wenn das Semaphor derzeit gleich Null ist und den Prozess blockiert
- Fügen Sie ein Semaphor hinzu
- Ein Semaphor ausschalten
Der Fokus von Warte und Post
- Warten
- Rufen Sie Kernel/Sched.c Sleep_on an, um auf die Blockierung zu warten
- Verwenden Sie den Linux Interrupt (Linux0.11 Single Core), um die kritische Zone zu schützen
- abzüglich eines Wertes von SEM
- Post
- Fügen Sie einen zum Wert von SEM hinzu. Wenn der Wert größer als 0 ist, rufen Sie Kernel/Sched.c Wake_UP auf, um den von diesem Semaphor blockierten Prozess aufzuwecken.
- Verwenden Sie den Linux Interrupt (Linux0.11 Single Core), um die kritische Zone zu schützen
Experimentelles Anleitungsbuch: https://www.lanqiao.cn/courses/115/labs/572/document/
✔️ Zuordnung und Freigabe von Lab5 -Adressen (Branch Lab5)
Logische Adresse -> GDT -> LDT -> Seitentabelle -> Physische Adresse
Wissenstheoriewissen
Segmentauswahl
Der Zugriff auf die globale Deskriptortabelle nach GDTR erfolgt über den "Segment Selector" (Segmentregister im realen Modus)
15 3 2 1 0
| Index | | Rpl |
- 3-15 ist der Deskriptorindex, der den Deskriptor des erforderlichen Segments in der Deskriptor-Tabelle angibt.
- 2 Um anzuzeigen, ob der Selektor in GDT oder in LDT ausgewählt ist (0 repräsentiert GDT 1 LDT).
- 0-1 ist die für die Auswahl von Privilegien.
Der Segment -Selektor enthält drei Teile: Deskriptorindex (Index), TI und Request -Privilege Level (RPL). Der Teil des Index (Deskriptorindex) gibt den Deskriptor des erforderlichen Segments in der Position der Deskriptorentelle an. Aus dieser Position kann der entsprechende Deskriptor basierend auf der in GDTR gespeicherten Deskriptor -Tabellenadresse gefunden werden. Anschließend kann die Segmentbasisadresse in der Deskriptor -Tabelle plus die logische Adresse (SEL: Offset) in eine lineare Adresse konvertiert werden. Der TI -Wert im Segment -Selektor beträgt nur ein 0 oder 1. Die Anfrage -Privilegienstufe (RPL) repräsentiert die Privilegienstufe des Selektors, und es gibt 4 Privilegien (Stufe 0, Stufe 1, Stufe 2 und Stufe 3).
Hinweis auf der Privilegienebene: Jedes Segment in der Aufgabe hat eine bestimmte Ebene. Immer wenn ein Programm versucht, auf ein Segment zuzugreifen, wird die Berechtigungsstufe, die das Programm hat, mit der zugegriffenen Berechtigungsstufe verglichen, um festzustellen, ob das Segment zugegriffen werden kann. Die Systemkonvention ist, dass die CPU nur auf Segmente derselben Privilegienstufe oder auf niedrigerer Privilegienebene zugreifen kann.
Geben Sie beispielsweise die logische Adresse an: 21H: 12345678H in die lineare Adresse konvertiert
A. Selector SEL = 21H = 0000000000100 0 01 (b) bedeutet: Der Index des Selektors = 4, dh 0100, und der vierte Deskriptor in GDT wird ausgewählt; Ti = 0 bedeutet, dass der Selektor in GDT ausgewählt ist; Die letzte 01 bedeutet eine Privilegienebene rpl = 1.
B. Offset = 12345678H Wenn die im vierte Deskriptor von GDT zu diesem Zeitpunkt beschriebene Segmentbasisadresse 1111111h ist, dann ist die lineare Adresse = 111111H + 12345678H = 23456789H.
Besuchen Sie GDT
- Holen Sie sich zunächst die GDT -Basisadresse aus dem GDTR -Register.
- In GDT wird der Segmentdeskriptor dann im Segment-Selector High 13-Bit-Positionsindex bewertet.
- Holen Sie sich die Basisadresse und fügen Sie den Offset hinzu, um die lineare Adresse zu erhalten.
Zugang zu LDT
- Holen Sie sich zunächst die GDT -Basisadresse aus dem GDTR -Register.
- Holen Sie sich den Positionsindex des Segments, in dem sich der LDT aus dem LDTR -Register befindet (LDTR ist 13 Bit höher).
- Der LDT -Segmentdeskriptor wird in GDT mit diesem Positionsindex erhalten, um die LDT -Segment -Basisadresse zu erhalten.
- Verwenden Sie den Segment -Selektor, um den Segmentdeskriptor aus dem LDT -Segment zu erhalten.
- Holen Sie sich die Basisadresse und fügen Sie den Offset hinzu, um die lineare Adresse zu erhalten.
Experimentelles Anleitung Buch: https://www.lanqiao.cn/courses/115/labs/573/document/
✔️ LAB6 Terminal Device Control (Branch Lab6)
- Wenn der Tastatur -Interrupt auftritt, wird der Tastatur -Scan -Code entfernt und der Scan -Code gemäß der Tabelle Key_Table verarbeitet.
- Vervollständigen Sie die Funktion zum Schreiben von F12.
- Fügen Sie nach der Verarbeitung die Zeichen mit dem entsprechenden Scan -Code in put_queue ein.
- Rufen Sie do_tty_interrupt für die endgültige Verarbeitung auf, wobei Copy_to_Coked die endgültige Vorverarbeitung durchführt, und rufen Sie dann Con_write auf, um auf die Grafikkarte auszugeben.
- Schreiben -> sys_write -> tty_write -> con_write.
Experimentelles Anleitung Buch: https://www.lanqiao.cn/courses/115/labs/574/document/
✔️ Implementierung des LAB7 Proc -Dateisystems (Branch Lab7)
Theoretisches Wissen
Scheibe
Das Lesen und Schreiben von mechanischen Festplatten erfordert drei Parameter, um zu lokalisieren
- Zylinder (c)
- Kopf (h)
- Sektor (en)
block = C * ( H * S ) + H * S + S ;
Teilen Sie mehrere Sektoren in einen Block, um die Effizienz des Festplatten -IO zu verbessern (Linux0.11 Teilen 2 Sektoren in einen Block). Für eine höhere Ebene müssen Sie nur die Lese- und Schreibblocknummer eingeben, um die Festplatte IO durchzuführen.
Division: Boot Block | Super Block | Bitmap Inode Bitmap | Datenbitmap | Inode -Block | Datenblock
dokumentieren
Verwenden Sie FCB (Inode in Linux0.11), um Dateiinformationen zu speichern, einschließlich verschiedener Arten von Dateien (z. B. Gerätedateien, Verzeichnisdateien ...).
struct m_inode
{
unsigned short i_mode ; // 文件类型和属性(rwx 位)。
unsigned short i_uid ; // 用户id(文件拥有者标识符)。
unsigned long i_size ; // 文件大小(字节数)。
unsigned long i_mtime ; // 修改时间(自1970.1.1:0 算起,秒)。
unsigned char i_gid ; // 组id(文件拥有者所在的组)。
unsigned char i_nlinks ; // 文件目录项链接数。
unsigned short i_zone [ 9 ]; // 直接(0-6)、间接(7)或双重间接(8)逻辑块号。
/* these are in memory also */
struct task_struct * i_wait ; // 等待该i 节点的进程。
unsigned long i_atime ; // 最后访问时间。
unsigned long i_ctime ; // i 节点自身修改时间。
unsigned short i_dev ; // i 节点所在的设备号。
unsigned short i_num ; // i 节点号。
unsigned short i_count ; // i 节点被使用的次数,0 表示该i 节点空闲。
unsigned char i_lock ; // 锁定标志。
unsigned char i_dirt ; // 已修改(脏)标志。
unsigned char i_pipe ; // 管道标志。
unsigned char i_mount ; // 安装标志。
unsigned char i_seek ; // 搜寻标志(lseek 时)。
unsigned char i_update ; // 更新标志。
}; Die Blocknummer der Datei auf der Festplatte wird im Inode und in einigen anderen Dateibeschreibungsinformationen gespeichert. Es kann mehrere Ebenen der Festplattenblockpositionsanleitung geben, die in den direkten Index- und den indirekten Index unterteilt sind, um die Blockposition zu erhalten.
Inhaltsverzeichnis
Suchen Sie die Daten in der entsprechenden Datenplattenblockennummer gemäß dem Verzeichnis -Inode, der die im Verzeichnis vorhandene Subdadumanlage enthält. Suchschicht für Schicht und Sie können den Ort des endgültigen Zielverzeichnisses finden.
Experimentelles Anleitungsbuch: https://www.lanqiao.cn/courses/115/labs/575/document/