En el segundo artículo consecutivo anterior, desarrollamos las funciones necesarias para trabajar con cadenas de la biblioteca C. En esta lección, implementamos una salida de depuración completa en la pantalla: el registro del sistema del núcleo.
Tabla de contenidos
- Sistema de construcción (marca, gcc, gas). Arranque inicial (arranque múltiple). Lanzamiento (qemu). Biblioteca C (strcpy, memcpy, strext).
- Biblioteca C (sprintf, strcpy, strcmp, strtok, va_list ...). Creación de la biblioteca en modo kernel y modo de aplicación de usuario.
- El registro del sistema del núcleo. Memoria de video Salida a la terminal (kprintf, kpanic, kassert).
- Memoria dinámica, montón (kmalloc, kfree).
- Organización de memoria y manejo de interrupciones (GDT, IDT, PIC, syscall). Excepciones
- Memoria virtual (directorio de páginas y tabla de páginas).
- Proceso. Planificador Multitarea Sistema de llamadas (kill, exit, ps).
- El sistema de archivos del kernel (initrd), elf y sus componentes internos. Sistema de llamadas (exec).
- Controladores de dispositivos de caracteres. Llamadas del sistema (ioctl, fopen, fread, fwrite). Biblioteca C (fopen, fclose, fprintf, fscanf).
- Shell como un programa completo para el kernel.
- Modo de protección del usuario (anillo3). Segmento de estado de la tarea (tss).
Registro del sistema de kernel
Antes de comenzar, necesitaremos presentar algunas funciones útiles para trabajar con puertos de E / S. Los puertos de E / S para un programador no son diferentes de las celdas ordinarias en la memoria, excepto que hay comandos separados para operarlos. Los dispositivos que operan en estos puertos están conectados al bus de memoria. También hay un espacio de direcciones dedicado para ellos. Necesitaremos dos funciones de ensamblador para trabajar con puertos de entrada / salida, porque como ya recordará, no tolero las inserciones de ensamblador.
extern u_char asm_read_port(u_char port); extern void asm_write_port(u_int port, u_char data);
Del mismo modo, dos comandos para controlar interrupciones de procesador enmascaradas.
extern void asm_lock(); extern void asm_unlock();
Bueno, para ahorrar energía después de errores fatales, necesita un comando de parada del procesador.
extern void asm_hlt();
Como recordará, la memoria de video comienza en 0xB8000, pero sugiero escribir primero los mensajes en el búfer en formato de texto sin formato. Y luego simplemente copie este búfer en la memoria de video, teniendo en cuenta los atributos de color. Para hacer esto, implementé varias utilidades para trabajar con estos búferes. Un búfer será para el syslog del núcleo y el resto para terminales virtuales. El desplazamiento de la pantalla también se realizará en el búfer. Y solo la función video_flush copiará el búfer en la memoria de video, expandiéndolo con atributos.
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);
Ahora es el momento de presentar las funciones más utilizadas. Los dos últimos se utilizarán para depurar el núcleo cuando el depurador depure la pereza. Créame, nunca he usado un depurador cuando escribí este núcleo.
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);
Bueno, en realidad, funciona para trabajar con el registro del sistema del núcleo. Para controlar lo que se muestra en la pantalla, ingresé la función kmode en el registro del sistema o en la consola del usuario. Y para leer el registro del sistema en el búfer, necesitará la función klog, porque los procesos del usuario no tendrán acceso al núcleo, excepto a través de llamadas al sistema.
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);
Doy las funciones más interesantes aquí:
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); } }
Vea el tutorial detallado en el video tutorial.
Referencias
→
Video tutorial para este artículo→
Código fuente (necesita una rama de lección3)
Referencias
- James Molloy Haga rodar su propio sistema operativo de clones UNIX de juguete.
- Zubkov Ensamblador para DOS, Windows, Unix
- Kalashnikov. ¡Ensamblador es fácil!
- Tanenbaum Sistemas operativos Implementación y desarrollo.
- Robert Love Kernel de Linux Descripción del proceso de desarrollo.