zoukankan      html  css  js  c++  java
  • printk解析

    以下源码基于Linux-2.6.32.2

    printk是Linux内核空间的信息输出函数,printk入口位于kernel/Printk.c

    以下是printk的流程解析:

    printk
        vprintk
            /* Emit the output into the temporary buffer */
             * vsnprintf把printk的参数进行解析分类,然后用一个暂时缓冲区接受解析后的数据
             */
            vscnprintf(printk_buf + printed_len,sizeof(printk_buf) - printed_len, fmt, args);
            p = printk_buf;
    
            /* Do we have a loglevel in the string? 
             *提取printk信息级别:<c>     c就是级别
             */
    
            /*
             * Copy the output into log_buf.  If the caller didn't provide
             * appropriate log level tags, we insert them here
             * 把printk的输出信息放到全局缓冲区log_buf里面,
             *     static char *log_buf = __log_buf;
             */
            emit_log_char('<');
            emit_log_char(current_log_level + '0');
            emit_log_char('>');
            emit_log_char(*p);
    
            release_console_sem();
                call_console_drivers(_con_start, _log_end);
                    _call_console_drivers(start_print, cur_index, msg_level);
                        /*如果printk的输出信息级别 < 控制台的信息级别,则输出*/
                        if ((msg_log_level < console_loglevel || ignore_loglevel) &&console_drivers && start != end)
                        __call_console_drivers(start, end);
                            /*对每个控制台调用写函数,输出printk信息*/
                            for_each_console(con)
                            con->write(con, &LOG_BUF(start), end - start);

    以上流程可以知道,printk最终会调用到控制台的写函数,用于输出数据
    另外,在设置bootargs的时候,设置了console=ttySAC0,也就是设置控制台为ttySAC0,通过下面的解析,得出name="ttySAC"等,并把数据放到console_cmdline数组内

    static int __init console_setup(char *str)
        strncpy(buf, str, sizeof(buf) - 1);
        for (s = buf; *s; s++)
            if ((*s >= '0' && *s <= '9') || *s == ',')
                break;
        __add_preferred_console(buf, idx, options, brl_options);
            c = &console_cmdline[i];
            strlcpy(c->name, name, sizeof(c->name));
            c->options = options;
            c->index = idx;
    
    __setup("console=", console_setup);    //当解析到console= 这个字符串的时候,调用console_setup函数进行处理

    可以想象的是,这时候我们设置的是控制台的名字,当我们需要设置控制台的操作函数时,通过比对该名字,如果匹配名字则把操作函数设置为底层的操作函数,
    在内核中搜索ttySAC,得到一个匹配项,位于drivers/serial/Samsung.c

    #define S3C24XX_SERIAL_NAME    "ttySAC"
    
    static struct console s3c24xx_serial_console = {
        .name        = S3C24XX_SERIAL_NAME,        //"ttySAC"
        .device        = uart_console_device,        //init进行,用户程序打开/dev/console时用到
        .flags        = CON_PRINTBUFFER,        //注册完成该结构体后,打印先前在log_buf中保存的信息,其实前面的数据都是还没能输出的,只是放在log_buf内
        .index        = -1,                //表示使用哪个串口命令由命令行参数决定
        .write        = s3c24xx_serial_console_write,    //串口控制台输出函数
        .setup        = s3c24xx_serial_console_setup    //串口控制台设置函数
    };

    以上就是控制台的各个属性跟操作函数,注册函数为
    s3c24xx_serial_initconsole(struct platform_driver *drv,struct s3c24xx_uart_info *info)
      register_console(&s3c24xx_serial_console);

    那么上面的函数是如何被调用的呢?
    我们搜索s3c24xx_serial_initconsole,发现下面的宏定义:

    #define s3c24xx_console_init(__drv, __inf)            \
    static int __init s3c_serial_console_init(void)            \
    {                                \
        return s3c24xx_serial_initconsole(__drv, __inf);    \
    }                                \
                                    \
    console_initcall(s3c_serial_console_init)

    在drivers/serial/S3c2440.c内有这么一行:

    s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);

    在include/linux/Init.h里面有这么一个宏定义(其中里面的initcall_t 为一个返回值为int的函数指针,__initcall_##fn中的##是一个连接符号)

    #define console_initcall(fn) \
        static initcall_t __initcall_##fn \
        __used __section(.con_initcall.init) = fn

    也就是上面的宏会变成:

    static int __init s3c_serial_console_init(void)
    {
        return s3c24xx_serial_initconsole(&s3c2440_serial_driver, &s3c2440_uart_inf);
    }
    
    static int __initcall_s3c_serial_console_init() __used __section(.con_initcall.init) = s3c_serial_console_init;

    也就是在.con_initcall.init段内定义了一个函数,该函数指针指向s3c_serial_console_init,最终调用到s3c24xx_serial_initconsole,从而注册控制台

    在linux源码搜索.con_initcall.init,得到如下信息

             __con_initcall_start = .;
                 *(.con_initcall.init)
             __con_initcall_end = .;

    在arch/arm/kernel/vmlinux.lsd.S里面有定义.con_initcall.init 段,段首为__con_initcall_start,段尾为__con_initcall_end

    这里我们可以猜想,linux在初始化的时候,调用该段内的所有函数,达到初始化控制台的目的

    在linux源码搜索__con_initcall_start,
    在driver/char/Tty_io.c内得到了如下结果

        call = __con_initcall_start;
            while (call < __con_initcall_end) {
            (*call)();
            call++;
            }

    印证了的上述猜想。

    然后向上搜索,发现该函数是在console_init内的,而console是在start_kernel内调用的。因此可以得到控制台初始化的整个轮廓:

    start_kernel(void)        //main.c
        console_init(void)    //driver/char/Tty_io.c
            call = __con_initcall_start;
            while (call < __con_initcall_end) {
            (*call)();
            call++;
            }
                /*然后就调用到了里面的函数
                 *实际上是调用s3c_serial_console_init
                 */
                __initcall_s3c_serial_console_init()    
                    s3c24xx_serial_initconsole(&s3c2440_serial_driver, &s3c2440_uart_inf)
                        register_console(&s3c24xx_serial_console);    
                        /*该函数位于kernel/Printk.c内
                         *在该函数内比对我们输入的console(console_cmdline数组)跟所初始化的console
                         */
  • 相关阅读:
    leetcode231 2的幂 leetcode342 4的幂 leetcode326 3的幂
    leetcode300. Longest Increasing Subsequence 最长递增子序列 、674. Longest Continuous Increasing Subsequence
    leetcode64. Minimum Path Sum
    leetcode 20 括号匹配
    算法题待做
    leetcode 121. Best Time to Buy and Sell Stock 、122.Best Time to Buy and Sell Stock II 、309. Best Time to Buy and Sell Stock with Cooldown 、714. Best Time to Buy and Sell Stock with Transaction Fee
    rand7生成rand10,rand1生成rand6,rand2生成rand5(包含了rand2生成rand3)
    依图
    leetcode 1.Two Sum 、167. Two Sum II
    从分类,排序,top-k多个方面对推荐算法稳定性的评价
  • 原文地址:https://www.cnblogs.com/TaigaCon/p/2839334.html
Copyright © 2011-2022 走看看