函数1、Linux下的打印函数printk

来源:互联网 发布:大数据 微盘 编辑:程序博客网 时间:2024/06/01 10:23

        从今天开始好好看看Linux的内核代码,听说学习代码最好的老师就是看内核中的代码,学习内核中代码的编写习惯,我打算整理一些学习笔记来督促自己学习,可恨自己太懒,呵呵呵。只打算学习内核代码,所以从源码的start_kernel函数看起,遇见不错的函数就记录两笔。至于详尽的函数代码分析这里不是重点,这里我们只分析函数用法中的亮点。

第一个函数:printk

 源码:重点分析多参数函数用法。

asmlinkage int printk(const char *fmt, ...)
{
va_list args;    //分析1
unsigned long flags;
int printed_len;
char *p;
static char printk_buf[1024];
static int log_level_unknown = 1;


if (unlikely(oops_in_progress))//oops_in_progress指示进程发生错误
zap_locks();


/* This stops the holder of console_sem just where we want him */
spin_lock_irqsave(&logbuf_lock, flags);//一般spin_lock在单cpu中无效的,所以spin_lock_irqsave真正的作用是关中断 和保存状态寄存器。


/* Emit the output into the temporary buffer */
va_start(args, fmt);//分析2
printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);分析3
va_end(args);//分析4

      ........省略一大段代码.

}

/*********************************************分析可变参数函数*************************************************/

可变参数函数学习

分析1:

va_list args;

va_list是一个宏,由va_start和va_end界定。

定义如下:

#ifdef _M_ALPHA
typedef struct {
      char *a0; /* pointer to first homed integer argument */
      int offset; /* byte offset of next parameter */
} va_list;
#else
typedef char * va_list;
#endif

_M_ALPHA是指DEC ALPHA(Alpha AXP)架构。所以一般情况下va_list所定义变量为字符指针。

      在printk(const char *fmt, ...)函数中,va_list是一个字符指针,是指向当前参数的一个指针。

      调用参数表之前,先定义一个va_list类型的变量,以供后用(如va_list类型变量被定义为args),接着对args进行初始化,让它指向可变参数表里面的第一个参数。这是通过va_start来实现的,它的第一个参数是args本身,第二个参数是在变参表前面紧挨着的一个变量。获取参数调用va_arg它的第一个参数是args,第二个参数是要获取的参数的指定类型,并返回这个指定类型的值同时把args的位置指向变参表的下一个变量位置。获取所有参数之后,我们需要将这个args指针关掉,以避免发生危险,方法是调用va_end。它将输入的参数args置为NULL。

      这里我们也应该学习一下va_start和va_end这两个宏写法:

      1、#define va_start(ap,t) (args = (va_list)&v) + INTSIZEOF(v))

      ap是类型va_list的指针,v是可变参数最左边的参数。

      2、#define va_arg(ap,t)  (*(t*)((ap +=_INTSIZEOF(t)) - _INTSIZEOF(t)))

      va_arg(ap,t)宏获取可变参数的当前参数,返回指定类型并将指针指向下一个参数(t参数描述了当前参数的类型)

      3,define va_end(ap)    (ap = (va_list)0)

       va_end宏用于清空va_list可变参数列表。

      注:_INTSIZEOF宏是获取类型占用的空间长度,最小占用长度为int的整数倍。宏函数如下:

             #define _INTSIZEOF(N)   ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

       这里,移动指针使其指向下一个参数,偏移量是多少是个关键的问题。这里涉及到内存对齐问题,内存对齐与具体的硬件平台有关。va机制中用宏_INTSIZEOF(N)解决了这个问题。用_INTSIZEOF(N)宏求出变量占用内存空间大小是va实现的基础。

有了这些分析我们举一个使用可变参数的例子:

    int max(int n, ...)  // 定参 n 表示后面变参数量,定界用,输入时切勿搞错

   {                  
         va_list ap;       // 定义一个 va_list 指针来访问参数表
         va_start(ap, n);     // 初始化 ap,让它指向第一个变参
         int maximum = -0x7FFFFFFF;   // 这是一个最小的整数
         int temp;
         for(int i = 0; i < n; i++)
        {
              temp = va_arg(ap, int);   // 获取一个 int 型参数,并且 ap 指向下一个参数
              if (maximum < temp)
              maximum = temp;
        }
        va_end(ap);       // 善后工作,关闭 ap
        return maximum;
   }
     // 在主函数中测试 max 函数的行为(C++ 格式)
     int main()
     {
           cout << max(3, 10, 20, 30) << endl;
           cout << max(6, 20, 40, 10, 50, 30, 40) << endl;
     }

(PS:参考了别人的代码)

下一篇文章本人将贴出一些源码:

像printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);分析3列出一些源码方便自己以后慢慢分析。

0 0