在
上一篇文章中,我们学习了如何运行Hello World内核,并编写了一些用于处理字符串的函数。 现在是时候扩展C库,以便您可以实现kprintf和其他必要的功能了。 走吧
目录
- 构建系统(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)。
图书馆C
首先,您需要使用明确的维度来实现类型。
由于我们将在一个平台上进行收集,因此直到我们的定义和实现正确为止。 它不是通用的,但这就是为什么它易读且简单的原因。 我坚持这种方法,有时最好做一些非通用的事情,但要满足当前的要求,因为维护通用解决方案非常困难。
typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned int u_long;
引入布尔类型并没有什么坏处。
#pragma once /* types */ typedef int bool; #define true 1 #define false 0 #define null 0
同样,使用几个宏来处理字节也不会伤害我们。
typedef unsigned long size_t; #define HIGH_WORD(addr) ((addr & 0xffff0000) >> 16) #define LOW_WORD(addr) ((addr & 0xffff)) #define LOW_BYTE(addr) ((addr & 0x00ff)) #define HIGH_BYTE(addr) ((addr & 0xff00) >> 8)
需要以下宏才能使用可变数量的参数。 这种方法仅在函数遵循C语言调用约定的情况下有效,在C语言调用约定中,函数参数从最后一个开始通过堆栈传递。
typedef size_t* va_list; #define va_start(l, a) (l = (void*)((size_t)&a) + sizeof(a)) #define va_end(l) (l = (void*)0) #define va_arg(l, s) (*(s*)(l++))
当然,可以另辟other径。 与其定义自己的函数,不如尝试使用内置库并替换将通过LD_PRELOAD访问内核的函数。 但是我喜欢完全控制该过程,因此让该选项留给那些开始在本教程中编写其操作系统的人一个想法。
此外,在
视频教程中,我们将考虑以下库函数的实现。 该实现并不声称是最优性和完整性,但我认为它声称是简单性和可读性。 我只是注意到我们正在使用strtok函数的线程安全实现,称为strtok_r。 在上一课中,我们自己想到了strinv和strext函数。 如果您熟悉C语言,那么我想您将会熟悉下面列出的几乎所有功能。
extern int strlen(const char* s); extern char* strcpy(char* s1, const char* s2); extern char* strncpy(char* s1, const char* s2, u_int n); extern void* memcpy(void* buf1, const void* buf2, u_int bytes); extern void* memset(void* buf1, u8 value, u_int bytes); extern int strcmp(const char* s1, const char* s2); extern int strncmp(const char* s1, const char* s2, u_int n); extern char* strcat(char* s1, const char* s2); extern char* strext(char* buf, const char* str, char sym); extern int strspn(char* str, const char* accept); extern int strcspn(char* str, const char* rejected); char* strchr(const char* str, char ch); extern char* strtok_r(char* str, const char* delims, char** save_ptr); extern char* memext(void* buff_dst, u_int n, const void* buff_src, char sym); extern char* itoa(unsigned int value, char* str, unsigned int base); extern unsigned int atou(char* str); extern char* strinv(char* str); extern unsigned int sprintf(char* s1, const char* s2, ...); extern unsigned int snprintf(char* s1, u_int n, const char* s2, ...); extern unsigned int vsprintf(char* s1, const char* s2, va_list list); extern unsigned int vsnprintf(char* s1, unsigned int n, const char* s2, va_list list);
例行工作已取消。 样板代码的末尾。 下一课将变得更加复杂和有趣。 如果您想赞助一个项目,则可以提供库函数的最佳实现。
参考文献
开发单片类Unix操作系统-入门本文的视频教程源代码(您需要一个lesson2分支)参考文献
- 詹姆斯·莫洛伊(James Molloy)。 滚动自己的玩具UNIX克隆操作系统。
- 祖布科夫。 DOS,Windows,Unix的汇编器
- 卡拉什尼科夫。 汇编程序很简单!
- Tanenbaum。 操作系统。 实施与开发。
- 罗伯特·洛夫(Robert Love)。 Linux内核 开发过程的描述。