Linux内核中的printf实现

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux内核中的printf实现
从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\n\r",NR_BUFFERS,
2 NR_BUFFERS*BLOCK_SIZE);
3 printf("Free mem: %d bytes\n\r",memory_end-main_memory_start);
先来分析第⼀个printf调⽤:
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, NR_BUFFERS*BLOCK_SIZE);
可以看到*fmt等于"%d buffers = %d bytes buffer space\n\r”,是⼀个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\n\r",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
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++ = ' ';
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(args, char *);
80 len = strlen(s);
81 if (precision < 0)
82 precision = len;
83 else if (len > precision)
84 len = precision;
85
86 if (!(flags & LEFT))
87 while (len < field_width--)
88 *str++ = ' ';
89 for (i = 0; i < len; ++i)
90 *str++ = *s++;
91 while (len < field_width--)
92 *str++ = ' ';
93 break;
94
95 case'o':
96 str = number(str, va_arg(args, unsigned long), 8,
97 field_width, precision, flags);
98 break;
99
100 case'p':
101 if (field_width == -1) {
102 field_width = 8;
103 flags |= ZEROPAD;
104 }
105 str = number(str,
106 (unsigned long) va_arg(args, void *), 16,
107 field_width, precision, flags);
108 break;
109
110 case'x':
111 flags |= SMALL;
112 case'X':
113 str = number(str, va_arg(args, unsigned long), 16,
114 field_width, precision, flags);
115 break;
116
117 case'd': //如果是整型,⾸先设定flags,然后利⽤number函数将可变参数取出,并根据base(这⾥是10)然后转换成字符串,赋给str
118 case'i':
119 flags |= SIGN;
120 case'u':
121 str = number(str, va_arg(args, unsigned long), 10,
122 field_width, precision, flags);
123 break;
124
125 case'n':
126 ip = va_arg(args, int *);
127 *ip = (str - buf);
128 break;
129
130 default:
131 if (*fmt != '%')//如果格式转换符不是%,则表⽰出错,直接打印⼀个%。

如果是%,那么格式转换符就是%%,就由下⾯if(*fmt)只输出⼀个%
132 *str++ = '%';
132 *str++ = '%';
133 if (*fmt)
134 *str++ = *fmt;//如果格式转换符不正确则输出%+不正确的格式转换符。

如果是%%,则只输出⼀个%
135 else
136 --fmt;//如果转换格式符不是上⾯这些正确的,也不是空,那么直接输出,并返回到判断fmt的for语句;否则就指向末尾了,fmt 后退⼀位,这样在for循环⾃动再加1进⾏判断时*fmt的条件就不满⾜,退出for循环
137 break;
138 }
139 }
140 *str = '\0';//设定str字符串的最后⼀位为'\0'
141 return str-buf;//返回值为字符串的长度
142
这样我们就实现了根据fmt中的格式转换符将可变参数转换到相应的格式,利⽤write函数进⾏输出的⽬的。

⽽后者的可变参数memory_end-main_memory_start,根据main.c中的定义
static long buffer_memory_end = 0;
static long main_memory_start = 0;可见为主内存的⼤⼩,类型为long。

分析同上
⽽write函数跟fork函数⼀样是由_syscall*来实现的,内嵌汇编就不多解释了,直接展开就⾏
write.c
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
unistd.h
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}。

相关文档
最新文档