linux打印机内核,linux开机过程中的内核打印

前言

嵌入式开发中, 通常使用串口输出调试信息,了解运行状态。 内核启动过程中,在不同阶段会通过不同的方式将调试信息输出到串口。 (注:以下内容针对 arm-linux.)

解压缩阶段

解压缩阶段内核会输出:

Uncompressing Linux................................ done, booting the kernel.

查找内核, 会发现如上输出是在如下语句中打印的:

312        putstr("Uncompressing Linux...");

313        gunzip();

314        putstr(" done, booting the kernel\n");

putstr 会调用 uncompress.h 中的 putc 来完成任务。

通常, 嵌入式开发中,我们假定 bootloader 已经进行了串口初始化, 所以我们只需要对串口进行写操作就可以了。比如:

18static void putc(char c)

19{

20        volatile u32 *uart = (volatile void *) DAVINCI_UART0_BASE;

21

22        while (!(uart[UART_LSR] & UART_LSR_THRE))

23                barrier();

24        uart[UART_TX] = c;

25}

实现函数,就可以看到串口输出了。当然, 如果您是完美主义者, 还应该实现:

27static inline void flush(void)

28{

29        volatile u32 *uart = (volatile void *) DAVINCI_UART0_BASE;

30        while (!(uart[UART_LSR] & UART_LSR_THRE))

31                barrier();

32}

Image 启动早期

这里的“早期”定义为串口驱动初始化之前。 这里又分为两个阶段:汇编阶段与 C 语言阶段。

汇编阶段

在启动正常后一般不需要在此阶段输出信息。但是,如果系统还没有正常启动, 则需要在汇编里向串口输出一些信息。

内核提供了函数:

116ENTRY(printascii)

为了使用它,我们需要实现:

addruart 获得调试串口的地址

senduart 发送一个字节

waituart 等待串口可用

三个汇编函数。

可以参考某一平台来实现, 比如:

pxa 的实现表明, 由于 uart 通常是8250 兼容的, 所以很可能我们只需要实现:

addruart, 提供一下串口基地址,即可。

另外, 值得注意的是:汇编代码会经历实地址模式与虚拟地址模式两个阶段。 而上面的打印函数是可以同时用于两种模式的。

C 语言阶段

C 语言代码从 start_kernel 开始, 可以看到,内核很快就会调用 printk:

printk(KERN_NOTICE);

然而在 arm 平台上, 如上的打印要等 console_init()函数执行后,才会输出到串口。

如果有人有兴趣, 可以为arm平台实现一个 patch, 使得在打印内核 notice时就能够向串口输出。 当然, 对于 BSP 开发, 目前的实现基本够用, 因为驱动程序初始化时间较晚,在 console_init 之后。

我们看一下该函数的实现:

3644/*

3645 * Initialize the console device. This is called *early*, so

3646 * we can't necessarily depend on lots of kernel help here.

3647 * Just do some early initializations, and do the complex setup

3648 * later.

3649 */

3650void __init console_init(void)

3651{

3652        initcall_t *call;

3653

3654        /* Setup the default TTY line discipline. */

3655        tty_ldisc_begin();

3656

3657        /*

3658         * set up the console device so that later boot sequences can

3659         * inform about problems etc..

3660         */

3661        call = __con_initcall_start;

3662        while (call < __con_initcall_end) {

3663                (*call)();

3664                call++;

3665        }

3666}

原来该函数会调用 __con_initcall_start 段里的函数。而串口驱动正是在此注册了一个这样的函数, 而提供了 early printk 功能。

比如, 中,就实现了改功能:

2645static int __init serial8250_console_init(void)

2646{

2647        if (nr_uarts > UART_NR)

2648                nr_uarts = UART_NR;

2649

2650        serial8250_isa_init_ports();

2651        register_console(&serial8250_console);

2652        return 0;

2653}

2654console_initcall(serial8250_console_init);

本质上,就是提前注册串口终端,而不是在串口驱动初始化时才注册。

Image 启动后期

有了 前面实现的 early print, 内核打印就没有问题了, 如果没有实现early print, 则在串口驱动初始化时,会注册一个console 驱动, 从而实现内核打印。

当然, 前提是我们需要在串口驱动中实现并注册 static struct console 。

具体细节可以参考样例驱动。 这里就不深究了。

内核打印机制浅析

内核函数调用 printk 后, printk 将 打印信息发送到一个 log_buffer. 然后调用如下函数, 试图进行后续处理。

如果已经注册了终端,则会调用终端的输出函数。

另外,终端注册函数也会调用 release_console_sem, 将此前所有的信息一次性打印到 console.

984 /**

985  * release_console_sem - unlock the console system

986  *

987  * Releases the semaphore which the caller holds on the console system

988  * and the console driver list.

989  *

990  * While the semaphore was held, console output may have been buffered

991  * by printk().  If this is the case, release_console_sem() emits

992  * the output prior to releasing the semaphore.

993  *

994  * If there is output waiting for klogd, we wake it up.

995  *

996  * release_console_sem() may be called from any context.

997  */

998 void release_console_sem(void)

999 {

1000         unsigned long flags;

1001         unsigned _con_start, _log_end;

1002         unsigned wake_klogd = 0;

1003

1004         if (console_suspended) {

1005                 up(&console_sem);

1006                 return;

1007         }

1008

1009         console_may_schedule = 0;

1010

1011         for ( ; ; ) {

1012                 spin_lock_irqsave(&logbuf_lock, flags);

1013                 wake_klogd |= log_start - log_end;

1014                 if (con_start == log_end)

1015                         break;         /* Nothing to print */

1016                 _con_start = con_start;

1017                 _log_end = log_end;

1018                 con_start = log_end;   /* Flush */

1019                 spin_unlock(&logbuf_lock);

1020                 call_console_drivers(_con_start, _log_end);

1021                 local_irq_restore(flags);

1022         }

1023         console_locked = 0;

1024         up(&console_sem);

1025         spin_unlock_irqrestore(&logbuf_lock, flags);

1026         if (wake_klogd)

1027                 wake_up_klogd();

1028 }

1029 EXPORT_SYMBOL(release_console_sem

========================================================================

转载:

http://blog.csdn.net/zjujoe/archive/2009/05/27/4220229.aspx

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值