从零实现一个操作系统-day7

字符串函数

因为在内核中,大部分的c标准库函数无法使用,字符串操作的函数又比较常用,所有自己实现一些

include/string.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef INCLUDE_STRING_H_
#define INCLUDE_STRING_H_

#include "types.h"

void memcpy(uint8_t *dest, const uint8_t *src, uint32_t len);

void memset(void *dest, uint8_t val, uint32_t len);

void bzero(void *dest, uint32_t len);

int strcmp(const char *str1, const char *str2);

char *strcpy(char *dest, const char *src);

char *strcat(char *dest, const char *src);

int strlen(const char *src);

#endif // INCLUDE_STRING_H_

libs/string.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include "string.h" 
void memcpy(uint8_t *dest, const uint8_t *src, uint32_t len)
{
int i;
for(i=0;i<len;i++)
{
dest[i]=src[i];
src++;
}
}

void memset(void *dest, uint8_t val, uint32_t len)
{
int i;
char * temp=dest;
for(i=0;i<len;i++)
temp[i]=val;
}

void bzero(void *dest, uint32_t len)
{
memset(dest,0,len);
}

int strcmp(const char *str1, const char *str2)
{
while(1)
{
if(*str1<*str2)
return -1;
else if(*str1>*str2)
return 1;
if(*str1=='\0')
return 0;
str1++;
str2++;
}
}

char *strcpy(char *dest, const char *src)
{
while(*src)
{
*dest=*src;
dest++;
src++;
}
*dest='\0';
return dest;
}

char *strcat(char *dest, const char *src)
{
while(*dest)
{
dest++;
}
while(*src)
{
*dest=*src;
src++;
dest++;
}
*dest='\0';
return dest;
}

int strlen(const char *src)
{
int len=0;
while(*src)
{
len++;
src++;
}
return len;
}

屏幕输出函数

现在我们来尝试一下实现printf函数
首先是函数声明

include/debug.h (部分)

1
2
3
4
5
6
7
8
#include "console.h"
#include "vargs.h"

// 内核的打印函数
void printk(const char *format, ...);

// 内核的打印函数带颜色
void printk_color(real_color_t back, real_color_t fore, const char *format, ...);

参数表的三个点代表的就是任意个数的实参,然后就是如何在没有形参名的情况下获取到实参
注意到上面的代码包含了另一个头文件,内容如下

includ/vargs.h

1
2
3
4
5
6
7
8
9
10
11
#ifndef INCLUDE_VARGS_H_
#define INCLUDE_VARGS_H_

typedef __builtin_va_list va_list;

#define va_start(ap, last) (__builtin_va_start(ap, last))
#define va_arg(ap, type) (__builtin_va_arg(ap, type))
#define va_end(ap)

#endif // INCLUDE_VARGS_H_

这个里面定义的一些宏是用于取得printk函数调用时的所有参数 ,__builtin_va_list这些是gcc内部实现的
当然也可以自己实现,一般定义为宏,如

1
2
3
4
5
6
#define va_list char*
#define __va_rounded_size(TYPE) (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
#define va_start(AP, LASTARG) (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#define va_arg(AP, TYPE) (AP += __va_rounded_size (TYPE),*((TYPE *) \
(AP - __va_rounded_size (TYPE))))
#define va_end(ap) ( ap = (va_list)0 )

来分析一下这些宏都干了什么,

  • va_list是一个char*指针
  • __va_rounded_size(TYPE)是字节对齐,在x86的机器上低于4字节会按照四字节对齐,大于4小于8会按照8字节对齐
  • va_start(AP, LASTARG) LASTARG是函数的最后一个固定参数,那么这个宏就是让AP指向可变参数的第一个参数
  • va_arg (AP,TYPE) 的作用就是在已知参数类型的情况下获取当前参数,然后让AP指向下一个参数
  • va_end(ap) 就是最后让ap指向0,结束
    这些在gcc内部都实现了,就不用我们自己管了
    当然这些成立的条件就是参数在内存中是顺序存储的,事实也就是这样,参数会被按顺序压入栈中
    弄明白这些就可以开始写printk函数了
    今天就先到这,明天再来写printk函数

本文标题:从零实现一个操作系统-day7

文章作者:

发布时间:2020年04月24日 - 00:04

最后更新:2020年04月25日 - 01:04

原始链接:http://startcraft.cn/post/9e1da38f.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------The End-------------