我的博客startcraft
字符串函数
因为在内核中,大部分的c标准库函数无法使用,字符串操作的函数又比较常用,所有自己实现一些
include/string.h
#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
#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 (部分)
#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
#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内部实现的
当然也可以自己实现,一般定义为宏,如
#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函数