打印版本信息

来源:互联网 发布:金庸小说评价 知乎 编辑:程序博客网 时间:2024/05/16 07:59

5.1.6 打印版本信息

我们来简单的看看在内核中,在屏幕上打印一条信息的原理是怎么实现的。由于我们配置了CONFIG_PRINTK编译选项,所以调用位于kernel/printk.c中的printk函数:

 

584 asmlinkage int printk(const char *fmt, ...)

585 {

586        va_list args;

587        int r;

588 

589        va_start(args, fmt);

590        r = vprintk(fmt, args);

591        va_end(args);

592

593        return r;

594}

 

又看到了”,不错跟刚才讲的可变参数宏差不多只不过这里是可变参函数。那么start_kernel中调用的printk(KERN_NOTICE "%s", linux_banner)其中linux_banner是个全局字符串常量定义在init/version.c

const char linux_banner[] =

       "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"

       LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "/n";

 

上面那些大写的东西都在我们顶层Makefile中定义过了,就是一些版本信息,整个函数的功能没有问题,我们主要关注本质。所以来看看va_startvprintkva_end是怎么协作的。

 

首先,va_list并不是一个什么复杂的数据结构,而是:

typedef char *va_list;

仅仅就是一个char*。所以,根据va_start的定义:

#define _bnd(X, bnd)            (((sizeof (X)) + (bnd)) & (~(bnd)))

#define va_arg(ap, T)         /

  (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

这就是一个可变参数的调整。调整好以后,我们就可以使用它了。

 

665asmlinkage int vprintk(const char *fmt, va_list args)

 666{

 667        int printed_len = 0;

 668        int current_log_level = default_message_loglevel;

 669        unsigned long flags;

 670        int this_cpu;

 671        char *p;

 672

 673        boot_delay_msec();

 674        printk_delay();

 675

 676        preempt_disable();

 677        /* This stops the holder of console_sem just where we want him */

 678        raw_local_irq_save(flags);

 679        this_cpu = smp_processor_id();

 680

 681        /*

 682         * Ouch, printk recursed into itself!

 683         */

 684        if (unlikely(printk_cpu == this_cpu)) {

 685                /*

 686                 * If a crash is occurring during printk() on this CPU,

 687                 * then try to get the crash message out but make sure

 688                 * we can't deadlock. Otherwise just return to avoid the

 689                 * recursion and return - but flag the recursion so that

 690                 * it can be printed at the next appropriate moment:

 691                 */

 692                if (!oops_in_progress) {

 693                        recursion_bug = 1;

 694                        goto out_restore_irqs;

 695                }

 696                zap_locks();

 697        }

 698

 699        lockdep_off();

 700        spin_lock(&logbuf_lock);

 701        printk_cpu = this_cpu;

 702

 703        if (recursion_bug) {

 704                recursion_bug = 0;

 705                strcpy(printk_buf, recursion_bug_msg);

 706                printed_len = strlen(recursion_bug_msg);

 707        }

 708        /* Emit the output into the temporary buffer */

 709        printed_len += vscnprintf(printk_buf + printed_len,

 710                                  sizeof(printk_buf) - printed_len, fmt, args);

 711

 712

 713        p = printk_buf;

 714

 715        /* Do we have a loglevel in the string? */

 716        if (p[0] == '<') {

 717                unsigned char c = p[1];

 718                if (c && p[2] == '>') {

 719                        switch (c) {

 720                        case '0' ... '7': /* loglevel */

 721                                current_log_level = c - '0';

 722                        /* Fallthrough - make sure we're on a new line */

 723                        case 'd': /* KERN_DEFAULT */

 724                                if (!new_text_line) {

 725                                        emit_log_char('/n');

 726                                        new_text_line = 1;

 727                                }

 728                        /* Fallthrough - skip the loglevel */

 729                        case 'c': /* KERN_CONT */

 730                                p += 3;

 731                                break;

 732                        }

 733                }

 734        }

 735

 736        /*

 737         * Copy the output into log_buf.  If the caller didn't provide

 738         * appropriate log level tags, we insert them here

 739         */

 740        for ( ; *p; p++) {

 741                if (new_text_line) {

 742                        /* Always output the token */

 743                        emit_log_char('<');

 744                        emit_log_char(current_log_level + '0');

 745                        emit_log_char('>');

 746                        printed_len += 3;

 747                        new_text_line = 0;

 748

 749                        if (printk_time) {

 750                                /* Follow the token with the time */

 751                                char tbuf[50], *tp;

 752                                unsigned tlen;

 753                                unsigned long long t;

 754                                unsigned long nanosec_rem;

 755

 756                                t = cpu_clock(printk_cpu);

 757                                nanosec_rem = do_div(t, 1000000000);

 758                                tlen = sprintf(tbuf, "[%5lu.%06lu] ",

 759                                                (unsigned long) t,

 760                                                nanosec_rem / 1000);

 761

 762                                for (tp = tbuf; tp < tbuf + tlen; tp++)

 763                                        emit_log_char(*tp);

 764                                printed_len += tlen;

 765                        }

 766

 767                        if (!*p)

 768                                break;

 769                }

 770

 771                emit_log_char(*p);

 772                if (*p == '/n')

 773                        new_text_line = 1;

 774        }

 775

 776        /*

 777         * Try to acquire and then immediately release the

 778         * console semaphore. The release will do all the

 779         * actual magic (print out buffers, wake up klogd,

 780         * etc).

 781         *

 782         * The acquire_console_semaphore_for_printk() function

 783         * will release 'logbuf_lock' regardless of whether it

 784         * actually gets the semaphore or not.

 785         */

 786        if (acquire_console_semaphore_for_printk(this_cpu))

 787                release_console_sem();

 788

 789        lockdep_on();

 790out_restore_irqs:

 791        raw_local_irq_restore(flags);

 792

 793        preempt_enable();

 794        return printed_len;

 795}

 

vprintk是个大家伙!该函数负责解析并组成打印格式。673~707行是几个必要的操作,这几个函数都是跟同步互斥以及调试相关。709行,调用vscnprintf将打印信息拷贝到printk_buf缓存中。比如printk("%d, %s", 5, "hello")printk_buf中将是5hello格式化后的数据不带'/0'

 

随后,p指向这个缓存,存放着linux_banner对应的字符串。716行,由于linux_banner的第一个字符不是“<”,所以不存在log级别,直接到740行进入一个循环。743emit_log_char函数是将打印信息已经保存到log_buf中,这个log_buf有什么用,待会再说。

 

749行,printk_time,由于没有定义CONFIG_PRINTK_TIME printk_time = 0,所以忽略749765行代码。好了,771行,打印信息已经保存到log_buf中了,最后787行调用release_console_sem()函数启动console输出。

 

注意,我们这里分析的printkprintf不同。回顾一下,他首先输出到系统的一个缓冲区内,大约4k,如果登记了console,则调用console->wirte函数输出,否则就一直在buffer里呆着。所以,用printk输出的信息,如果超出了4k,会冲掉前面的。在系统引导起来后,用dmesg看的也就是这个buffer中的东西。

 

此时的信息还在buf中躺着呢,因为console还没有初始化。

原创粉丝点击