weiyuhu 发表于 2013-1-15 02:44:31

库函数的实现

梗概
这一节新增的文件有:
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 = src; } while (src != 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 != 0)) { dest = src; i++; }
30   while (i < (int)n) dest = 0;
31   return dest;
32 }
33
34 int strcmp (const char *s1, const char *s2) {
35   int i = 0;
36   while ((s1 == s2) && (s1 != 0)) i++;
37   return s1 - s2;
38 }
39
40 int strncmp (const char *s1, const char *s2, size_t n) {
41   int i = 0;
42   while ((i < (int)n) && (s1 == s2) && (s1 != 0)) i++;
43   return (i == (int)n) ? 0 : s1 - s2;
44 }
45
46 int strlen (const char *s) {
47   int i = 0;
48   while (s != 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;
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;
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 ==http://www.agoit.com/images/smiles/icon_cool.gif { wide--; precision--; }   /* '0' */
201   }
202   
203   if (num == 0) tmp = '0';
204   /* signed format */
205   if (flags & SIGN) {
206         while (num != 0) {
207             tmp = digits;
208             num = num / base;
209         }
210   }
211   /* unsigned format */
212   else {
213         while (ul_num != 0) {
214             tmp = digits;
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 ==http://www.agoit.com/images/smiles/icon_cool.gif *((*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;
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/
页: [1]
查看完整版本: 库函数的实现