现在的位置: 首页 > 综合 > 正文

Linux内核中的printf的实现

2019年07月18日 ⁄ 综合 ⁄ 共 4734字 ⁄ 字号 评论关闭
本文转自http://www.cnblogs.com/chenglei/archive/2009/08/06/1540702.html

从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("%dbuffers=%dbytesbufferspace\n\r",NR_BUFFERS,
2 NR_BUFFERS*BLOCK_SIZE);
3 printf("Freemem:%dbytes\n\r",memory_end-main_memory_start);

先来分析第一个printf调用:

printf("%dbuffers=%dbytesbufferspace\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);

可以看到*fmt等于"%dbuffers=%dbytesbufferspace\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("%dbuffers=%dbytesbufferspace\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;
/*
flagstonumber()*/
10  
11 int field_width;
/*
widthofoutputfield*/
12 int precision;
/*
min.#ofdigitsforintegers;max
13 numberofcharsforfromstring*/
14 int qualifier;
/*
'h','l',or'L'forintegerfields*/
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; /*thisalsoskipsfirst'%'*/
//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
conversionqualifier*/
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(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++ = '%';
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; \

抱歉!评论已关闭.