No segundo artigo
anterior consecutivo, desenvolvemos as funções necessárias para trabalhar com seqüências de caracteres da biblioteca C. Nesta lição, implementamos uma saída de depuração completa na tela - o log do sistema do kernel.
Sumário
- Construa o sistema (marca, gcc, gás). Inicialização inicial (inicialização múltipla). Iniciar (qemu). Biblioteca C (strcpy, memcpy, strext).
- Biblioteca C (sprintf, strcpy, strcmp, strtok, va_list ...). Construindo a biblioteca no modo kernel e no modo de aplicativo do usuário.
- O log do sistema do kernel. Memória de vídeo Saída para o terminal (kprintf, kpanic, kassert).
- Memória dinâmica, heap (kmalloc, kfree).
- Organização da memória e manipulação de interrupções (GDT, IDT, PIC, syscall). Exceções
- Memória virtual (diretório e tabela de páginas).
- Processo. Planejador Multitarefa. Chamadas do sistema (interrupção, saída, ps).
- O sistema de arquivos do kernel (initrd), elf e seus internos. Chamadas do sistema (exec).
- Drivers de dispositivo de caracteres. Chamadas do sistema (ioctl, fopen, fread, fwrite). Biblioteca C (fopen, fclose, fprintf, fscanf).
- Shell como um programa completo para o kernel.
- Modo de proteção do usuário (anel3). Segmento de status da tarefa (tss).
Log do sistema do kernel
Antes de começarmos, precisaremos apresentar algumas funções úteis para trabalhar com portas de E / S. As portas de E / S para um programador não são diferentes das células comuns na memória, exceto que existem comandos separados para operá-las. Os dispositivos que operam nessas portas estão conectados ao barramento de memória. Há também um espaço de endereço dedicado para eles. Vamos precisar de duas funções de assembler para trabalhar com portas de entrada / saída, porque como você já se lembra, eu não tolero inserções de assembler.
extern u_char asm_read_port(u_char port); extern void asm_write_port(u_int port, u_char data);
Da mesma forma, dois comandos para controlar o processador mascarado são interrompidos.
extern void asm_lock(); extern void asm_unlock();
Bem, para economizar energia após erros fatais, você precisa de um comando de parada do processador.
extern void asm_hlt();
Como você se lembra, a memória de vídeo começa em 0xB8000, mas eu sugiro escrever as mensagens primeiro no buffer em formato de texto sem formatação. E então basta copiar esse buffer na memória de vídeo, levando em consideração os atributos de cores. Para fazer isso, implementei vários utilitários para trabalhar com esses buffers. Um buffer será para o syslog do kernel e o restante para os terminais virtuais. A rolagem da tela também será realizada no buffer. E apenas a função video_flush copiará o buffer para a memória de vídeo, expandindo-o com 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);
Agora é a hora de apresentar os recursos mais usados. Os dois últimos serão usados para depurar o kernel quando a preguiça for depurada pelo depurador. Acredite, eu nunca usei um depurador quando escrevi este kernel.
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);
Bem, na verdade, funções para trabalhar com o log do sistema do kernel. Para controlar o que é exibido na tela, entrei na função kmode no log do sistema ou no console do usuário. E para ler o log do sistema no buffer, a função klog será necessária, porque os processos do usuário não terão acesso ao kernel, exceto por meio de chamadas do 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);
Dou as funções mais interessantes aqui:
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); } }
Veja o tutorial detalhado no tutorial em vídeo.
Referências
→
Tutorial em vídeo deste artigo→
Código fonte (você precisa de um ramo da lição3)
Referências
- James Molloy. Role seu próprio sistema operacional clone do UNIX de brinquedo.
- Zubkov. Assembler para DOS, Windows, Unix
- Kalashnikov. Assembler é fácil!
- Tanenbaum. Sistemas operacionais. Implementação e desenvolvimento.
- Robert Love. Kernel Linux Descrição do processo de desenvolvimento.