Linux-0.11
Cours de théorie: https://www.bilibili.com/video/bv1d4411v7u7
Références utiles ( Linux-0.11 Version commentée): https://github.com/beride/linux0.11-1
La branche principale est le code source Linux-0.11
Afficher la différence de code: Pull Demande Sélectionnez la branche correspondante et comparez-la avec le principal
✔️ Boot du système d'exploitation LAB0 (Branch Lab0)
Réécrire BootsEct.s complète principalement les fonctions suivantes:
- BootsECT.s peut imprimer un message "XXX démarre ..." à l'écran.
La réécriture de la configuration.s complète principalement les fonctions suivantes:
- BootsECT.s peut compléter le chargement de Configuration et sauter à la configuration.S Démarrer l'adresse d'exécution. et setup.s sort une ligne "Maintenant, nous sommes en configuration" à l'écran. Setup.s peut obtenir au moins un paramètre matériel de base (tel que les paramètres de mémoire, les paramètres de la carte graphique, les paramètres du disque dur, etc.), le stocker dans une adresse spécifique en mémoire et le sortir à l'écran. Setup.s ne charge plus le noyau Linux, gardez simplement les informations ci-dessus affichées à l'écran.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/568/document/
✔️ LAB1 implémente les appels du système (Branch Lab1)
- Le nombre total d'appels système modifiés dans Kernel / System_Call.s est de 74.
- Ajoutez une macro dans include / unistd.h pour indiquer le positionnement de la table de fonction d'appel.
- Écrivez deux définitions de fonctions de fonctions (SYS_IAM, SYS_WOAMI) dans include / linux / sys.h. Et ajouter deux nouvelles fonctions à la table de fonction après.
- Ajout de Kernel / Who.c pour implémenter deux appels système.
- Modifier makefile
- Objs augmente la dépendance de l'OMS (OMS)
- Ajouter les conditions de génération de dépendances pour l'OM.S et l'OM.O dans les dépendances
- Faites l'exécution ./Run pour entrer le sous-système Linux et ajoutez les définitions de macro d'IAM et WHOAMI dans /USR/include/unistd.h (identique au point 2).
- La rédaction d'un programme de test dans l'état utilisateur teste si elle réussit.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/569/document/
✔️ LAB2 met en œuvre le suivi de la trajectoire de l'opération de processus (Branch Lab2)
- Process.c implémente un scénario qui simule un calcul de CPU hybride et un calcul IO, et l'exécute de manière multi-processus.
- Imprimez plusieurs états de commutation de processus du noyau (temps d'état PID) à /var/process.log.
- Une fois que la fonction init () dans init / main entre dans l'instruction de l'état utilisateur, Associer le descripteur de fichiers 3 à /var/process.log.
- Implémentez la fonction fprintk () dans le noyau / printk pour imprimer la sortie au journal process.log.
- Trouvez le point de commutation d'état et ajoutez fprintk () pour écrire des journaux.
- Processus Start Kernel / System_Call.s -> SYS_FORK (CALL COPY_PROCESS)
- Exécuter, bloquer le noyau de commutateur d'état / sched.c -> Planifiez sys_pause sleep_on interruption_sleep_on wake_up
- Exit Kernel / exit.c -> do_exit sys_waitpid
- Remarque: GCC dans Linux-0.11 ne peut pas être compilé // commenté.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/570/document/
✔️ Lab3 remplace le processus TSS d'origine dans Linux0.11 pour passer à la commutation de pile (Branch Lab3)
La méthode de commutation d'origine se situe via TSS, ce qui équivaut à un instantané du registre. Il est directement remplacé sur place à travers les instructions fournies par Intel, qui est plus lente. La commutation de pile est plus efficace.
noyau / sched.c
- La méthode Switch_To d'origine est basée sur le commutateur TSS, et nous voulons le commenter dans le fichier d'en-tête.
- New switch_to, nécessite deux paramètres (1: pointeur vers le prochain PCB 2: La position de la tâche suivante dans le tableau est utilisée pour changer LDT).
kernel / system_call.s
- Implémentation d'écriture de commutateur_to
- Commutation de PCB
- Réécrivez la position de la pile du noyau TSS (TSS est conservé pour le moment, mais il n'y a qu'un seul TSS dans le monde, et il n'est pas utilisé pour faire de la commutation de processus.)
- Changer la pile du noyau
- Interrupteur LDT
noyau / fork.c
- Connectez-vous des paramètres TSS dans PCB.
- Ajoutez une nouvelle variable membre - pile de noyau à PCB, qui est utilisée pour stocker les informations de pile de noyau.
- Écrivez des informations de pile ici et laissez PCB la variable membre pointer vers le pointeur.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/571/document/
Référence: https://blog.csdn.net/qq_42518941/article/details/119182097
✔️ Lab4 implémente les appels du système de sémaphore dans Linux0.11 (Branch Lab4)
Points de connaissance pré-clés
- kernel / sched.c sleep_on passer l'en-tête de file d'attente d'attente entrant, définir le courant sur l'état de blocage et exécuter activement calendrier (). TMP stocke la file d'attente de blocage d'origine. Lorsqu'ils sont éveillés, les files d'attente de blocage sont toutes prêtes à courir.
- kernel / sched.c wake_up réveille tous les PCB bloqués. Seul le chef d'équipe de réveil peut être vu dans le programme, mais il doit être lu en combinaison avec Sleep_on. Une fois combinée, vous constaterez que la tête de réveil se réveillera tous par la suite.
Écrivez l'application "PC.C" pour résoudre le problème de producteur-consommateur classique et remplir les fonctions suivantes:
- Établir un processus de producteur, n processus de consommation (n> 1)
- Créer un tampon partagé avec des fichiers
- Le processus du producteur écrit des entiers 0, 1, 2,…, m, m> = 500 au tampon tour à tour
- Le processus de consommation se lit dans le tampon, en lit un à la fois et supprime les numéros de lecture du tampon, puis publie l'ID de processus et les nombres à la sortie standard
- Le tampon ne peut économiser que jusqu'à 10 numéros en même temps
Mettre en œuvre le sémaphore
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 ouvre un sémaphore
- Si le sémaphore est actuellement égal à zéro, bloquant le processus
- Ajouter un sémaphore
- Éteindre un sémaphore
L'attente de l'attente et du post
- attendez
- Appelez le noyau / sched.c sleep_on pour attendre le blocage
- Utilisez l'interruption Linux (Linux0.11 Single Core) pour protéger la zone critique
- moins une valeur de SEM
- poste
- Ajoutez-en un à la valeur de SEM. Si la valeur est supérieure à 0, appelez le noyau / sched.c wake_up pour réveiller le processus bloqué par ce sémaphore.
- Utilisez l'interruption Linux (Linux0.11 Single Core) pour protéger la zone critique
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/572/document/
✔️ Mappage et partage des adresses LAB5 (Branch Lab5)
Adresse logique -> GDT -> LDT -> Tableau de page -> Adresse physique
Connaissances de la théorie des instruments
Sélecteur de segment
L'accès à la table des descripteurs globaux par GDTR est effectué via le "Segment Sélecteur" (segment Register en mode réel)
15 3 2 1 0
| Index | | RPL |
- 3-15 est l'index du descripteur, qui indique le descripteur du segment requis dans le tableau descripteur.
- 2 Pour indiquer si le sélecteur est sélectionné dans GDT ou dans LDT (0 représente GDT 1 représente LDT).
- 0-1 est le niveau de privilège à sélectionner.
Le sélecteur de segment comprend trois parties: le descripteur Index (Index), Ti et Demande Privilege Niveau (RPL). Sa pièce d'index (index descripteur) indique le descripteur du segment requis dans la position de la table descripteurs. À partir de cette position, le descripteur correspondant peut être trouvé sur la base de l'adresse de base de la table de description stockée dans GDTR. Ensuite, l'adresse de base du segment dans la table descripteurs plus l'adresse logique (SEL: Offset) peut être convertie en une adresse linéaire. La valeur Ti dans le sélecteur de segment n'est qu'un 0 ou 1. 0 signifie que le sélecteur est sélectionné dans GDT, et 1 signifie que le sélecteur est sélectionné dans LDT. Le niveau de privilège de demande (RPL) représente le niveau de privilège du sélecteur, et il y a 4 niveaux de privilège (niveau 0, niveau 1, niveau 2 et niveau 3).
Remarque au niveau du privilège: chaque segment de la tâche a un niveau spécifique. Chaque fois qu'un programme essaie d'accéder à un segment, le niveau de privilège dont dispose le programme est comparé au niveau de privilège à accessible pour déterminer si le segment est accessible. La convention système est que le CPU ne peut accéder qu'à des segments du même niveau de privilège ou à un niveau de privilège inférieur.
Par exemple, donnez l'adresse logique: 21h: 12345678h converti en adresse linéaire
un. Sélecteur SEL = 21H = 0000000000100 0 01 (b) signifie: l'index du sélecteur = 4, c'est-à-dire 0100, et le quatrième descripteur de GDT est sélectionné; Ti = 0 signifie que le sélecteur est sélectionné dans GDT; Le dernier 01 signifie le niveau de privilège RPL = 1.
né Offset = 12345678h Si l'adresse de base du segment décrite dans le quatrième descripteur de GDT à l'heure actuelle est 1111111h, alors l'adresse linéaire = 11111h + 12345678h = 23456789h.
Visiter GDT
- Obtenez d'abord l'adresse de base GDT du registre GDTR.
- Ensuite, dans GDT, le descripteur du segment est évalué au segment du segment élevé indice de position 13 bits.
- Obtenez l'adresse de base, ajoutez le décalage pour obtenir l'adresse linéaire.
Accéder à LDT
- Obtenez d'abord l'adresse de base GDT du registre GDTR.
- Obtenez l'indice de position du segment où le LDT est situé à partir du registre LDTR (LDTR est 13 bits plus haut).
- Le descripteur du segment LDT est obtenu dans GDT avec cet index de position pour obtenir l'adresse de base du segment LDT.
- Utilisez le sélecteur de segment pour obtenir le descripteur du segment du segment LDT.
- Obtenez l'adresse de base, ajoutez le décalage pour obtenir l'adresse linéaire.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/573/document/
Contrôle du terminal LAB6 (Branch Lab6)
- Lorsque l'interruption du clavier se produit, le code de balayage du clavier est supprimé et le code de numérisation est traité en fonction de la table KEY_TABLE.
- Terminez la fonction d'écriture pour la touche F12.
- Après le traitement, placez les caractères avec le code de balayage correspondant dans put_queue.
- Appelez DO_TTY_INTERRURT pour le traitement final, où Copy_To_Cooked fait le prétraitement final, puis appelez Con_write pour sortir sur la carte graphique.
- Write -> sys_write -> tty_write -> con_write.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/574/document/
✔️ Implémentation du système de fichiers LAB7 Proc (Branch Lab7)
Connaissance théorique
disque
La lecture et l'écriture des disques mécaniques nécessitent trois paramètres pour localiser
- Cylindre (c)
- Tête (h)
- Secteur (s)
block = C * ( H * S ) + H * S + S ;
Diviser plusieurs secteurs en un bloc pour améliorer l'efficacité du disque IO (Linux0.11 Divisant 2 secteurs en un seul bloc). Pour un niveau supérieur, il vous suffit de saisir le numéro de bloc de lecture et d'écriture pour effectuer un disque IO.
Division: bloc de démarrage | Super bloc | bitmap inode | Data Bitmap | Bloc inode | Bloc de données
document
Utilisez FCB (Inode dans Linux0.11) pour stocker des informations sur les fichiers, y compris différents types de fichiers (par exemple: fichiers de périphérique, fichiers de répertoire ...).
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 ; // 更新标志。
}; Le numéro de bloc du fichier sur le disque est stocké dans l'inode Inode et quelques autres informations de description de fichier. Il peut y avoir plusieurs niveaux de guidage de position de bloc de disque, qui sont divisés en index direct et index indirect pour obtenir la position du bloc.
Table des matières
Trouvez les données du numéro de bloc de disque de données correspondant en fonction du répertoire Inode, qui contient le numéro de bloc de disque Inode du sous-répertoire qui existe dans le répertoire. Rechercher la couche par couche et vous pouvez trouver l'emplacement du répertoire cible final.
Book Instructions expérimental: https://www.lanqiao.cn/courses/115/labs/575/document/