嵌入式 Linux内核中的printf实现

2019-07-12 17:10发布

从main.c中的printf开始读这个函数。 首先看printf函数的定义: static int printf(const char *fmt, ...) {     va_list args;     int i;
    va_start(args, fmt);     write(1,printbuf,i=vsprintf(printbuf, fmt, args));     va_end(args);     return i; 10  }   参数中明显采用了可变参数的定义,而在main.c函数的后面直接调用了printf函数,我们可以看下printf函数的参数是如何使用的。 printf("%d buffers = %d bytes buffer space ",NR_BUFFERS,         NR_BUFFERS*BLOCK_SIZE); 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。 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(