嵌入式 Linux内核中的printf实现
2019-07-12 17:10发布
生成海报
从main.c中的printf开始读这个函数。
首先看printf函数的定义:
1
static int printf(const char *fmt, ...)
2
{
3
va_list args;
4
int i;
5
6
va_start(args, fmt);
7
write(1,printbuf,i=vsprintf(printbuf, fmt, args));
8
va_end(args);
9
return i;
10
}
参数中明显采用了可变参数的定义,而在main.c函数的后面直接调用了printf函数,我们可以看下printf函数的参数是如何使用的。
1
printf("%d buffers = %d bytes buffer space
",NR_BUFFERS,
2
NR_BUFFERS*BLOCK_SIZE);
3
printf("Free mem: %d bytes
",memory_end-main_memory_start);
先来分析第一个printf调用:
printf("%d buffers = %d bytes buffer space
",NR_BUFFERS, NR_BUFFERS*BLOCK_SIZE);
可以看到*fmt等于"%d buffers = %d bytes buffer space
”,是一个char
类型的指针,指向字符串的启始位置。而可变的参数在这里是NR_BUFFERS和NR_BUFFERS*BLOCK_SIZE。
其中NR_BUFFERS在buffer.c中定义为缓冲区的页面大小,类型为int;BLOCK_SIZE在fs.h中的定义为
#define BLOCK_SIZE 1024
因此两个可变参数NR_BUFFERS和NR_BUFFERS*BLOCK_SIZE都为int类型;
以前已经分析过可变参数的一系列实现函数va函数。
va_list arg_ptr;
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针。然后使用va_start使arg_ptr指针指向prev_param的下一位,然后使用va_args取出从arg_ptr开始的type类型长度的数据,并返回这个数据,最后使用va_end结束可变参数的获取。
在printf("%d buffers = %d bytes buffer space
",NR_BUFFERS, NR_BUFFERS*BLOCK_SIZE)中,根据以上的分析fmt指向字符串,args首先指向第一个可变参数,也就是NR_BUFFERS(args在经过一次type
va_arg( va_list arg_ptr, type )调用后,会根据type的长度自动增加,从而指向第二个可变参数NR_BUFFERS*BLOCK_SIZE)。
我们先不管write函数的实现,首先来看vsprint。
1
int vsprintf(char *buf, const char *fmt, va_list args)
2
{
3
int len;
4
int i;
5
char * str;
6
char *s;
7
int *ip;
8
9
int flags; /* flags to number() */
10
11
int field_width; /* width of output field */
12
int precision; /* min. # of digits for integers; max
13
number of chars for from string */
14
int qualifier; /* 'h', 'l', or 'L' for integer fields */
15
16
for (str=buf ; *fmt ; ++fmt) { //str为最终存放字符串的位置但是他随着字符串增长而增长,buf始终指向最终字符串的启始位置。fmt为格式字符串
17
if (*fmt != '%') {
18
*str++ = *fmt; //如果不是%则表示这是需要原样打印的字符串,直接复制即可
19
continue;
20
}
21
22
/* process flags */
23
flags = 0;
24
repeat:
25
++fmt; /* this also skips first '%' */
//fmt指向%的后一位
26
switch (*fmt) {
27
case '-': flags |= LEFT; goto repeat;
28
case '+': flags |= PLUS; goto repeat;
29
case ' ': flags |= SPACE; goto repeat; //判断标志位,并设置flags
30
case '#': flags |= SPECIAL; goto repeat;
31
case '0': flags |= ZEROPAD; goto repeat;
32
}
33
34
/* get field width */
35
field_width = -1;
36
if (is_digit(*fmt))
37
field_width = skip_atoi(&fmt);
38
else if (*fmt == '*') {
39
/* it's the next argument */
40
field_width = va_arg(args, int);
41
if (field_width < 0) {
42
field_width = -field_width;
43
flags |= LEFT;
44
}
45
}
46
47
/* get the precision */
48
precision = -1;
49
if (*fmt == '.') {
50
++fmt;
51
if (is_digit(*fmt))
52
precision = skip_atoi(&fmt);
53
else if (*fmt == '*') {
54
/* it's the next argument */
55
precision = va_arg(args, int);
56
}
57
if (precision < 0)
58
precision = 0;
59
}
60
61
/* get the conversion qualifier */
62
qualifier = -1;
63
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
64
qualifier = *fmt;
65
++fmt;
66
}
67
68
switch (*fmt) {
//如果没有上面奇怪的标志位(*/./h/l/L)则fmt仍然指向%的后一位,下面判断这个标志位
69
case 'c':
70
if (!(flags & LEFT))
71
while (--field_width > 0)
72
*str++ = ' ';
73
*str++ = (unsigned char) va_arg(args, int);
74
while (--field_width > 0)
75
*str++ = ' ';
76
break;
77
78
case 's':
79
s = va_arg(
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮