走读printk代码
走读printk代码
在我们书写内核代码的时候通常会使用printk,这里我们看下printk是如何和uart关联起来的。关于uart的相关的硬件知识不介绍了,so easy。我们这里只是走读下printk的代码,看看和uart的驱动的关联。printk---函数实现在kernel/printk.c文件中。接下来的很多的函数都是在这个文件中。
这是一个神奇的函数哦,参数中的“...”可以让你参数输入的比较随意了。在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。
- asmlinkage int printk(const char *fmt, ...)
- {
- va_list args;
- int r;
- #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);
- _trace_kernel_printk(_RET_IP_);
- r = vprintk(fmt, args);
- va_end(args);
- return r;
- }
- asmlinkage int vprintk(const char *fmt, va_list args)
- {
- ........................
- spin_lock(&logbuf_lock); //申请到对log buffer的锁,锁的使用对防止并发有很好的作用
- ........................
- /* 这里会看到我们的输入的log level的处理 */
- if (p[0] == '<') {
- unsigned char c = p[1];
- if (c && p[2] == '>') {
- switch (c) {
- case '0' ... '7': /* loglevel */
- current_log_level = c - '0';
- /* Fallthrough - make sure we're on a new line */
- case 'd': /* KERN_DEFAULT */
- if (!new_text_line) {
- emit_log_char('\n');
- new_text_line = 1;
- }
- /* Fallthrough - skip the loglevel */
- case 'c': /* KERN_CONT */
- p += 3;
- break;
- }
- }
- }
- /*
- * 在这里就把所要输出的内容copy to log_buf。然后判断是否需要打印log tag,和时间戳。
- */
- for ( ; *p; p++) {
- if (new_text_line) {
- /* Always output the token */
- emit_log_char('<');
- emit_log_char(current_log_level + '0');
- emit_log_char('>');
- printed_len += 3;
- new_text_line = 0;
- if (printk_time) {
- /* Follow the token with the time */
- char tbuf[50], *tp;
- unsigned tlen;
- unsigned long long t;
- unsigned long nanosec_rem;
- t = cpu_clock(printk_cpu);
- nanosec_rem = do_div(t, 1000000000);
- tlen = sprintf(tbuf, "[%5lu.%06lu] ",
- (unsigned long) t,
- nanosec_rem / 1000);
- for (tp = tbuf; tp < tbuf + tlen; tp++)
- emit_log_char(*tp);
- printed_len += tlen;
- }
- if (!*p)
- break;
- }
- emit_log_char(*p);
- if (*p == '\n')
- new_text_line = 1;
- }
- /*
- *取得console的使用标志并且要立即释放掉,释放函数会做输出的动作
- *
- * 在前面看到的spin_lock(&logbuf_lock),在函数acquire_console_semaphore_for_printk函数中也会得到释放
- *重申:对于锁一定要记得去释放
- */
- if (acquire_console_semaphore_for_printk(this_cpu))
- release_console_sem();
- ............................
- }
release_console_sem的作用是解锁console系统
- void release_console_sem(void)
- {
- //先判断了下是否系统需要休眠,需要休眠则离开
- for ( ; ; ) {
- spin_lock_irqsave(&logbuf_lock, flags);
- wake_klogd |= log_start - log_end;
- if (con_start == log_end)
- break; /* Nothing to print */
- _con_start = con_start;
- _log_end = log_end;
- con_start = log_end; /* Flush */
- spin_unlock(&logbuf_lock);
- stop_critical_timings(); /* don't trace print latency */
- //在把数据拿出来之后,这里终于看到了调用串口驱动的接口
- call_console_drivers(_con_start, _log_end);
- start_critical_timings();
- local_irq_restore(flags);
- }
- console_locked = 0;
- up(&console_sem);
- spin_unlock_irqrestore(&logbuf_lock, flags);
- if (wake_klogd)
- wake_up_klogd();
- }
- EXPORT_SYMBOL(release_console_sem);
|
评论暂时关闭