linux设备驱动学习笔记--内核调试方法之printk

1,printk类似于用户态的printf函数,但是比printf函数多了一个日志级别,内核中最常见的日志输出都是通过调用printk来实现的,其打印级别有8种可能的记录字串, 在头文件 <linux/kernel.h> 里定义:

KERN_EMERG  0用于紧急消息, 常常是那些崩溃前的消息.
KERN_ALERT  1需要立刻动作的情形.
KERN_CRIT  2严重情况, 常常与严重的硬件或者软件失效有关.
KERN_ERR  3用来报告错误情况; 设备驱动常常使用KERN_ERR 来报告硬件故障.
KERN_WARNING  4有问题的情况的警告, 这些情况自己不会引起系统的严重问题.
KERN_NOTICE  5正常情况, 但是仍然值得注意. 在这个级别一些安全相关的情况会报告.
KERN_INFO  6信息型消息. 在这个级别, 很多驱动在启动时打印它们发现的硬件的信息.
KERN_DEBUG  7用作调试消息.

2,信息格式化与printf一致,可以有以下几种用法:

                   If variable is of Type,                  useprintk format specifier:
                   int                                      %d or %x
                   unsignedint                              %u or %x
                   long                                     %ld or %lx
                   unsignedlong                             %lu or %lx
                   longlong                                 %lld or %llx
                   unsignedlong long                        %llu or %llx
                   size_t                                   %zu or %zx
                   ssize_t                                  %zd or %zx
                   pointer                                  %p


3,内核有的代码执行很快,如果直接用printk打印会导致系统慢或者卡死现象,内核提供了一个日志流控函数printk_ratelimit,定义及使用如下:

int printk_ratelimit(void);
if (printk_ratelimit())
    printk(KERN_INFO“testprint rate limit.\n”);

4,内核中为了方便打印设备编号提供了以下两个函数:

int print_dev_t(char *buffer, dev_t dev);
char *format_dev_t(char *buffer, dev_t dev);


定义见内核代码(include\linux\kdev_t.h):

#define print_dev_t(buffer, dev)                 \
    sprintf((buffer),"%u:%u\n", MAJOR(dev), MINOR(dev))
 
#define format_dev_t(buffer, dev)                \
    ({                          \
       sprintf(buffer,"%u:%u", MAJOR(dev), MINOR(dev));    \
       buffer;                         \
    })

5,在linux系统中通过文件/proc/sys/kernel/printk文件可以查看、修改内核的日志级别,此文件有四个数值,分别表示:当前记录级别, 适用没有明确记录级别的消息的缺省级别,允许的最小记录级别, 以及启动时缺省记录级别。例如

[root@bogon ~]# cat/proc/sys/kernel/printk
4       4      1       7
[root@bogon ~]#

6,不仅可以控制内核的日志级别,还可以通过下面两个文件来控制控制内核通过printk打印的速度。/proc/sys/kernel/{printk_ratelimit ,printk_ratelimit_burst},对应内核代码:

 

内核对/proc/sys下面的文件会统一导出,其定义在struct ctl_table kern_table[]中:

        {
                   .procname        = "printk_ratelimit",
                   .data             =&printk_ratelimit_state.interval, //表示printk_ratelimit文件对应interval值
                   .maxlen             = sizeof(int),
                   .mode                = 0644,
                   .proc_handler  = proc_dointvec_jiffies,
         },
         {
                   .procname        = "printk_ratelimit_burst",
                   .data            =&printk_ratelimit_state.burst,  //<span style="font-family: Arial, Helvetica, sans-serif;">表示printk_ratelimit文件对应burst值</span>

                   .maxlen             = sizeof(int),
                   .mode                = 0644,
                   .proc_handler  = proc_dointvec,
         },</span>

流控函数printk_ratelimit()的定义在include\linux\printk.h中,

#define printk_ratelimit()  __printk_ratelimit(__func__)

__printk_ratelimit()函数定义在include\linux\printk.c中

int __printk_ratelimit(const char *func)
{
	return ___ratelimit(&printk_ratelimit_state, func);
}
EXPORT_SYMBOL(__printk_ratelimit);


最终调用到函数__ratelimit,此函数在lib\ratelimit.c中实现,从实现中可以看出interval控制时间长度,burst控制打印的条数,即调用printk_ratelimit的结果是:在 /proc/sys/kernel/printk_ratelimit对应的时间段内(单位秒),打印不超过/proc/sys/kernel/printk_ratelimit_burst对应的条数。

int ___ratelimit(struct ratelimit_state*rs, const char *func)
{
         unsignedlong flags;
         intret;
 
         if(!rs->interval)       //从上面可以这个就对应/proc下面的print_ratelimit文件的值

                   return1;
 
         /*
          * If we contend on this state's lock thenalmost
          * by definition we are too busy to print amessage,
          * in addition to the one that will be printedby
          * the entity that is holding the lock already:
          */
         if(!raw_spin_trylock_irqsave(&rs->lock, flags))
                   return0;
 
         if(!rs->begin)
                   rs->begin= jiffies;
 
         if(time_is_before_jiffies(rs->begin + rs->interval)) {
                   if(rs->missed)
                            printk(KERN_WARNING"%s: %d callbacks suppressed\n",
                                     func,rs->missed);
                   rs->begin   = 0;
                   rs->printed= 0;
                   rs->missed  = 0;
         }
         if(rs->burst && rs->burst > rs->printed) {  //对应前面的proc文件中print_ratelimit_burst文件的值

                   rs->printed++;
                   ret= 1;
         }else {
                   rs->missed++;
                   ret= 0;
         }
         raw_spin_unlock_irqrestore(&rs->lock,flags);
 
         return ret;
}



 


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值