在
前面的第二篇文章中,我们开发了使用C库中的字符串的必要函数,在本课程中,我们将完整的调试输出实现到屏幕上-内核系统日志。
目录
- 构建系统(make,gcc,gas)。 初始引导(多次引导)。 启动(qemu)。 C库(strcpy,memcpy,strext)。
- C库(sprintf,strcpy,strcmp,strtok,va_list ...)。 以内核模式和用户应用程序模式构建库。
- 内核系统日志。 显存 输出到终端(kprintf,kpanic,kassert)。
- 动态内存,堆(kmalloc,kfree)。
- 内存和中断处理的组织(GDT,IDT,PIC,syscall)。 例外情况
- 虚拟内存(页面目录和页面表)。
- 过程。 策划人 多任务处理。 系统调用(kill,exit,ps)。
- 内核(initrd),elf及其内部文件系统。 系统调用(执行)。
- 字符设备驱动程序。 系统调用(ioctl,fopen,fread,fwrite)。 C库(fopen,fclose,fprintf,fscanf)。
- Shell作为内核的完整程序。
- 用户保护模式(ring3)。 任务状态段(tss)。
内核系统日志
在开始之前,我们将需要介绍一些使用I / O端口的有用功能。 程序员的I / O端口与内存中的普通单元没有什么不同,除了有单独的命令可操作它们。 在这些端口上运行的设备已连接到内存总线。 还有一个专用的地址空间。 我们将需要两个汇编器功能来使用输入/输出端口,因为您已经记住,我不容许汇编器插入。
extern u_char asm_read_port(u_char port); extern void asm_write_port(u_int port, u_char data);
同样,有两个用于控制屏蔽的处理器中断的命令。
extern void asm_lock(); extern void asm_unlock();
好吧,为了在发生致命错误后节省能量,您需要一个处理器停止命令。
extern void asm_hlt();
您还记得,视频内存从0xB8000开始,但是我建议先以纯文本格式将消息写入缓冲区。 然后考虑到颜色属性,只需将该缓冲区复制到视频内存中即可。 为此,我实现了几个实用程序来使用这些缓冲区。 一个缓冲区将用于内核syslog,其余缓冲区将用于虚拟终端。 滚动屏幕也将在缓冲区上执行。 并且只有video_flush函数会将缓冲区复制到视频内存,并使用属性对其进行扩展。
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);
现在该介绍最常用的功能了。 当惰性由调试器调试时,最后两个将用于调试内核。 相信我,我在编写此内核时从未使用过调试器。
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);
好吧,实际上,是用于内核系统日志的功能。 为了控制屏幕上显示的内容,我在系统日志或用户控制台中输入了kmode功能。 要将系统日志读入缓冲区,将需要klog函数,因为用户进程除非通过系统调用,否则将无法访问内核。
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);
我在这里给出了最有趣的功能:
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); } }
请参阅视频教程中的详细教程。
参考文献
→
本文的视频教程→
源代码 (您需要一个lesson3分支)
参考文献
- 詹姆斯·莫洛伊(James Molloy)。 滚动自己的玩具UNIX克隆操作系统。
- 祖布科夫。 DOS,Windows,Unix的汇编器
- 卡拉什尼科夫。 汇编程序很简单!
- Tanenbaum。 操作系统。 实施与开发。
- 罗伯特·洛夫(Robert Love)。 Linux内核 开发过程的描述。