zoukankan      html  css  js  c++  java
  • linux内核调试技术之printk

    1、简介(基于s3c2440 linux)

    在内核调试技术之中,最简单的就是printk的使用了,它的用法和C语言应用程序中的printf使用类似,在应用程序中依靠的是stdio.h中的库,而在linux内核中没有这个库,所以在linux内核中,使用这个printk就要对内核的实现有一定的了解。

    printf和printk的区别:printk会在开头处加上"<N>"样式的字符,N的范围是0~7,表示这个信息的级别。

    当printk("<n>"......);中的n < console_loglevel 时候,这个信息才能被打印出来。 

    在内核文件中Printk.c (kernel) 中初始化这个console_loglevel 的级别为7.

    复制代码
    /* printk's without a loglevel use this.. */
    #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
    
    /* We show everything that is MORE important than this.. */
    #define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
    #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */int console_printk[4] = {
        DEFAULT_CONSOLE_LOGLEVEL,    /* console_loglevel */
        DEFAULT_MESSAGE_LOGLEVEL,    /* default_message_loglevel */
        MINIMUM_CONSOLE_LOGLEVEL,    /* minimum_console_loglevel */
        DEFAULT_CONSOLE_LOGLEVEL,    /* default_console_loglevel */
    };
    复制代码

    2、通过命令cat /proc/sys/kernel/printk查看等级配置:

    # cat /proc/sys/kernel/printk
    7 4 1 7

    其中的 7 4 1 7,分别对应与:console_loglevel、default_message_loglevel、minimum_console_loglevel、default_console_loglevel 

    3、修改等级配置:

    #echo "1 4 1 7">/proc/sys/kernel/printk 改变这四个值,当被console_loglevel设置为1的时候,所有的调试信息都会被打印出来。

    4、printk函数记录的名称及使用

     在内核文件:Kernel.h (includelinux) 定义了0~7这8个级别的名称

    复制代码
    #define   KERN_EMERG   "<0>"    /* system is unusable */
    #define    KERN_ALERT    "<1>"    /* action must be taken immediately */
    #define    KERN_CRIT     "<2>"    /* critical conditions */
    #define    KERN_ERR      "<3>"    /* error conditions */
    #define    KERN_WARNING  "<4>"    /* warning conditions */
    #define    KERN_NOTICE   "<5>"    /* normal but significant condition*/
    #define    KERN_INFO     "<6>"    /* informational*/
    #define    KERN_DEBUG    "<7>"    /* debug-level messages */
    #define console_loglevel      (console_printk[0])
    #define default_message_loglevel (console_printk[1])
    #define minimum_console_loglevel (console_printk[2])
    #define default_console_loglevel (console_printk[3])
    复制代码

    使用printk:

    ① printk(KERN_WARNING"there is a warning here!
    ");//注意,中间没有逗号间隔。

    ② printk(KERN_DEBUG"%s %s %d ", __FILE__, __FUNCTION__, __LINE__);

    在①和②中,我们需要查看缓冲区log_buf中的数据,才能看到打印出来的信息。需要使用命令 #dmesg

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #
    # dmesg
    Linux version 2.6.22.6 (book@book-desktop) (gcc version 3.4.5) #19 Thu Dec 8 14:06:03 CST 2016
    CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
    Machine: SMDK2440
    Memory policy: ECC disabled, Data cache writeback
    On node 0 totalpages: 16384
      DMA zone: 128 pages used for memmap
      DMA zone: 0 pages reserved
      DMA zone: 16256 pages, LIFO batch:3
      Normal zone: 0 pages used for memmap
    CPU S3C2440A (id 0x32440001)<br>................................

    还可以使用 cat /proc/kmsg& 后台运行,实时打印出调试信息。在②中,__FILE__, __FUNCTION__, __LINE__分别对应的是那个文件,那个函数,第几行。非常实用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # cat /proc/kmsg&
    # ./firstdrvtest on
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 23
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 25
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 27
    # ./firstdrvtest off
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 23
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 25
    <7>/work/LinuxDrives/20.printk_debug/first_drv.c first_drv_open 27

      

     

    5、串口与printk函数的关系:   

    复制代码
    printk
        vprintk
            vscnprintf //先把输出信息放入临时BUFFER
                    // Copy the output into log_buf.
                    // 把临时BUFFER里的数据稍作处理,再写入log_buf
                    // 比如printk("abc")会得到"<4>abc", 再写入log_buf
                    // 可以用dmesg命令把log_buf里的数据打印出来重现内核的输出信息
    
            // 调用硬件的write函数输出
            release_console_sem    
                call_console_drivers
              //从log_buf得到数据,算出打印级别 _call_console_drivers if ((msg_log_level < console_loglevel)//如果级别足够则打印 __call_console_drivers con->write //con是console_drivers链表中的项,对用具体的输出函数 在drives/serial/s3c2410.c中查看

    复制代码

    在该函数中注册一个console结构体

    static void s3c24xx_serial_console_write(struct console *co, const char *s,
    unsigned int count)
    {
    uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
    }

    串口输出函数,会调用s3c24xx_serial_console_putchar函数

    复制代码
    static int s3c24xx_serial_initconsole(void)
    {
        ...........................
        register_console(&s3c24xx_serial_console);
        return 0;
    }
    static struct console s3c24xx_serial_console =
    {
    .name = S3C24XX_SERIAL_NAME,//这个宏被定义为:TTYSAC
    .device = uart_console_device, //init进程,用户程序打开/dev/console的时候会调用
    .flags = CON_PRINTBUFFER,//打印还没有初始化化console前保存在log_buf里面的数据
    .index = -1,//选择那个串口,由uboot中的命令决定
    .write = s3c24xx_serial_console_write,//控制台输出函数
    .setup = s3c24xx_serial_console_setup //串口设置函数
    };
    复制代码

    这个函数和硬件相关,读取寄存器,看数据是否被发送完成,最后将数据一个字节一个字节的写入寄存器,s3c2440的串口控制器会自动把数据发送出去

    复制代码
    static void 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);
    }
    复制代码

    uboot的console=ttySAC0如果在linux中被识别出来是串口0的,在/kernel/printk.c中有一下代码,

    复制代码
    __setup("console=", console_setup);
    
    static int __init console_setup(char *str)
    {
    ........
      add_preferred_console(name, idx, options);
    }
    复制代码

    内核开始执行时,会发现“console=...”的命令行参数时候,就会调用console_setup函数进行数据解析,对于命令行参数"console=ttySAC0",会解析出:设备名(name)为ttySAC,索引index为0,这些信息被保存在类型为console_cmdline、名称为console_cmdline的全局数组中(注意:数组名和数组的类型相同)

    在后面的register_console(&s3c24xx_serial_console);的时候,会将s3c24xx_serial_console结构和console_cmdline数组中的设备进行比较。

    ①解析出来的name为S3C24XX_SERIAL_NAME 既“ttySAC”,而console_cmdline中的命令中的name也为“ttySAC”

    ②s3c24xx_serial_console结构体中的索引index为-1,表示使用命令行参数“console=ttySAC0”中的索引,index =  0

    如果还想了解uboot的console=ttySAC0如果在linux中被识别出来是串口0的,可以看《嵌入式linux开发完全手册》Page 365

  • 相关阅读:
    mysql命令集锦
    linux 删除文件名带括号的文件
    linux下的cron定时任务
    struts2文件下载的实现
    贴一贴自己写的文件监控代码python
    Service Unavailable on IIS6 Win2003 x64
    'style.cssText' is null or not an object
    "the current fsmo could not be contacted" when change rid role
    远程激活程序
    新浪图片病毒
  • 原文地址:https://www.cnblogs.com/Ph-one/p/13399209.html
Copyright © 2011-2022 走看看