字符串函数
因为在内核中,大部分的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
|
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
|
这个里面定义的一些宏是用于取得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函数