D:LINUX内核层PRINTK实现原理

    xiaoxiao2022-06-22  20

    

    (最终调用上面注册的CONSOLE来输出调试信息)

    LINUX内核源码版本:linux-3.0.86  

    Linux内核层printk函数用于输出内核调试信息。Printk->vprintk->log_prefix->

    emit_log_char->log_start/log_end->console_unlock->call_console_drivers(_con_start, _log_end)->_call_console_drivers->__call_console_drivers->con->write(con, &LOG_BUF(start), end - start)上面printk输出信息的流程。直到调用到最低层的write函数就把数据从串口输出来了,其实就调用到上节介绍了的操控台。调用操控台中的写函数。上面函数需要注意的几个地方。

    /* Emit the output into the temporary buffer */

    printed_len += vscnprintf(printk_buf + printed_len,

      sizeof(printk_buf) - printed_len, fmt, args);//输出信息暂存在printk_buf中。

    p = printk_buf;

    int current_log_level = default_message_loglevel;

    CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4

     

    log_prefix当我们不在printk函数输出信息中添加级别的话函数会为其添加一个默认的输出信息级别4

    上面准备好了输出信息,则下面函数就来把准备好的数据输出来了。

    static void _call_console_drivers(unsigned start, unsigned end, int msg_log_level)

    {

    if ((msg_log_level < console_loglevel || ignore_loglevel) &&

    console_drivers && start != end) {

    if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {

    /* wrapped write */

    __call_console_drivers(start & LOG_BUF_MASK,

    log_buf_len);

    __call_console_drivers(0, end & LOG_BUF_MASK);

    } else {

    __call_console_drivers(start, end);

    }

    }

    }//此函数判断我们调试信息是否能输出。判断信息的级别console_loglevel。如果我们的调试信息小于console_loglevel则输出。否则不能输出的。因此console_loglevel这个变量是可以通过外部来调节的。默认是7.默认输出所有调试信息的。ignore_loglevel如果这个定义了。则console_loglevel不起作用,也会输出所有调试信息。

    sysrq_handle_loglevel->console_loglevel = i;可以通过文件系统层来调节允许输出调试信息级别。值越小能输出到操控台的信息就少,反之则越大。

    /*

     * Call the console drivers on a range of log_buf

     */

    static void __call_console_drivers(unsigned start, unsigned end)

    {

    struct console *con;

    for_each_console(con) {//轮询console_driver上一节注册的操控台。

    if (exclusive_console && con != exclusive_console)//如果设置了调试用的操控台,则其它操控台不输出调试信息。

    我们的平台是exclusive_console=s3c24xx_serial_console

    continue;

    if ((con->flags & CON_ENABLED) && con->write &&

    (cpu_online(smp_processor_id()) ||

    (con->flags & CON_ANYTIME)))

    con->write(con, &LOG_BUF(start), end - start);

    }

    }//这部分代码完成真正的输出功能。con->write(con, &LOG_BUF(start), end - start);真正调用到的是s3c24xx_serial_console_writes3c24xx_serial_console &LOG_BUF(start), end - start)说明了当第一次注册时就会把缓冲的数据一次性完部输出。其后是每次调用时输出相关功能。Printk实现的了信号量的,每次只有一个外部能调用它输出,当在PIRINT执行中时就MUTEX了等待。

     

    uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);

    s3c24xx_serial_console_putchar(struct uart_port *port, int ch)

    port = &s3c24xx_serial_ports[co->index].port;

    [0] = {

    .port = {

    .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),

    .iotype = UPIO_MEM,

    .irq = IRQ_S3CUART_RX0,

    .uartclk = 0,

    .fifosize = 16,

    .ops = &s3c24xx_serial_ops,

    .flags = UPF_BOOT_AUTOCONF,

    .line = 0,

    }

    },

    cons_uart=等于在console_register注册时值,我们代码就会根据co->index=0等到为每一个串口终端输出数据。

    s3c24xx_serial_console_putchar(struct uart_port *port, int ch)

    {

    unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);

    while (!s3c24xx_serial_console_txrdy(port, ufcon))

    barrier();

    wr_regb(cons_uart, S3C2410_UTXH, ch);

    }因此最终实际输出数据是由cons_uart=决定为一那一个串口。

    通过上面代码可知中如查定义了多个操控台,则输出的为cons_uart=最后初始化它的。因为这个变量只存了最后一个。前面定义的操控台还是不能输出数据的。

     

     

     

    兴趣交流群抠抠: 461283592

     

    转载请注明原文地址: https://ju.6miu.com/read-1122684.html

    最新回复(0)