库函数的实现

梗概
这一节新增的文件有:
stdarg.h 用来处理可变参数
ctype.h 判断变量一些属性。比如一个字符是不是大写,是不是数字
string.h string.c 常用的关于字符串的操作
vsprintf.h vsprintf.c 该节的主要文件用来实现格式化输出
改变的文件有:
test.c 主要用来测试 printk函数


正文

大家上程序设计课程写的第一个程序应该是hello world吧,它仅仅是用printf函数打印一条问候语句,是的,非常简单。但是想在内核中用printf可就没那么容易了,因为内核程序根本就不能使用C库函数,但是printf函数却又非常重要。怎么办?大家应该还记得上一节写的vga驱动,用它直接打印不就得了。醒醒吧,printf可没那么简单,它不仅要能打印字符串,还要控制格式,就像这样: printf("Tinux is %d years old.", 2)。cons_write函数可不会把%d替换成数字2。看来还是要自己动手,才能丰衣足食了。

本节目标就是实现printf函数,不过我给它取了另一个名字叫printk,以表明它是用在内核当中的printf函数。另外还会有些附带的函数产品,像strcpy,memcpy等字符串操作函数,sprintf,vsprintf等底层打印函数。printk函数就是靠vsprintf实现的。

可变参数的头文件 stdarg.h
请拿出你的C语言参考书,看一下printf函数的声明: int printf(const char *format, ...);
是的,这就是传说中的变长参数。我在这里只提供变长参数的大概知识,详情请看变长参数的实现。变长参数涉及一个自定义的数据类型va_list,和三个宏定义va_start, va_arg, va_end。他们都定义在头文件stdarg.h中,我是直接通过gcc内置宏定义的,与链接给出的文章不同,因为那篇文章是为了讲解变长参数,真正实现时用gcc内置宏就可以了,相当于gcc内置宏为我们做好了定义,这样正确率更高,要知道调试OS Kernel可是相当烦。
stdarg.h
1 #ifndef __STDARG_H__
2 #define __STDARG_H__
3
4 #define va_list __builtin_va_list
5 #define va_start(ap, last) __builtin_va_start (ap, last)
6 #define va_arg(ap, type) __builtin_va_arg (ap, type)
7 #define va_end(ap) __builtin_va_end (ap)
8
9 #endif
10

C类型头文件 ctype.h
里面都是些常用宏,比如 islower(c)判断c是不是小写字母,isdigit(c)判断c是不是十进制数码,isxdigit(c)判断c是不是十六进制数码
isspace(c)判断c是不是空白字符
ctype.h
1 #ifndef __CTYPE_H__
2 #define __CTYPE_H__
3
4 #define islower(c) (((c) >= 'a') && ((c) <= 'z'))
5 #define isupper(c) (((c) >= 'A') && ((c) <= 'Z'))
6 #define isalpha(c) (islower (c) || isupper (c))
7 #define isdigit(c) (((c) >= '0') && ((c) <= '9'))
8 #define isalnum(c) (isalpha (c) || isdigit (c))
9 #define ispunct(c) \
10 ((((c) >= 0x21) && ((c) <= 0x2f)) || \
11 (((c) >= 0x3a) && ((c) <= 0x40)) || \
12 (((c) >= 0x5b) && ((c) <= 0x60)) || \
13 (((c) >= 0x7b) && ((c) <= 0x7e)))
14 #define isgraph(c) (((c) >= 0x21) && ((c) <= 0x7e))
15 #define isspace(c) ((((c) >= 0x09) && ((c) <= 0x0d)) || ((c) == ' '))
16 #define isprint(c) (((c) >= 0x20) && ((c) <= 0x7e))
17 #define iscntrl(c) (((c) <= 0x1f) || ((c) == 0x7f))
18 #define isxdigit(c) \
19 (isdigit (c) || (((c) >= 'a') && ((c) <= 'f')) || \
20 (((c) >= 'A') && ((c) <= 'F')))
21
22 #define tolower(c) (isupper (c) ? (c)+'a'-'A' : (c))
23 #define toupper(c) (islower (c) ? (c)+'A'-'a' : (c))
24
25 #endif
26

字符串处理 string.h string.c
关于 memcpy函数和mememove函数的区别请自行Google
string.h
1 #ifndef __STRING_H__
2 #define __STRING_H__
3
4 #include "types.h"
5
6 void *memcpy (void *dest, const void *src, size_t n);
7 void *memmove (void *dest, const void *src, size_t n);
8 char *strcpy (char *dest, const char *src);
9 char *strncpy (char *dest, const char *src, size_t n);
10 int strcmp (const char *s1, const char *s2);
11 int strncmp (const char *s1, const char *s2, size_t n);
12 int strlen (const char *s);
13
14 #endif

string.c
1 #include "types.h"
2 #include "string.h"
3
4 void *memcpy (void *dest, const void *src, size_t n) {
5 int i;
6 for (i = 0; i < (int)n; i++) *(char *)(dest + i) = *(char *)(src + i);
7 return dest;
8 }
9
10 void *memmove (void *dest, const void *src, size_t n) {
11 int i;
12 if (src > dest)
13 for (i = 0; i < (int)n; i++)
14 *(char *)(dest + i) = *(char *)(src + i);
15 if (src < dest)
16 for (i = (int)(n - 1); i >= 0; i--)
17 *(char *)(dest + i) = *(char *)(src + i);
18 return dest;
19 }
20
21 char *strcpy (char *dest, const char *src) {
22 int i = 0;
23 do { dest[i] = src[i]; } while (src[i++] != 0);
24 return dest;
25 }
26
27 char *strncpy (char *dest, const char *src, size_t n) {
28 int i = 0;
29 while ((i < (int)n) && (src[i] != 0)) { dest[i] = src[i]; i++; }
30 while (i < (int)n) dest[i++] = 0;
31 return dest;
32 }
33
34 int strcmp (const char *s1, const char *s2) {
35 int i = 0;
36 while ((s1[i] == s2[i]) && (s1[i] != 0)) i++;
37 return s1[i] - s2[i];
38 }
39
40 int strncmp (const char *s1, const char *s2, size_t n) {
41 int i = 0;
42 while ((i < (int)n) && (s1[i] == s2[i]) && (s1[i] != 0)) i++;
43 return (i == (int)n) ? 0 : s1[i] - s2[i];
44 }
45
46 int strlen (const char *s) {
47 int i = 0;
48 while (s[i] != 0) i++;
49 return i;
50 }
51

打印函数 vsprintf.h vsprintf.c
vsprintf.h
1 #ifndef __VSPRINTF_H__
2 #define __VSPRINTF_H__
3
4 #include "stdarg.h"
5
6 int sprintf(char *str, const char *format, ); // 将输出重定向到缓冲区 str
7 int vsprintf (char *str, const char *format, va_list ap); // 这是一个中转函数,真正完成格式化功能
8 int printk(const char *format, ); // 和 printf一样
9
10 #endif

vsprintf.c
1 #include "types.h"
2 #include "ctype.h"
3 #include "console.h"
4 #include "stdarg.h"
5 #include "string.h"
6 #include "vsprintf.h"
7
8 /* flags */
9 #define LEFT 0x01
10 #define PLUS 0x02
11 #define SPACE 0x04
12 #define SPECIAL 0x08
13 #define ZERO 0x10
14 #define SIGN 0x20 /* signed if set */
15 #define SMALL 0x40 /* 'abcdef' if set, 'ABCDEF' otherwise */
16
17 int32_t get_wide (const char **s);
18 void number_to_string (long num, int32_t base, int32_t flags, int32_t wide, int32_t precision, char **s);
19
20 int sprintf(char *str, const char *format,) {
21 va_list args;
22 int32_t res;
23 va_start (args, format);
24 res = vsprintf(str, format, args);
25 va_end (args);
26 return res;
27 }
28
29 int32_t vsprintf (char *str, const char *format, va_list ap) {
30 char c;
31 char *start = str;
32 int32_t flags;
33 int32_t wide;
34 int32_t precision;
35 int32_t qualifier;
36 char *s;
37 int32_t i, len, base;
38
39 while ((c = *format++) != 0) {
40 if (c != '%') { *str++ = c; continue; }
41 if (*format == '%') { *str++ = '%'; format++; continue; }
42
43 /* get flags */
44 flags = 0;
45 while (1) {
46 if (*format == '-') { flags |= LEFT; format++; continue; }
47 if (*format == '+') { flags |= PLUS; format++; continue; }
48 if (*format == ' ') { flags |= SPACE; format++; continue; }
49 if (*format == '#') { flags |= SPECIAL; format++; continue; }
50 if (*format == '0') { flags |= ZERO ; format++; continue; }
51 break;
52 }
53
54 /* get wide */
55 wide = -1;
56 if (isdigit (*format)) wide = get_wide ((const char **)(&format));
57 else if (*format == '*') { wide = va_arg (ap, int32_t); format++; }
58
59 /* get precision */
60 precision = -1;
61 if (*format == '.') {
62 format++;
63 if (isdigit (*format))
64 precision = get_wide ((const char **)(&format));
65 else if (*format == '*') {
66 precision = va_arg (ap, int32_t);
67 format++;
68 }
69 else precision = 0;
70 }
71
72 /* get qualifier */
73 qualifier = -1;
74 if ((*format == 'h') || (*format == 'l')) qualifier = *format++;
75
76 /* get format */
77 switch (*format++) {
78 case 'i':
79 case 'd':
80 flags |= SIGN;
81 if (precision != -1) flags &= ~ZERO;
82 switch (qualifier) {
83 case 'h':
84 number_to_string ((short) va_arg (ap, int32_t), 10, flags,
85 wide, precision, &str);
86 break;
87 case 'l':
88 number_to_string (va_arg (ap, long), 10, flags,
89 wide, precision, &str);
90 break;
91 default:
92 number_to_string (va_arg (ap, int32_t), 10, flags,
93 wide, precision, &str);
94 break;
95 }
96 break;
97
98 case 'u':
99 base = 10;
100 goto num_to_str_without_sign;
101
102 case 'o':
103 base = 8;
104 goto num_to_str_without_sign;
105
106 case 'x':
107 flags |= SMALL;
108 case 'X':
109 base = 16;
110
111 num_to_str_without_sign:
112 flags &= (~PLUS & ~SPACE);
113 if (precision != -1) flags &= ~ZERO;
114 switch (qualifier) {
115 case 'h':
116 number_to_string ((unsigned short) va_arg (ap, int32_t), \
117 base, flags, wide, precision, &str);
118 break;
119 case 'l':
120 number_to_string ((unsigned long) va_arg (ap, long), \
121 base, flags, wide, precision, &str);
122 break;
123 default:
124 number_to_string((uint32_t)va_arg (ap, int32_t), \
125 base, flags, wide, precision, &str);
126 break;
127 }
128 break;
129
130 case 's':
131 s = va_arg (ap, char *);
132 len = strlen (s);
133 if ((precision >= 0) && (len > precision)) len = precision;
134
135 /* rigth justified : pad with spaces */
136 if (!(flags & LEFT)) while (len < wide--) *str++ = ' ';
137 for (i = 0; i < len; i++) *str++ = *s++;
138 /* left justified : pad with spaces */
139 while (len < wide--) *str++ = ' ';
140 break;
141
142 case 'c':
143 /* rigth justified : pad with spaces */
144 if (!(flags & LEFT)) while (1 < wide--) *str++ = ' ';
145 *str++ = (unsigned char) va_arg (ap, int32_t);
146 /* left justified : pad with spaces */
147 while (1 < wide--) *str++ = ' ';
148 break;
149
150 default:
151 return -1;
152 }
153 }
154 *str = 0;
155
156 return (int32_t)(str-start);
157 }
158
159 int32_t printk(const char *format, )
160 {
161 char buff[1024];
162 char *str = buff;
163 va_list args;
164 int32_t res;
165 va_start (args, format);
166 res = vsprintf (str, format, args);
167 va_end (args);
168 cons_write(buff);
169 return res;
170 }
171
172 int32_t get_wide (const char **s) {
173 int32_t res = 0;
174 while (isdigit (**s)) res = 10*res + *((*s)++) - '0';
175 return res;
176 }
177
178 #define LONG_STRSIZE_BASE_2 32
179
180 void number_to_string (long num, int32_t base, int32_t flags, int32_t wide, int32_t precision, char **s) {
181 char sign; /* sign printed : '+', '-', ' ', or 0 (no sign) */
182 int32_t num_cpy = num;
183 unsigned long ul_num = (unsigned long) num; /* for unsigned format */
184
185 /* string representation of num (reversed) */
186 char tmp[LONG_STRSIZE_BASE_2];
187 int32_t i = 0; /* number of figures in tmp */
188
189 const char *digits = "0123456789ABCDEF";
190 if (flags & SMALL) digits = "0123456789abcdef";
191
192 if ((base < 2) || (base > 16)) return;
193
194 if ((flags & SIGN) && (num < 0)) { sign = '-'; num = -num; }
195 else sign = (flags & PLUS) ? '+' : ((flags & SPACE) ? ' ' : 0);
196 if (sign) wide--;
197
198 if (flags & SPECIAL) {
199 if ((base == 16) && (num != 0)) wide -= 2; /* '0x' or '0X' */
200 if (base == 8) { wide--; precision--; } /* '0' */
201 }
202
203 if (num == 0) tmp[i++] = '0';
204 /* signed format */
205 if (flags & SIGN) {
206 while (num != 0) {
207 tmp[i++] = digits[num % base];
208 num = num / base;
209 }
210 }
211 /* unsigned format */
212 else {
213 while (ul_num != 0) {
214 tmp[i++] = digits[ul_num % base];
215 ul_num = ul_num / base;
216 }
217 }
218
219 if (i > precision) precision = i;
220 wide -= precision;
221
222 /* wide = number of padding chars */
223 /* precision = number of figures after the sign and the special chars */
224
225 /* right justified and no zeropad : pad with spaces */
226 if (!(flags & (LEFT + ZERO))) while (wide-- > 0) *((*s)++) = ' ';
227
228 if (sign) *((*s)++) = sign;
229 if ((flags & SPECIAL) && (num_cpy != 0)) {
230 if (base == 8) *((*s)++) = '0';
231 if (base == 16) {
232 *((*s)++) = '0';
233 if (flags & SMALL) *((*s)++) = 'x';
234 else *((*s)++) = 'X';
235 }
236 }
237
238 /* rigth justified and zeropad : pad with 0 */
239 if (!(flags & LEFT)) while (wide-- > 0) *((*s)++) = '0';
240
241 /* print num */
242 while (i < precision--) *((*s)++) = '0';
243 while (i-- > 0) *((*s)++) = tmp[i];
244
245 /* left justfied : pad with spaces */
246 while (wide-- > 0) *((*s)++) = ' ';
247 }
248

这一节的程序和上节的一样,起基础作用,并没有关系到进程,内存管理等OS核心理论。但是它们的正确与否至关重要,这也是OS研究的繁琐之处,想要一探它的奥秘还必须先做一些杂七杂八的玩意儿。

测试程序 test.c
让该程序做一些算术题目,并格式化输出结果

test.c
1 #include "console.h"
2 #include "vsprintf.h"
3
4 int test(void)
5 {
6 int a = 32;
7 int b = 68;
8 cons_clear();
9 printk("\n\n");
10 printk(" November 8, 2009\n");
11 printk("Hi,John\n\n");
12 printk("Now, I am capable of caculating math expressions,\n");
13 printk("and what's more, showing the results to our friends.\n");
14 printk("Look!\n\n");
15 printk(" %d + %d = %d \n",a, b, a+b);
16 printk(" 0x%x + 0x%x = 0x%x \n",a,b,a+b);
17 printk(" Your Tinux\n");
18 return 0;
19 }


运行结果如下:



PS: 此节的程序几乎全部来自CROCOS, 这是个开源OS项目, 它的新颖之处在于OS开发方式, 先像开发用户程序一样开发OS组件,最后再移到真实的硬件环境中。
若有兴趣,请访问 http://crocos.sourceforge.net/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值