走读printk代码


在我们书写内核代码的时候通常会使用printk,这里我们看下printk是如何和uart关联起来的。关于uart的相关的硬件知识不介绍了,so easy。我们这里只是走读下printk的代码,看看和uart的驱动的关联。

printk---函数实现在kernel/printk.c文件中。接下来的很多的函数都是在这个文件中。

这是一个神奇的函数哦,参数中的“...”可以让你参数输入的比较随意了。在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。
  1. asmlinkage int printk(const char *fmt, ...)  
  2. {  
  3.     va_list args;  
  4.     int r;  
  5.   
  6.   
  7. #ifdef CONFIG_KGDB_KDB   
  8.     if (unlikely(kdb_trap_printk)) {  
  9.         va_start(args, fmt);  
  10.         r = vkdb_printf(fmt, args);  
  11.         va_end(args);  
  12.         return r;  
  13.     }  
  14. #endif   
  15.     va_start(args, fmt);  
  16.     _trace_kernel_printk(_RET_IP_);  
  17.     r = vprintk(fmt, args);  
  18.     va_end(args);  
  19.   
  20.   
  21.     return r;  
  22. }  
这里就是对数据的规整,最终走到了vprintk这个函数中。
  1. asmlinkage int vprintk(const char *fmt, va_list args)  
  2. {  
  3. ........................  
  4.     spin_lock(&logbuf_lock); //申请到对log buffer的锁,锁的使用对防止并发有很好的作用   
  5. ........................  
  6.     /* 这里会看到我们的输入的log level的处理 */  
  7.     if (p[0] == '<') {  
  8.         unsigned char c = p[1];  
  9.         if (c && p[2] == '>') {  
  10.             switch (c) {  
  11.             case '0' ... '7'/* loglevel */  
  12.                 current_log_level = c - '0';  
  13.             /* Fallthrough - make sure we're on a new line */  
  14.             case 'd'/* KERN_DEFAULT */  
  15.                 if (!new_text_line) {  
  16.                     emit_log_char('\n');  
  17.                     new_text_line = 1;  
  18.                 }  
  19.             /* Fallthrough - skip the loglevel */  
  20.             case 'c'/* KERN_CONT */  
  21.                 p += 3;  
  22.                 break;  
  23.             }  
  24.         }  
  25.     }  
  26.   
  27.   
  28.     /* 
  29.      * 在这里就把所要输出的内容copy to log_buf。然后判断是否需要打印log tag,和时间戳。 
  30.      */  
  31.     for ( ; *p; p++) {  
  32.         if (new_text_line) {  
  33.             /* Always output the token */  
  34.             emit_log_char('<');  
  35.             emit_log_char(current_log_level + '0');  
  36.             emit_log_char('>');  
  37.             printed_len += 3;  
  38.             new_text_line = 0;  
  39.   
  40.   
  41.             if (printk_time) {  
  42.                 /* Follow the token with the time */  
  43.                 char tbuf[50], *tp;  
  44.                 unsigned tlen;  
  45.                 unsigned long long t;  
  46.                 unsigned long nanosec_rem;  
  47.   
  48.   
  49.                 t = cpu_clock(printk_cpu);  
  50.                 nanosec_rem = do_div(t, 1000000000);  
  51.                 tlen = sprintf(tbuf, "[%5lu.%06lu] ",  
  52.                         (unsigned long) t,  
  53.                         nanosec_rem / 1000);  
  54.   
  55.   
  56.                 for (tp = tbuf; tp < tbuf + tlen; tp++)  
  57.                     emit_log_char(*tp);  
  58.                 printed_len += tlen;  
  59.             }  
  60.   
  61.   
  62.             if (!*p)  
  63.                 break;  
  64.         }  
  65.   
  66.   
  67.         emit_log_char(*p);  
  68.         if (*p == '\n')  
  69.             new_text_line = 1;  
  70.     }  
  71.   
  72.   
  73.     /* 
  74.      *取得console的使用标志并且要立即释放掉,释放函数会做输出的动作 
  75.      * 
  76.      * 在前面看到的spin_lock(&logbuf_lock),在函数acquire_console_semaphore_for_printk函数中也会得到释放 
  77.      *重申:对于锁一定要记得去释放 
  78.      */  
  79.     if (acquire_console_semaphore_for_printk(this_cpu))  
  80.         release_console_sem();  
  81.   
  82.   
  83. ............................  
  84. }  
那么下面我们看看release_console_sem是如何实现输出的。
release_console_sem的作用是解锁console系统
  1. void release_console_sem(void)  
  2. {  
  3. //先判断了下是否系统需要休眠,需要休眠则离开   
  4.       
  5.     for ( ; ; ) {  
  6.         spin_lock_irqsave(&logbuf_lock, flags);  
  7.         wake_klogd |= log_start - log_end;  
  8.         if (con_start == log_end)  
  9.             break;          /* Nothing to print */  
  10.         _con_start = con_start;  
  11.         _log_end = log_end;  
  12.         con_start = log_end;        /* Flush */  
  13.         spin_unlock(&logbuf_lock);  
  14.         stop_critical_timings();    /* don't trace print latency */  
  15.         //在把数据拿出来之后,这里终于看到了调用串口驱动的接口   
  16.         call_console_drivers(_con_start, _log_end);  
  17.         start_critical_timings();  
  18.         local_irq_restore(flags);  
  19.     }  
  20.     console_locked = 0;  
  21.     up(&console_sem);  
  22.     spin_unlock_irqrestore(&logbuf_lock, flags);  
  23.     if (wake_klogd)  
  24.         wake_up_klogd();  
  25. }  
  26. EXPORT_SYMBOL(release_console_sem);  
  • 1
  • 2
  • 下一页

相关内容