Dans le deuxième article
précédent , nous avons développé les fonctions nécessaires pour travailler avec des chaînes de la bibliothèque C. Dans cette leçon, nous implémentons une sortie de débogage complète à l'écran - le journal système du noyau.
Table des matières
- Construisez le système (make, gcc, gas). Démarrage initial (multiboot). Lancez (qemu). Bibliothèque C (strcpy, memcpy, strext).
- Bibliothèque C (sprintf, strcpy, strcmp, strtok, va_list ...). Construction de la bibliothèque en mode noyau et en mode application utilisateur.
- Le journal système du noyau. Mémoire vidéo Sortie vers le terminal (kprintf, kpanic, kassert).
- Mémoire dynamique, tas (kmalloc, kfree).
- Organisation de la mémoire et gestion des interruptions (GDT, IDT, PIC, syscall). Exceptions
- Mémoire virtuelle (répertoire de pages et table de pages).
- Processus. Planificateur Multitâche. Appels système (kill, exit, ps).
- Le système de fichiers du noyau (initrd), elf et ses composants internes. Appels système (exec).
- Pilotes de périphériques de caractères. Appels système (ioctl, fopen, fread, fwrite). Bibliothèque C (fopen, fclose, fprintf, fscanf).
- Shell comme programme complet pour le noyau.
- Mode de protection utilisateur (ring3). Segment d'état de la tâche (tss).
Journal système du noyau
Avant de commencer, nous devrons introduire quelques fonctions utiles pour travailler avec les ports d'E / S. Les ports d'E / S pour un programmeur ne sont pas différents des cellules ordinaires en mémoire, sauf qu'il existe des commandes distinctes pour les faire fonctionner. Les périphériques qui fonctionnent sur ces ports sont connectés au bus mémoire. Il y a aussi un espace d'adressage dédié pour eux. Nous aurons besoin de deux fonctions d'assembleur pour travailler avec les ports d'entrée / sortie, car comme vous vous en souvenez déjà, je ne tolère pas les insertions d'assembleur.
extern u_char asm_read_port(u_char port); extern void asm_write_port(u_int port, u_char data);
De même, deux commandes pour contrôler les interruptions du processeur masqué.
extern void asm_lock(); extern void asm_unlock();
Eh bien, pour économiser de l'énergie après des erreurs fatales, vous avez besoin d'une commande d'arrêt du processeur.
extern void asm_hlt();
Comme vous vous en souvenez, la mémoire vidéo commence à 0xB8000, mais je suggère d'écrire d'abord les messages dans le tampon au format texte brut. Et puis copiez simplement ce tampon dans la mémoire vidéo, en tenant compte des attributs de couleur. Pour ce faire, j'ai implémenté plusieurs utilitaires pour travailler avec ces tampons. Un tampon sera pour le syslog du noyau, et le reste pour les terminaux virtuels. Le défilement de l'écran sera également effectué sur le tampon. Et seule la fonction video_flush copiera le tampon dans la mémoire vidéo, en l'étendant avec des attributs.
extern void video_init(); extern void video_disable_cursor(); extern void* video_scroll(char const* video_buff, char* pos); extern char* video_clear(char const* video_buff); extern void video_flush(char const* video_buff);
Il est maintenant temps de présenter les fonctionnalités les plus couramment utilisées. Les deux derniers seront utilisés pour déboguer le noyau lorsque la paresse est déboguée par le débogueur. Croyez-moi, je n'ai jamais utilisé de débogueur lorsque j'ai écrit ce noyau.
extern void kpanic(char* message, ...); extern void kassert(const char* file, u_int line, bool expr); extern void kunreachable(const char* file, u_int line);
Eh bien, en fait, des fonctions pour travailler avec le journal système du noyau. Afin de contrôler ce qui est affiché à l'écran, j'ai entré la fonction kmode dans le journal système ou la console utilisateur. Et pour lire le journal système dans le tampon, la fonction klog sera nécessaire, car les processus utilisateur n'auront accès au noyau que par le biais d'appels système.
extern void kclear(); extern void kprintf(const char* format, ...); extern void kvprintf(const char* format, va_list list); extern void kmode(bool is_early); extern void klog(char* buf, u_int n);
Je donne ici les fonctions les plus intéressantes:
extern void* video_scroll(char const* video_buff, char* pos) { char* ptr = (void*)video_buff; for (int i = 1; i < VIDEO_SCREEN_HEIGHT; ++i) { for (int j = 0; j < VIDEO_SCREEN_WIDTH; ++j) { ptr[(i - 1) * VIDEO_SCREEN_WIDTH + j] = ptr[i * VIDEO_SCREEN_WIDTH + j]; } } for (int j = 0; j < VIDEO_SCREEN_WIDTH; ++j) { ptr[(VIDEO_SCREEN_HEIGHT - 1) * VIDEO_SCREEN_WIDTH + j] = ' '; } pos -= VIDEO_SCREEN_WIDTH; return pos; }
extern void kvprintf(const char* format, va_list list) { char buff[VIDEO_SCREEN_WIDTH]; int len = vsprintf(buff, format, list); for (int i = 0; i < len; ++i) { if (buff[i] != '\n') { kputc(buff[i]); } else { int line_pos = (syslog_pos - syslog) % VIDEO_SCREEN_WIDTH; for (int j = 0; j < VIDEO_SCREEN_WIDTH - line_pos; ++j) { kputc(' '); } } } kflush(); }
static void kputc(char ch) { if ((size_t)syslog_pos - (size_t)syslog + 1 < VIDEO_SCREEN_SIZE) { *syslog_pos++ = ch; } else { syslog_pos = video_scroll(syslog, syslog_pos); kputc(ch); } }
Voir le tutoriel détaillé dans le tutoriel vidéo.
Les références
→
Tutoriel vidéo pour cet article→
Code source (vous avez besoin d'une branche de leçon3)
Les références
- James Molloy. Faites rouler votre propre système d'exploitation jouet UNIX-clone.
- Zubkov. Assembleur pour DOS, Windows, Unix
- Kalachnikov. L'assembleur est facile!
- Tanenbaum. Systèmes d'exploitation. Mise en œuvre et développement.
- Robert Love. Noyau Linux Description du processus de développement.