编写自己的内核打印函数

原创文章,转载写明出处

作者Crazii @ CSDN

我看了minix的内核printf函数之后,发现它里面的goto有语句...我就读不下去了,于是自己写了一个.kprintf

首先要了解的就是printf的变参.这得从c调用规则讲起.回顾一下c(cdecl)调用规则(vc里面__cdecl关键字,MASM里面的.model c)除了命名要加下划线之外,还有函数的调用:参数是从右至左入栈,并且是调用者恢复栈.

;printf(fmt,s);假定fmt和s是指针变量

push s

push fmt

call _printf

add esp 8

嗯,这里还有一个问题,就是栈对齐问题.如果是char型参数,或者是short型参数,那么压栈的数据宽度是多少?16位下我们知道,最小得是一个word,也就是short型,char型数据入栈时,也是按word.而32位下,压栈的默认大小跟段描述符里面的描述有关,如果段是16位段,则按16位入栈,如果是32位段,则按32位入栈.这里当然要说32位的栈了,push 0xFF,这样的立即数是按32位对齐,push ds这样的16位段寄存器也是按32位对齐.好了.ok到这里,感觉离题有点远了..

 

正因为cdecl调用跟stdcall不同,它是调用者负责恢复栈的,所以调用者想压几个参数就压几个参数,调用完了自己在平衡堆栈就OK了,所以可以支持变参,但是stdcall是被调用函数在结束时ret n来维护栈的,调用者不能随便压参数.总之,cdecl支持可变参数vararg,所以才有现在的printf

嗯,根据C调用规则,就知道了栈上的参数分布,这样在读取可变参数时,就好办了.

  1. /******************************************************
  2. stdarg.h
  3. standard argument header for NGOS
  4. *******************************************************
  5. C语言参数头文件
  6. Author: ZZU-Crazii
  7. */
  8. typedef char    *var_list;  //VC里面也是这样写的,跟我想的一样
  9. /*  参考VC里的头文件,栈的对齐,是sizeof(int)的整数倍  */
  10. #define INT_SIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
  11. /* 按对齐,取下一个参数 */
  12. #define var_start(arg_ptr,arg_list) (  (void) (arg_ptr = (var_list)(&arg_list)+INT_SIZEOF(arg_list)) )
  13. /* 将取出的参数强制转换为指定的Type,并指向下一个参数 */
  14. #define var_arg(arg_ptr,type)   (*(type*)   ( (arg_ptr +=INT_SIZEOF(type))-INT_SIZEOF(type) )   )
  15. #define var_end(arg_ptr) ((void)(arg_ptr=NULL)

 

需要说明的是,我自己写的stdarg之前的版本,栈取参数后的增量是++操作(+sizeof(type)),那么如果取出的类型是char,则varptr是+1的,这显然不符合栈对齐原则.应该将sizeof(type)对齐到INT长度的倍数,就像VC的crt头文件里面一样.这样,如果取出的参数是char,那么varptr将+4,来指向下一个参数好了,可以看kprintf了:

  1. //这个宏来自minix
  2. #define isdigit(c)  ( (unsigned)( (unsigned)(c) - '0') <= (unsigned)9 )
  3. #define BUFFER_SIZE 32  /* 将数字转化为字符串的缓冲 */
  4. /*****************kprintf*************************/
  5. #include <stdarg.h> /* 自己做的头文件 */
  6. /* 支持%s, %d,%u 的简单版*/
  7. /* 3.26,添加%x,长度,对齐.等 */
  8. /* 3.26 稍作修改,添加%X */
  9. void kprintf(const char *fmt,...)
  10. {
  11.     var_list    argp;
  12.     int c;
  13.     /*const char *pString;*/
  14.     const char *Ostring;
  15.     union
  16.     {
  17.         int     s;  /* 有符号 */
  18.         unsigned    uns;    /* 无符号 */
  19.     }num;
  20.     int i;
  21.     unsigned int len;   
  22.     static char tmp[BUFFER_SIZE];
  23.     static const char null_pointer[] ="(NULL)";
  24.     
  25.     int width;      /* 指定显示长度 */
  26.     unsigned int limit; /* 截断显示长度 */
  27.     int align_left;
  28.     char    fill_prefix;
  29.     
  30.     if( fmt == NULL ) return;
  31.     var_start(argp,fmt);
  32.     
  33.     while( (c=*(fmt++)) !=NULL )
  34.     {
  35.         if( c!='%' )
  36.         {
  37.             kputchar(c);
  38.             continue;
  39.         }
  40.         num.uns = 0;
  41.         Ostring = NULL;
  42.         
  43.         c = *(fmt++);       /* 跳过 '%' */
  44.         
  45.         /* 对齐方式 */
  46.         align_left = FALSE;
  47.         if( c == '-' )
  48.         {
  49.             align_left = TRUE;
  50.             c = *(fmt++);
  51.         }
  52.         
  53.         /* '0'前缀 */
  54.         fill_prefix =' ';
  55.         if( c == '0')
  56.         {
  57.             fill_prefix = '0';
  58.             c = *(fmt++);
  59.         }
  60.         
  61.         /* 宽度 */
  62.         width = 0;
  63.         if( c == '*' )
  64.         {
  65.             width = var_arg(argp,int);
  66.             c = *(fmt++);
  67.         }
  68.         else while ( isdigit(c) )
  69.         {
  70.             width= width * 10 + (c - '0');
  71.             c = *(fmt++);
  72.         }
  73.         
  74.         /* 截断 */ /* 修复BUG,limit=0,串不能显示 */
  75.         limit = ULONG_MAX;
  76.         if( c == '.' )
  77.         {
  78.             c = *(fmt++);
  79.             if( isdigit(c) )
  80.             {
  81.                 limit = 0;
  82.                 while( isdigit(c) )
  83.                 {
  84.                     limit= limit * 10 + (c - '0');
  85.                     c = *(fmt++);
  86.                 }
  87.             }
  88.         }
  89.         
  90.         switch(c)
  91.         {
  92.             case 's':   /* %s */
  93.                 Ostring=var_arg(argp,char*);
  94.                 if( Ostring == NULL )
  95.                     Ostring=null_pointer;
  96.                 
  97.                 /* 计算字符串长度 */
  98.                 for( i=0; Ostring[i]!=0; i++);
  99.                 len = i;
  100.                 break;
  101.             case 'd':   /* %d */
  102.                 num.s=var_arg(argp,int);
  103.                 
  104.                 if(num.s==0)
  105.                 {
  106.                     tmp[0] ='0';
  107.                     Ostring = tmp;
  108.                     len =1;
  109.                     break;
  110.                 }
  111.                 if( num.s<0 )       /* 将负数转换成正数 */
  112.                 {
  113.                     kputchar('-');
  114.                     num.s = -num.s;
  115.                 }
  116.                 /* !!!没有break,继续,作为无符号数处理 */
  117.             case 'u':   /* %u */
  118.                 if( num.uns == 0 ) num.uns = var_arg(argp,unsigned int);
  119.                 if(num.uns==0)
  120.                 {
  121.                     tmp[0] ='0';
  122.                     Ostring = tmp;
  123.                     len =1;
  124.                     break;
  125.                 }
  126.                 /* 3.26 改成从高字节(BUFFER_SIZE-1)开始写入字符 */
  127.                 for(i=0; num.uns/10 !=0 || num.uns%10 !=0;num.uns/=10,i++)
  128.                     tmp[BUFFER_SIZE-1-i] = (char)(num.uns%10) + '0';
  129.                 
  130.                 /****************************
  131.                 字符串: H=结束字节,第一个写入,
  132.                     L=开始字节,最后一个被写入,
  133.                     B=缓冲区开始位置
  134.                     |<-长度i->|
  135.                 |B|.....|L|.....|H|
  136.                      ^
  137.                      |
  138.                     (BUFFER_SIZE-i)索引的最终位置
  139.                 *****************************/
  140.                 Ostring = tmp+ (BUFFER_SIZE - i);
  141.                 len = i;
  142.                 /* 数字不做截断处理 */
  143.                 limit = ULONG_MAX;
  144.                 break;
  145.             case 'x'case'X':
  146.                 num.uns = var_arg(argp,unsigned int);
  147.                 if(num.uns==0)
  148.                 {
  149.                     tmp[0] ='0';
  150.                     Ostring = tmp;
  151.                     len =1;
  152.                     break;
  153.                 }
  154.                 for(i=0; num.uns/16 !=0 || num.uns%16 !=0;num.uns/=16,i++)
  155.                 {
  156.                     if( (num.uns%16) < 10 )
  157.                         tmp[BUFFER_SIZE-1-i] = (char)(num.uns%16) + '0';
  158.                     else        /* 'x'-'a' == 'X'-'a' 嘎嘎~ */
  159.                         tmp[BUFFER_SIZE-1-i] = (char)((num.uns%16)-10)+c-('x'-'a');
  160.                 }
  161.                 Ostring = tmp+ (BUFFER_SIZE - i);
  162.                 len = i;
  163.                 /* 数字不做截断处理 */
  164.                 limit = ULONG_MAX;
  165.                 break;
  166.             default:
  167.                 kputchar('%');
  168.                 kputchar(c);
  169.                 break;
  170.         }
  171.         /* 问题:数字缓冲是倒序,但是字符串是正序排列... */
  172.         /* 把数字缓冲改成正序.OK */
  173.         if( Ostring != NULL )
  174.         {
  175.             /* 计算对齐并输出 */
  176.             if( !align_left && len< width )
  177.             {
  178.                 do
  179.                 {
  180.                     kputchar(fill_prefix);
  181.                     width--;
  182.                 }while( len<width );
  183.             }
  184.             
  185.             /* 截断处理 */
  186.             if( len > limit )
  187.                 len = limit;
  188.                 
  189.             for(i=0;i<len;i++)
  190.                 kputchar( Ostring[i] );
  191.                 
  192.             if( align_left && len< width )
  193.             {
  194.                 do
  195.                 {
  196.                     kputchar(' ');
  197.                     width--;
  198.                 }while( len<width );
  199.             }
  200.         }
  201.     }
  202.     var_end(argp);
  203.     kputchar( NULL );        /* 释放缓冲,显示 */        
  204. }


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值