Linux kernel printk的分析

记得在编译linux内核make menuconfig的时候设定输出信息到console,要修改CONFIG_CMDLINE的内容,但是自始至终也没搞懂为何这样设置就可以把打印信息从串口输出呢?   带着这个疑问,我查看了linux的printk函数,最后找到了答案 .

一 printk 函数
printk 函数首先把要打印的信息放到buffer里面,然后调用release_console_sem最后调用到相关驱动的write函数,如果你设定了 CONFIG_CMDLINE="console=ttySL0,19200,那么printk信息就会调用ttySL这个驱动的write函数,也就是从串口输出数据了.在__call_console_drivers里面有一个很重要的变量console_drivers,它决定了调用哪支 driver输出printk信息.

printk()
{
  ..............
 
 //把待输出文字放入buffer
 va_start(args, fmt);
 printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
 va_end(args);
 
   ..............
 
 release_console_sem();
       |
       ----------void release_console_sem(void)
         {
           .............
           _call_console_drivers
                 |
                 ----------static void __call_console_drivers(unsigned long start, unsigned long end)
                   {
                    struct console *con;
                  
                    for (con = console_drivers; con; con = con->next) {
                     if ((con->flags & CON_ENABLED) && con->write)
                      con->write(con, &LOG_BUF(start), end - start);
                    }
                   }
}

二 选择console driver

下面就是printk如果确定调用哪个driver的write函数输出信息过程,或者说一个console driver选择的过程.
首先看一下linux内核启动代码:

[Main.c]
asmlinkage void __init start_kernel(void)
{
  ..........

setup_arch(&command_line);
 printk("Kernel command line: %s\n", saved_command_line);
 parse_options(command_line);
 
  ..........
}

parse_options
{
 //关键调用
 checksetup(line)
      |
      --------int __init checksetup(char *line)
       {
        struct kernel_param *p;
      
        if (line == NULL)
         return 0;
      
        p = &__setup_start;
        do {
         int n = strlen(p->str);
         if (!strncmp(line,p->str,n)) {
          if (p->setup_func(line+n))
           return 1;
         }
         p++;
        } while (p < &__setup_end);
        return 0;
       }
}

setup_arch根据CONFIG_CMDLINE指定的内容设定command_line指针. parse_options会遍历一个kernel_param结构数组,起始于__setup_start, 终止于__setup_end,

struct kernel_param {
 const char *str;
 int (*setup_func)(char *);
};

此数组里面的数据均来自于__setup(str, fn)这个宏.

[Init.h]
#define __setup(str, fn)        \
 static char __setup_str_##fn[] __initdata = str;    \
 static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/23948.html