Un système d'exploitation jouet, 32 bits implémente enfin une coque interactive simple 
Liste des tâches:
Il y a deux disques durs au total, le système lui-même est installé sur le disque principal, et le mode de démarrage de MBR est adopté, et le processus de MBR-> Boot Loader-> Kernnel est utilisé.
MBR est situé dans 1 secteur en commençant LBA 0号扇区
Boot Loader est situé dans 4 secteurs en commençant par LBA 2号扇区du disque
Kernel est situé dans les 200 secteurs à commencer par LBA 9号扇区
Le système de fichiers est implémenté sur le disque d'esclaves. Ce n'est peut-être pas très raisonnable. Si le système de fichiers est basé sur la logique du système commercial, il doit être implémenté maintenant, puis le système d'exploitation est installé dans la partition correspondante.
Paging de la mémoire, une page est 4KB
La gestion de la mémoire adopte la gestion de Bitmap et se distingue par la taille lors de l'allocation de la mémoire. S'il est supérieur à 1024 octets, il est alloué directement par page.
S'il est inférieur à 1024 octets, sur la base de l'allocation de l'arène par page, utilisez la blockchain inactive dans l'arène pour l'attribution et le contrôle.
Pour plus de commodité, bien que le mécanisme de pagination soit activé, la fonction de commutation des pages de mémoire et des disques n'est pas implémentée.

PCB est de 1 page
L'essence principale de la planification des threads est de changer de PCB en contrôlant la commutation du pointeur ESP par interruption d'horloge. La priorité se reflète dans la longueur de la tranche de temps de fonctionnement de chaque fil.
La mise en œuvre du processus est basée sur des threads, et la sélection de TSS est basée sur Linux, en utilisant des piles TSS unique pour sauvegarder le niveau 0 et des pointeurs de pile de niveau 0. La plus grande différence par rapport à un thread est que le processus a une adresse de table de page dans le PCB, qui est la plus grande différence entre un processus et un thread. Le processus a vraiment son propre espace mémoire virtuel indépendant.
Il n'y a pas d'algorithme efficace dans la planification, utilisez simplement la planification de la boucle de file d'attente
Implémentation du fil inactif
L'implémentation du thread inactif est très simple. Lorsque la première fois que vous obtenez l'horaire, vous le bloquez et le donnez du CPU. Lorsque le planificateur exécute à nouveau la répartition, si aucun thread ou processus prêt n'est trouvé dans la file d'attente prête, le fil inactif sera éveillé. À l'heure actuelle, le fil inactif suspend le CPU via hlt . Lorsque la tranche de temps est utilisée et que le processeur n'a pas trouvé de processus ou de thread prêt, continuez à remplacer le thread inactif par le CPU. À l'heure actuelle, le ralenti continuera de se bloquer puis commencera à répéter le processus de planification ci-dessus.
// 空载任务
static void idle ( void * arg ) {
while ( 1 ) {
thread_block ( TASK_BLOCKED );
asm volatile ( "sti; hlt" : : : "memory" );
}
}Procédé
La fourche du processus copie d'abord le PCB du processus actuel, puis créez un nouveau tableau de page via le bitmap de pool virtuel du processus actuel. La correspondance de l'adresse virtuelle est exactement la même que dans le processus d'origine. Enfin, un site d'interruption est forgé et le processus enfant est ajouté à la file d'attente de planification et attendez l'exécution de la planification. Dans le site d'interruption forgé, l'EAX dans le PCB du processus enfant est modifié à 0, ce qui signifie que la valeur de retour de la fourche est 0 dans le nouveau processus, tandis que l'EAX dans le PCB du processus parent reste inchangé, représentant le PID du processus enfant. Le processus parent revient via la fin de l'appel système, tandis que le processus de l'enfant revient directement via la fonction de sortie d'interruption.
Processus Exec
L'implémentation de l'exec charge d'abord le fichier ELF du disque à la mémoire, puis modifie le nom du processus dans le PCB du processus actuel et met les paramètres requis par le processus à exécuter dans le registre convenu et modifie EIP au point d'entrée de la fonction d'ELF, Intr_exit d'interruption et exécute le nouveau processus en appelant directement la fonction de sortie d'interruption intr_exit.
Parmi eux, le point d'entrée d'Elf est implémenté en implémentant un CRT extrêmement simple lui-même, qui donne une entrée _Start et pousse les registres de paramètres convenus dans la pile à 3 niveaux, et appelle la fonction principale de la commande externe pour réaliser le passage des paramètres.
[bits 32]
extern main
extern exit
; 这是一个简易版的CRT
; 如果链接时候ld不指定-e main的话,那ld默认会使用_start来充当入口
; 这里的_start的简陋实现,充当了exec调用的进程从伪造的中断中返回时的入口地址
; 通过这个_start, 压入了在execv中存放用户进程参数的两个寄存器。然后call 用户进程main来实现了向用户进程传递参数
section .text
global _start
_start:
;下面这两个要和 execv 中 load 之后指定的寄存器一致
push ebx ;压入 argv
push ecx ;压入 argc
call main
; 压入main的返回值
push eax
call exit ; 不再返回,直接调度别的进程了,这个进程直接被回收了Processez l'attente
Après Fork et exécuté une commande locale, afin de ne pas apparaître, le processus parent doit se terminer dans le processus d'attente local.
L'implémentation ici consiste à saisir l'appel système SYS_WAIT, à traverser toute la file d'attente de processus, à trouver le processus dont le processus parent est son propre état en suspension, puis à obtenir la valeur de retour dans son PCB, à recycler les tableaux PCB et répertoires de page et le supprimer de la file d'attente de planification. Si aucun processus enfant en attente n'est trouvé après la traversée, bloquez lui-même et attendez que le processus de l'enfant se réveille.
Procéder à la sortie
Pendant l'exécution, les commandes externes sont en fait enveloppées par un simple CRT créé par eux-mêmes. Le principal de la commande externe d'appel CRT simple obtiendra sa valeur de retour à la fin, la passera à la sortie, puis l'appeler la sortie
L'implémentation ici fait principalement trois choses:
L'ensemble du processus de chargement des commandes externes et de les exécuter
Tout d'abord, la commande externe doit fournir une fonction int main(int argc, char **argv) . Lors du lien, vous devez apporter un CRT simple start.o fait maison et enfin écrire les commandes externes compilées au système de fichiers.
Lorsqu'une commande externe doit être exécutée, le processus actuel fournit un processus, EXECV dans le nouveau processus, le processus actuel exécute Wait, transmet une adresse pour accepter la valeur de retour du processus enfant, puis bloque et attend le retour du nouveau processus. Le nouveau processus charge les commandes externes du système de fichiers à la mémoire dans EXEVC, et modifie le contenu pertinent en PCB vers les informations des commandes externes, et modifie l'EIP dans la pile d'interruption de PCB avec la commande _Start du CRT (à ce moment-là, le nouveau processus s'est complètement remplacé par le processus de commande externe à exécuter et il utilise enfin le fonctionnement de l'interruption intr_exit pour tracer l'interruption principal de la commande externe. Lorsque l'exécution principale de la commande externe se termine et revient, CRT appelle la sortie via la valeur de retour principale, placez la valeur de retour principale dans sys_exit dans la position correspondante du PCB de processus, recyclez toutes les ressources, sauf PCB et table de répertoire de page, puis réveillez le processus parent et bloquez le processus enfant. Une fois que le parent est éveillé, l'appel système sys_wait obtient la valeur de retour du PCB du PCB du PCB du PCB du PCB du PCB du PCB du PCB du PCB du PCB du PCB du processus d'enfant, le processus de répartition de la file d'attente de la file d'attente et le processus de remise des enfants et le processus de la remise des enfants et les retours et les retours. À ce stade, le processus de l'enfant a été exécuté et est complètement recyclé.
L'implémentation du système de fichiers imite l'inode des systèmes de type UNIX
La partition limite le nombre d'Inodes 4096. Le CPU exploite le disque dur en fonction de la taille du bloc (cluster), et un bloc est réglé sur un secteur, avec 512 octets.
Inode prend en charge 12 blocs directs et 1 table indirecte de premier niveau. Un bloc est de 512 octets dans un secteur, donc un seul fichier prend en charge jusqu'à 140 * 512 octets.
Structure inode

Disposition du système de fichiers

La correspondance entre le descripteur de fichiers et Inode

L'implémentation du pipeline dépend de la structure des fichiers dans le système de fichiers. Son essence est de remplacer l'inode à laquelle la structure de fichiers doit correspondre à l'origine par un espace tampon d'anneau dans l'espace du noyau.
// 因为管道也是当作文件来对待,因此file结构体在针对真实文件和管道是有不同的意义
struct file {
// 文件操作的偏移指针, 当是管道是表示管道打开的次数
uint32_t fd_pos ;
// 文件的操作标志,当是管道是一个固定值0xFFFF
uint32_t fd_flag ;
// 对应的inode指针,当是管道时指向管道的环形缓冲区
struct inode * fd_inode ;
}; Étant donné que l'espace du noyau est partagé, la communication entre différents processus peut être réalisée via des pipelines de lecture et d'écriture. La lecture et l'écriture du pipeline sont encapsulées dans sys_write et sys_read , il n'y a donc pas de différence entre le fonctionnement du pipeline et le fonctionnement des fichiers ordinaires.
L'essence de la redirection consiste à modifier l'adresse de la table de descripteur globale correspondante dans la table des descripteurs de fichiers PCB, puis le fonctionnement de la lecture et de l'écriture du descripteur de fichier correspondant est indiqué vers le nouveau fichier.
La mise en œuvre du caractère du pipeline | Dans le shell est réalisé en redirigeant l'entrée standard et la sortie standard vers le pipeline.
int32_t sys_read ( int32_t fd , void * buf , uint32_t count ) {
if ( fd == stdin_no ) {
if ( is_pipe ( fd )) {
// 从已经重定向好管道中读
ret = pipe_read ( fd , buf , count );
} else {
// 从键盘获取输入
}
} else if ( is_pipe ( fd )) {
// 读管道
ret = pipe_read ( fd , buf , count );
} else {
// 读取普通文件
}
return ret ;
}
int32_t sys_write ( int32_t fd , const void * buf , uint32_t count ) {
if ( fd == stdout_no ) {
if ( is_pipe ( fd )) {
// 向已经重定向好管道中写入
return pipe_write ( fd , buf , count );
} else {
// 向控制台输出内容
}
} else if ( is_pipe ( fd )) {
// 写管道
return pipe_write ( fd , buf , count );
} else {
// 向普通文件写入
}
}