printk浅析

printk浅析


printk的机制

日志等级
    #define KERN_EMERG "<0>" /* 系统不可使用 */
    #define KERN_ALERT "<1>" /* 需要立即采取行动 */
    #define KERN_CRIT "<2>" /* 严重情况 */
    #define KERN_ERR "<3>" /* 错误情况 */
    #define KERN_WARNING "<4>" /* 警告情况 */
    #define KERN_NOTICE "<5>" /* 正常情况, 但是值得注意 */
    #define KERN_INFO "<6>" /* 信息型消息 */
    #define KERN_DEBUG "<7>" /* 调试级别的信息 */
printk内核实现

printk 可以在内核的任意上下文中调用。这个调用从 ./linux/kernel/printk.c 中的 printk 函数开始,它会在使用 va_start 解析可变长度参数之后调用 vprintk(在同一个源文件)。

vprintk 函数执行了许多管理级检查(递归检查),然后获取日志缓冲区的锁(__log_buf)。接下来,它会对输入的字符串进行日志级别检查; 如果发现日志级别信息,那么对应的日志级别就会被设置。最后,vprintk 会获取当前时间(使用函数 cpu_clock)并使用 sprintf(不是标准库版本,而是在 ./linux/lib/vsprintf.c 中实现的内部内核版本)将它转换成一个字符串。这个字符串会被传递给 printk,然后它会被一个管理缓冲边界(emit_log_char)的特殊函数复制到内核日志缓冲区中。这个函数最后将获取和释放执行控制台信号,并将下一条日志消息发送到控制台(在 release_console_sem 中执行)。内核缓冲缓冲区的大小初始值为 4KB,但是最新的内核大小已经升级到 16KB(在不同的体系架构上,这个值最高可以达到 1MB)。

asmlinkage int printk(const char *fmt, ...)
{
    va_list args;
    int r;

/*
 *启动KDB调试
 */
#ifdef CONFIG_KGDB_KDB
    if (unlikely(kdb_trap_printk)) {
        va_start(args, fmt);
        r = vkdb_printf(fmt, args);
        va_end(args);
        return r;
    }
#endif
    va_start(args, fmt);
    r = vprintk(fmt, args);//printk函数的核心步骤
    va_end(args);

    return r;
}


asmlinkage int vprintk(const char *fmt, va_list args)
{
    int printed_len = 0;
    int current_log_level = default_message_loglevel;//printk函数的默认输出级别
    unsigned long flags;
    int this_cpu;
    char *p;
    size_t plen;
    char special;

    boot_delay_msec();
    printk_delay();

    /* 当我们需要console_sem的持有者的时候 我们会停止这一步 */
    local_irq_save(flags);
    this_cpu = smp_processor_id();//获得cpu号

    /* printk在错误使用下会引发panic */
    if (unlikely(printk_cpu == this_cpu)) {
        /*
         * 如果在这个CPU上printk调用失败了,然后试着得到失败的信息但
         * 需要保证我们不会引发死锁。否则直接返回来避免printk的递归
         * 但是要标记已经发生递归,并在下一个合适的时刻将信息打印出来
         */
        /* oops_in_progress只有在panic函数中才为1 */
        if (!oops_in_progress && !lockdep_recursing(current)) {
            recursion_bug = 1;
            goto out_restore_irqs;
        }
        /*如果在printk运行时,这个CPU崩溃,确信不能死锁,
         *10秒1次初始化锁logbuf_lock和console_sem,
         *留时间给控制台打印完全的oops信息
         */
        zap_locks();
    }

    lockdep_off();
    raw_spin_lock(&logbuf_lock);
    printk_cpu = this_cpu;

    /* 因为第一次printk调用失败 所以这次需要把第一次失败的信息打印出来 */
    if (recursion_bug) {
        recursion_bug = 0;
        strcpy(printk_buf, recursion_bug_msg);
        printed_len = strlen(recursion_bug_msg);
    }
    /*将要输出的字符串按照fmt中的格式编排好,
     *放入printk_buf中,并返回应该输出的字符个数
     */
    printed_len += vscnprintf(printk_buf + printed_len,
                  sizeof(printk_buf) - printed_len, fmt, args);

    p = printk_buf;

    /* 读取日志等级 并且处理printk的特殊前缀 */
    plen = log_prefix(p, &current_log_level, &special);
    if (plen) {
        p += plen;

        switch (special) {
        case 'c
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值