Linux內核中的printf的實現

本文轉自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; \

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章