转自:http://www.cnblogs.com/chenglei/archive/2009/08/06/1540702.html

从main.c中的printf开始读这个函数。

首先看printf函数的定义:

static int printf(const char *fmt, ...)
{
    va_list args;
    int i;
 
    va_start(args, fmt);
,printbuf,i=vsprintf(printbuf, fmt, args));
    va_end(args);
    return i;
10  }

参数中明显采用了可变参数的定义,而在main.c函数的后面直接调用了printf函数,我们可以看下printf函数的参数是如何使用的。

printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
        NR_BUFFERS*BLOCK_SIZE);
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。

int vsprintf(char *buf, const char *fmt, va_list args)
{
    int len;
    int i;
    char * str;
    char *s;
    int *ip;
 
    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(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; \
}

Linux内核中的printf实现【转】的更多相关文章

  1. Linux内核中的printf实现

    1 #ifndef __PRINT_H_ 2 #define __PRINT_H_ 3 4 void print(char* fmt, ...); 5 void printch(char ch); 6 ...

  2. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  3. (十)Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  4. Linux内核中的常用宏container_of

    Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...

  5. linux内核中的C语言常规算法(前提:你的编译器要支持typeof和type)

    学过C语言的伙伴都知道,曾经比较两个数,输出最大或最小的一个,或者是比较三个数,输出最大或者最小的那个,又或是两个数交换,又或是绝对值等等,其实这些算法在linux内核中通通都有实现,以下的代码是我从 ...

  6. linux内核中的排序接口--sort函数

    linux内核中的sort函数,其实跟我们所说的qsort函数很像,我们来看看qsort: qsort 的函数原型是 void qsort(void*base,size_t num,size_t wi ...

  7. C语言在linux内核中do while(0)妙用之法

    为什么说do while(0) 妙?因为它的确就是妙,而且在linux内核中实现是相当的妙,我们来看看内核中的相关代码: #define db_error(fmt, ...) \ do { \ fpr ...

  8. Linux内核中的常用宏container_of其实很简单【转】

    转自:http://blog.csdn.net/npy_lp/article/details/7010752 开发平台:Ubuntu11.04 编 译器:gcc version 4.5.2 (Ubun ...

  9. Linux内核中的信号机制--一个简单的例子【转】

    本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123 ...

随机推荐

  1. classpath与clsspath*

    classpath是指 WEB-INF文件夹下的classes目录 classes含义: 1.存放各种资源配置文件 eg.init.properties log4j.properties struts ...

  2. Alpha 冲刺九

    团队成员 051601135 岳冠宇 051604103 陈思孝 031602629 刘意晗 031602248 郑智文 031602234 王淇 会议照片 项目燃尽图 项目进展 完善各自部分 项目描 ...

  3. HDU 2088 Box of Bricks

    http://acm.hdu.edu.cn/showproblem.php?pid=2088 Problem Description Little Bob likes playing with his ...

  4. centos 6.9安装mysql

    1.确认mysql是否已安装,有下面的代码可知 [root@cdh1 zjl]# yum list installed mysql* Loaded plugins: fastestmirror, re ...

  5. CORS跨域资源共享简述

    什么是CORS? 默认情况下,为预防某些而已行为,浏览器的XHR对象只能访问来源于同一个域中的资源.但是我们在日常实际开发中,常常会遇到跨域请求的需求,因此就出现了一种跨域请求的方案:CORS(Cro ...

  6. java 堆和栈二

    1.数组 整数默认初始化值0 浮点数默认初始化值0.0 布尔类型默认初始化值false 字符类型默认初始化值\u0000 [I@7852e922 [有几个代表是几维数组 I代表是Int类型 @固定格式 ...

  7. 一本通1649【例 2】2^k 进制数

    1649:[例 2]2^k 进制数 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:NOIP 2006 提高组 设 r 是个 2k 进制数,并满足以 ...

  8. 一本通1630SuperGCD

    1630:SuperGCD 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 来源:SDOI 2009 Sheng Bill 有着惊人的心算能力,甚至能用大脑计 ...

  9. 词法作用域 vs 动态作用域

    词法作用域 vs 动态作用域 链接:https://www.jianshu.com/p/cdebb5965000 scheme是一门采用词法作用域(lexical scoping)的lisp方言,这个 ...

  10. Python 线程同步

    #-*-coding:utf-8-*- '''如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性, 需要对多个线程进行同步. 线程同步所使用的的方法: Lock RLock ...