开机过程中的内核打印

news/2024/11/14 9:36:04


作者: zjujoe 转载请注明出处

Email:zjujoe@yahoo.com

BLOG:http://blog.csdn.net/zjujoe

 

前言
嵌入式开发中, 通常使用串口输出调试信息,了解运行状态。 内核启动过程中,在不同阶段会通过不同的方式将调试信息输出到串口。 (注:以下内容针对 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 等待串口可用三个汇编函数。 

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

http://lxr.linux.no/linux+v2.6.27/arch/arm/mach-pxa/include/mach/debug-macro.S#L16

 

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 功能。

 

比如, http://lxr.linux.no/linux+v2.6.27/drivers/serial/8250.c 中,就实现了改功能:

2645static int __init serial8250_console_init(void)2646{2647        if (nr_uarts > UART_NR)2648                nr_uarts = UART_NR;26492650        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 system986  *987  * Releases the semaphore which the caller holds on the console system988  * and the console driver list.989  *990  * While the semaphore was held, console output may have been buffered991  * by printk().  If this is the case, release_console_sem() emits992  * 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);

发表于 @ 2009年05月27日 13:36:00 | 评论( 0 ) | 编辑| 举报| 收藏

旧一篇:PC 使用小技巧收集 | 新一篇:Git 快速参考

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zjujoe/archive/2009/05/27/4220229.aspx


http://www.niftyadmin.cn/n/4464364.html

相关文章

matlab学习笔记(一)---二维绘图

1、基本绘图指令 键入如下指令&#xff1a; xlinspace(0,2*pi,100); plot(x,sin(x),co,x,cos(x),g*); 图像如下&#xff1a; 加上一下注解&#xff1a; axis([0,2*pi,-1,1]); xlabel(x轴); ylabel(y轴); title(正弦和余弦函数图像); legend(ysin(x),ycos(x)); grid…

移植linux2.6.29内核到mini2440(太有帮助了)

移植linux2.6.29内核到mini2440 移植linux2.6.29内核到mini2440 移植环境&#xff1a; 主机&#xff1a;CentOS 5.1 交叉编译器&#xff1a;arm-linux-gcc-4.3.2 开发板平台&#xff1a;S3C2440&#xff08;mini2440开发板&#xff09; 注意:红色部分仅供参考&#xff0c;我没有…

ORACLE实时SQL监控视图

引言 实时的SQL监控&#xff08;Real Time SQL Monitoring&#xff09;是Oracle 11g的一个新特性&#xff0c;它是一项强大的工具&#xff0c;用于监视和分析正在执行的SQL语句的性能和执行计划。该功能允许我们实时地跟踪SQL查询的执行过程&#xff0c;以及了解其资源消耗、等…

matlab学习笔记(二)---三维图形的绘制

1、三维螺线例子&#xff1a; t0:pi/50:10*pi;xsin(t);ycos(t);zt;hplot3(x,y,z);set(h,LineWidth,4*get(h,LineWidth));grid图像如下&#xff1a; 2、用plot3函数重叠绘制多条曲线 xlinspace(0,3*pi);z1sin(x);z2sin(2*x);z3sin(3*x);y1zeros(size(x));y3zeros(size(x));y2y3…

matlab学习笔记(三)---图像的代数运算

1、绝对值差函数imabstiff Iimread(cameraman.tif);Juint8(filter2(fspecial(gaussian),I));Kimabsdiff(I,J);imshow(I);imshow(K,[]);</span>原图和处理后的图如下&#xff1a; 2、图像的叠加函数imadd Iimread(rice.png);Jimread(cameraman.tif);Kimadd(I,J,uint16);im…

platform_device与驱动的联系

首先你需要为SOC的各个功能部分定义他的一些资源.例如可用于访问的寄存器地址.中断号,DMA什么的。然后将这些资源(resource) 作为 platform 的dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需要的驱动添加:sta…

find查找文件的时候排除某个或几个文件或目录 find . * -path src -o -prune -print | xargs -i mv {} desc

比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件 find /usr/sam -path "/usr/sam/dir1" -prune -o -printfind [-path ..] [expression] 在路径列表的后面的是表达式-path "/usr/sam" -prune -o -print 是 -path "/usr/sam" -a -prune …

matlab学习笔记(四)--- 图像的几何操作

1、改变图像的大小imresize imresize的调用方法&#xff1a; Yimresize(X,M,Method); 其中X表示原图像&#xff0c;M表示方法倍数&#xff0c;Method表示使用何种差值方法&#xff0c;默认最近邻插值法。 Iimread(circuit.tif);Jimresize(I,1.25);imshow(I);figure,imshow(J…