嵌入式Linux之Kernel(裁减移植)启动调试、打印技术 printascii(补充)、内核调试
- 格式:doc
- 大小:26.00 KB
- 文档页数:4
1,获得源码,解压,进入解压后的目录;命令;2,修改makefile;为了能让此目录被执行所以在顶级目录的makefile中同时也进行修改;3,得到.config文件;命令;编译内核时对.config文件的依赖比较大,我们需要一个自己的.config文件,又因为我们的板子和smdk2410的很像,仅需将smdk2410的.config 文件复制到顶级目录即可不用修改;4;修改nandflash 分区;此系统启动时从nandflash 中启动而我们的板子不是的所以对其进行必要的修改;5,添加网卡驱动;arch/arm/mach-s3c2410/mach-smdk2410.c开发板上已经配置要的相应的网卡,并且内核中也有相应的实现代码我们只需做一下简单的修改;6添加yaffz文件系统支持将yaffz 源码包考到和linux-2.6.24 同一级目录下解压;在给内核打上补丁;命令是;7、配置和编译内核到现在,一个简单的内核就准备好了,我们还需要做一些配置,然后编译,内核才能正常使用。
在内核源代码的根目录下运行make menuconfig命令,进入配置界面:8,用u-boot启动内核;编译U-Boot时在源代码的tools目录下会生成一个mkimage可执行文件,用这个工具可以对前面编译内核时生成的zImage进行处理,以供U-Boot启动。
cd linux-2.6.24.4/arch/arm/bootcp /up-Star2410/kernel/linux-2.6.24.4/mkimage . 获取mkimage工具./mkimage -A arm -T kernel -C none -O linux -a 0x30008000 -e 0x30008040 -d zImage -n 'Linux-2.6.24' uImage9,最后把生成的uimage 放到主机tftp同目录下,启动开发板;用u-boot的tftp命令下载到sdram;。
嵌入式linux 裁剪编译摘要:一、嵌入式Linux 简介1.嵌入式系统的概念2.嵌入式Linux 的发展和应用二、嵌入式Linux 裁剪的重要性1.裁剪的定义和目的2.裁剪对系统性能和资源的影响三、嵌入式Linux 裁剪和编译的步骤1.准备工作a.下载Linux 内核源码b.安装开发工具和环境2.内核配置a.选择需要的配置选项b.配置编译器3.编译内核a.生成Makefileb.编译内核4.安装内核a.烧写内核到目标板b.启动嵌入式系统四、裁剪和编译过程中遇到的问题及解决方法1.编译错误a.检查Makefileb.修复依赖关系c.更新软件包2.烧写失败a.检查烧写工具和文件b.更新目标板驱动c.更换烧写方式正文:嵌入式Linux 是一种适用于嵌入式系统的轻量级操作系统,通过裁剪和编译可以使其更好地适应特定应用场景的需求。
本文将详细介绍嵌入式Linux 裁剪和编译的步骤以及过程中可能遇到的问题和解决方法。
首先,我们需要了解嵌入式系统的概念。
嵌入式系统是指被嵌入到其他设备或系统中的计算机系统,具有特定功能和性能要求。
嵌入式Linux 作为一种开源、可定制的操作系统,得到了广泛的应用。
裁剪是嵌入式Linux 开发中的重要环节,可以去除不必要的功能和代码,从而减小系统体积、降低资源占用和提高运行效率。
裁剪过程中需要选择合适的配置选项,以满足系统性能和资源的要求。
嵌入式Linux 裁剪和编译的步骤如下:1.准备工作:首先,需要下载Linux 内核源码,并安装相应的开发工具和环境。
2.内核配置:在配置阶段,需要选择需要的配置选项,例如去除不用的模块、调整编译器参数等。
此外,还需要配置编译器,以满足嵌入式系统的开发需求。
3.编译内核:在编译内核阶段,需要生成Makefile,然后使用编译器编译内核。
这一过程可能需要较长时间,需要耐心等待。
4.安装内核:最后,将编译好的内核烧写至目标板,并启动嵌入式系统。
在烧写过程中,可能会遇到一些问题,如烧写失败等。
Linux内核裁剪的具体过程和方法根据部分网摘资料和实际烧录结果进行整理:内核功能:在能够实现AT91SAM9260开发板基本功能的基础上,通过串口连接上读卡器后能进行一系列的操作和控制功能等,将读卡器的相应数据进行存储或者通过网络传输到远程的PC 机上。
远程PC机能够通过网络方式在开发板上对所连接的读卡器参数进行更新配置,如设置天线接口、设置读卡方式等。
(待与读卡器配套使用后再对内核的功能描述进行补充和完善。
)编译环境:源代码解压完成后,进入linux 2.6.19目录下,使用VI命令编辑Makefile。
确定编译环境为arm交叉编译工具与本机的安装路径一致ARCH = armCROSS_COMPILE = /opt/timesys/toolchains/armv5l-linux/bin/armv5l-linux-内核版本是linux 2.6.19 ,开发板的版本是AT91SAM9260 BOARD V1.01 ,主机系统是ubuntu11.10内核配置:内核配置的方法很多,make config、make xconfig、make menuconfig、make oldconfig 等等,它们的功能都是一样的,区别应该从名字上就能看出来,只有make oldconfig是指用系统当前的设置(./.config)作为缺省值。
这里用的是make menuconfig。
需要牢记:不必要的驱动越多,内核就越大,不仅运行速度慢、占用内存多,在少数情况下、还会引发其他问题。
具体步骤如下:首先确定shell是bash。
然后$make menuconfig。
有一些默认的符号其含义如下:"[ ]"表示该选项有两种选择方式;[*] 直接编译进内核;[] 不编译;"<>"表示该选项有三种选择方式; <*>直接编译进内核; <M>编译成模块形式,但不编译进内核;<> 不编译。
关于嵌入式Linux操作系统的内核调试技术详解近年处理器技术发展速度加快,嵌入式领域发生了翻天覆地的变化。
特别是网络的普及,消费电子异军突起,嵌入式与互联网成为最热门的技术。
在所有操作系统中,Linux是发展很快、应用很广泛的一种操作系统。
Linux的开放性以及其他优秀特性使其成为嵌入式系统开发的首选。
总的来说,嵌入式开发所面临的问题主要表现在以下几个方面。
涉及多种CPU 及多种OS嵌入式的CPU或处理器包括MIPS、PPC、ARM,XScale等不同的架构,这些处理器上运行的操作系统也有VxWorks、Linux、C/OS、WinCE等多种。
在一个企业之内,可能会同时使用好几种处理器,甚至几种嵌入式操作系统。
如果需要同时调试多种类型的电路板,那复杂性是可想而知的。
这也是我们选用瑞士Abatron公司的BDI2000的原因之一,它是一款功能强大的JTAG/BDM通用仿真器。
它支持:PPC/MIPS/ARM/XSCALE/ CPU12/CPU32/M-CORE/ColdFire等多种处理器,支持Windows/Linux系统平台,以及多种第三方调试器,并且对Flash的烧写也很简单方便。
开发工具种类繁多通常各种操作系统有各自的开发工具,在同一系统下开发的不同阶段也会应用不同的开发工具。
如在用户的目标板开发初期,需要硬件仿真器来调试硬件系统和基本的引导程序,然后进行操作系统及驱动程序的开发调试。
在调试应用程序阶段可以使用交互式的开发环境进行软件调试,在测试阶段需要一些专门的测试工具软件进行功能和性能的测试。
在生产阶段需要固化程序及出厂检测等等。
BDI2000可以适应开发的各个阶段,节约企业的支出和简化管理难度。
对目标系统的观察和控制由于嵌入式硬件系统千差万别,软件模块和系统资源也多种多样,要使系统能正常工作,软件开发者必须要对目标系统具有完全的观察和控制能力,例如硬件的各种寄存器、内存空间、操作系统的信号量、消息队列、任务、堆栈等。
Linux内核裁剪与移植内核,即操作系统。
它为底层的可编程部件提供服务,为上层应用程序提供执行环境。
内核裁剪就是对这些功能进行裁剪,选取满足特定平台和需求的功能。
不同的硬件平台对内核要求也不同,因此从一个平台到另一个平台需要对内核进行重新配置和编译。
操作系统从一个平台过渡到另一个平台称为移植。
Linux是一款平台适应性且容易裁剪的操作系统,因此Linux在嵌入式系统得到了广泛的应用。
本章将详细讲解内核裁剪与移植的各项技术。
4.1 Linux内核结构Linux内核采用模块化设计,并且各个模块源码以文件目录的形式存放,在对内核的裁剪和编译时非常方便。
下面介绍内核的主要部分及其文件目录。
4.1.1 内核的主要组成部分在第1章中已经介绍了Linux内核主要的5个部分:进程调度、内存管理、虚拟文件系统、网络接口、进程通信。
在系统移植的时候,它们是内核的基本元素,这5个部分之间的关系,如图4.1所示。
图4.1 Linux内核子系统及其之间的关系进程调度部分负责控制进程对CPU的访问。
内存管理允许多个进程安全地共享主内存区域。
内存管理从逻辑上分为硬件无关部分和硬件相关部分。
硬件无关部分提供了进程的映射和逻辑内存的对换;硬件相关部分为内存管理硬件提供了虚拟接口。
虚拟文件系统隐藏了不同类型硬件的具体细节,为所有的硬件设备提供了一个标准的接口,VFS提供了十多种不同类型的文件系统。
网络接口提供了对各种网络标准的存取和各种网络硬件的支持。
进程通信部分用于支持进程间各种不同的通信机制。
进程调度处于核心位置,内核的其他子系统都要依赖它,因为每个子系统都存在进程挂起或恢复过程。
* 进程调度与内存管理之间的关系:这两个子系统为互相依赖关系。
在多道程序环境下,程序要运行必须为之创建进程,而创建进程首先就是要将程序和数据装入内存。
另外,内存管理子系统也存在进程的挂起和恢复过程。
* 进程间通信与内存管理之间的关系:进程间通信子系统要依赖内存管理支持共享内存通信机制,通过对共同的内存区域进行操作来达到通信的目的。
linux内核printascii详解printasccii的实现在arch/arm/kernel/debug.S中,其中涉及到的文件还有arch/arm/plat-s3c/include/plat/debug-macro.S,arch/arm/mach-s3c2410/include/mach/debug-marco.SENTRY(printascii)addruart r3 /* 选择UARTn的第一个寄存器地址,一般默认选择UART0则r3=0x5000_0000 */b 2f1: waituart r2, r3senduart r1, r3busyuart r2, r3teq r1, #'\n'moveq r1, #'\r'beq 1b2: teq r0, #0 /* 在调用printascii时,r0一般指向要输出的buffer */ldrneb r1, [r0], #1teqne r1, #0 /* 判断是否到了字符串末尾 */bne 1bmov pc, lrENDPROC(printascii)ENTRY(printch) //这里是现实打印一个字符addruart r3mov r1, r0mov r0, #0b 1bENDPROC(printch)下面分别介绍addruart,waituart,senduart,busyuartaddruart的定义在arch/arm/plat-s3c/include/plat/debug-macro.S,主要用来选择uart。
#define S3C2410_UART1_OFF (0x4000)#define SHIFT_2440TXF (14-9).macro addruart, rxmrc p15, 0, \rx, c1, c0 /* 读c1控制寄存器 */tst \rx, #1 /* 测试MMU是否开启 */ldreq \rx, = S3C24XX_PA_UART //直接使用物理地址访问UART 的寄存器ldrne \rx, = S3C24XX_VA_UART //使用虚拟地址访问UART的寄存器,那么就一定要位UART的寄存器建立页表映射在head.S中#if CONFIG_DEBUG_S3C_UART != 0/* 默认使用UART0来显示,如果CONFIG_DEBUG_S3C_UART不等于0则选择相应UART */add \rx, \rx, #(S3C2410_UART1_OFF * CONFIG_DEBUG_S3C_UART)#endif这个三个宏的定义在arch/arm/plat-s3c/include/plat/debug-macro.S中 waituart,senduart,busyuart,waituart是等待Tx FIFO为empty,而busyuart则是判断Tx FIFO是否full,如果full那么就忙循环等待,知道fifo不满为止。
Linux内核构成(国嵌)Linux/arch/arm/boot/compressed/head.s1.解压缩2.初始化3.启动应用程序1 arch/arm/boot/compressed/Makefile arch/arm/boot/compressed/vmlinux.lds2. arch/arm/kernel/vmlinux.ldsLinux内核启动流程(国嵌)arch/arm/boot/compressed/start.S(head.s—负责解压缩)Start:.type start,#function.rept 8mov r0, r0.endrb 1f.word 0x016f2818 @ Magic numbers to help the loader.word start @ absolute load/run zImage address.word _edata @ zImage end address1: mov r7, r1 @ save architecture IDmov r8, r2 @ save atags pointer这也标志着u-boot将系统完全的交给了OS,bootloader生命终止。
之后代码在133行会读取cpsr并判断是否处理器处于supervisor模式——从u-boot进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。
之后是再次确认中断关闭,并完成cpsr写入mrs r2, cpsr @ get current modetst r2, #3 @ not user?bne not_angelmov r0, #0x17 @ angel_SWIreason_EnterSVCswi 0x @ angel_SWI_ARMnot_angel:mrs r2, cpsr @ turn off interrupts toorr r2, r2, #0xc0 @ prevent angel from runningmsr cpsr_c, r2然后在LC0地址处将分段信息导入r0-r6、ip、sp等寄存器,并检查代码是否运行在与链接时相同的目标地址,以决定是否进行处理。
Linuxkernel调试⽅法及⼯具Linux内核调试的⽅式以及⼯具集锦CSDN GitHubLinux内核调试的⽅式以及⼯具集锦 LDD-LinuxDeviceDrivers/study/debug"调试难度本来就是写代码的两倍. 因此, 如果你写代码的时候聪明⽤尽, 根据定义, 你就没有能耐去调试它了."--Brian Kernighan121 内核调试以及⼯具总结内核总是那么捉摸不透, 内核也会犯错, 但是调试却不能像⽤户空间程序那样, 为此内核开发者为我们提供了⼀系列的⼯具和系统来⽀持内核的调试.内核的调试, 其本质是内核空间与⽤户空间的数据交换, 内核开发者们提供了多样的形式来完成这⼀功能.⼯具描述debugfs等⽂件系统提供了 procfs, sysfs, debugfs以及 relayfs 来与⽤户空间进⾏数据交互, 尤其是 debugfs, 这是内核开发者们实现的专门⽤来调试的⽂件系统接⼝. 其他的⼯具或者接⼝, 多数都依赖于 debugfs.printk 强⼤的输出系统, 没有什么逻辑上的bug是⽤PRINT解决不了的ftrace以及其前端⼯具trace-cmd等内核提供了 ftrace ⼯具来实现检查点, 事件等的检测, 这⼀框架依赖于 debugfs, 他在 debugfs 中的 tracing ⼦系统中为⽤户提供了丰富的操作接⼝, 我们可以通过该系统对内核实现检测和分析. 功能虽然强⼤, 但是其操作并不是很简单, 因此使⽤者们为实现了 trace-cmd 等前端⼯具, 简化了 ftrace 的使⽤.kprobe以及更强⼤的systemtap 内核中实现的 krpobe 通过类似与代码劫持⼀样的技巧, 在内核的代码或者函数执⾏前后, 强制加上某些调试信息, 可以很巧妙的完成调试⼯作, 这是⼀项先进的调试技术, 但是仍然有觉得它不够好, 劫持代码需要⽤驱动的⽅式编译并加载, 能不能通过脚本的⽅式⾃动⽣成劫持代码并⾃动加载和收集数据, 于是systemtap 出现了. 通过 systemtap ⽤户只需要编写脚本, 就可以完成调试并动态分析内核kgdb && kgtp KGDB 是⼤名⿍⿍的内核调试⼯具, KGTP则通过驱动的⽅式强化了 gdb的功能, 诸如tracepoint, 打印内核变量等.perf erf Event是⼀款随 inux内核代码⼀同发布和维护的性能诊断⼯具, 核社区维护和发展. Perf 不仅可以⽤于应⽤程序的性能统计分析, 也可以应⽤于内核代码的性能统计和分析. 得益于其优秀的体系结构设计, 越来越多的新功能被加⼊ Perf, 使其已经成为⼀个多功能的性能统计⼯具集LTTng LTTng 是⼀个 Linux 平台开源的跟踪⼯具, 是⼀套软件组件, 可允许跟踪 Linux 内核和⽤户程序, 并控制跟踪会话(开始/停⽌跟踪、启动/停⽌事件等等).2 ⽤户空间与内核空间数据交换的⽂件系统内核中有三个常⽤的伪⽂件系统: procfs, debugfs和sysfs.⽂件系统描述procfs The proc filesystem is a pseudo-filesystem which provides an interface to kernel data structures.sysfs The filesystem for exporting kernel objects.debugfs Debugfs exists as a simple way for kernel developers to make information available to user space.relayfs A significantly streamlined version of relayfs was recently accepted into the -mm kernel tree.它们都⽤于Linux内核和⽤户空间的数据交换, 但是适⽤的场景有所差异:procfs 历史最早, 最初就是⽤来跟内核交互的唯⼀⽅式, ⽤来获取处理器、内存、设备驱动、进程等各种信息.sysfs 跟 kobject 框架紧密联系, ⽽ kobject 是为设备驱动模型⽽存在的, 所以 sysfs 是为设备驱动服务的.debugfs 从名字来看就是为 debug ⽽⽣, 所以更加灵活.relayfs 是⼀个快速的转发 (relay) 数据的⽂件系统, 它以其功能⽽得名. 它为那些需要从内核空间转发⼤量数据到⽤户空间的⼯具和应⽤提供了快速有效的转发机制.在 Linux 下⽤户空间与内核空间数据交换的⽅式, 第 2 部分: procfs、seq_file、debugfs和relayfsLinux ⽂件系统:procfs, sysfs, debugfs ⽤法简介2.1 procfs⽂件系统ProcFs 介绍`procfs 是⽐较⽼的⼀种⽤户态与内核态的数据交换⽅式, 内核的很多数据都是通过这种⽅式出⼝给⽤户的, 内核的很多参数也是通过这种⽅式来让⽤户⽅便设置的. 除了 sysctl 出⼝到 /proc 下的参数, procfs 提供的⼤部分内核参数是只读的. 实际上, 很多应⽤严重地依赖于procfs, 因此它⼏乎是必不可少的组件. 前⾯部分的⼏个例⼦实际上已经使⽤它来出⼝内核数据, 但是并没有讲解如何使⽤, 本节将讲解如何使⽤procfs.参考资料⽤户空间与内核空间数据交换的⽅式(2)——procfs2.2 sysfs⽂件系统内核⼦系统或设备驱动可以直接编译到内核, 也可以编译成模块, 编译到内核, 使⽤前⼀节介绍的⽅法通过内核启动参数来向它们传递参数, 如果编译成模块, 则可以通过命令⾏在插⼊模块时传递参数, 或者在运⾏时, 通过 sysfs 来设置或读取模块数据.Sysfs 是⼀个基于内存的⽂件系统, 实际上它基于ramfs, sysfs 提供了⼀种把内核数据结构, 它们的属性以及属性与数据结构的联系开放给⽤户态的⽅式, 它与 kobject ⼦系统紧密地结合在⼀起, 因此内核开发者不需要直接使⽤它, ⽽是内核的各个⼦系统使⽤它. ⽤户要想使⽤ sysfs 读取和设置内核参数, 仅需装载 sysfs 就可以通过⽂件操作应⽤来读取和设置内核通过 sysfs 开放给⽤户的各个参数:mkdir -p /sysfsmount -t sysfs sysfs /sysfs12注意, 不要把 sysfs 和 sysctl 混淆, sysctl 是内核的⼀些控制参数, 其⽬的是⽅便⽤户对内核的⾏为进⾏控制, ⽽ sysfs 仅仅是把内核的 kobject 对象的层次关系与属性开放给⽤户查看, 因此 sysfs 的绝⼤部分是只读的, 模块作为⼀个 kobject 也被出⼝到 sysfs, 模块参数则是作为模块属性出⼝的, 内核实现者为模块的使⽤提供了更灵活的⽅式, 允许⽤户设置模块参数在 sysfs 的可见性并允许⽤户在编写模块时设置这些参数在sysfs 下的访问权限, 然后⽤户就可以通过 sysfs 来查看和设置模块参数, 从⽽使得⽤户能在模块运⾏时控制模块⾏为.⽤户空间与内核空间数据交换的⽅式(6)——模块参数与sysfs2.3 debugfs⽂件系统内核开发者经常需要向⽤户空间应⽤输出⼀些调试信息, 在稳定的系统中可能根本不需要这些调试信息, 但是在开发过程中, 为了搞清楚内核的⾏为, 调试信息⾮常必要, printk可能是⽤的最多的, 但它并不是最好的, 调试信息只是在开发中⽤于调试, ⽽ printk 将⼀直输出, 因此开发完毕后需要清除不必要的 printk 语句, 另外如果开发者希望⽤户空间应⽤能够改变内核⾏为时, printk 就⽆法实现.因此, 需要⼀种新的机制, 那只有在需要的时候使⽤, 它在需要时通过在⼀个虚拟⽂件系统中创建⼀个或多个⽂件来向⽤户空间应⽤提供调试信息.有⼏种⽅式可以实现上述要求:使⽤ procfs, 在 /proc 创建⽂件输出调试信息, 但是 procfs 对于⼤于⼀个内存页(对于 x86 是 4K)的输出⽐较⿇烦, ⽽且速度慢, 有时回出现⼀些意想不到的问题.使⽤ sysfs( 2.6 内核引⼊的新的虚拟⽂件系统), 在很多情况下, 调试信息可以存放在那⾥, 但是sysfs主要⽤于系统管理,它希望每⼀个⽂件对应内核的⼀个变量,如果使⽤它输出复杂的数据结构或调试信息是⾮常困难的.使⽤ libfs 创建⼀个新的⽂件系统, 该⽅法极其灵活, 开发者可以为新⽂件系统设置⼀些规则, 使⽤ libfs 使得创建新⽂件系统更加简单, 但是仍然超出了⼀个开发者的想象.为了使得开发者更加容易使⽤这样的机制, Greg Kroah-Hartman 开发了 debugfs(在 2.6.11 中第⼀次引⼊), 它是⼀个虚拟⽂件系统, 专门⽤于输出调试信息, 该⽂件系统⾮常⼩, 很容易使⽤, 可以在配置内核时选择是否构件到内核中, 在不选择它的情况下, 使⽤它提供的API的内核部分不需要做任何改动.⽤户空间与内核空间数据交换的⽅式(1)——debugfsLinux内核⾥的DebugFSLinux驱动调试的Debugfs的使⽤简介Linux Debugfs⽂件系统介绍及使⽤Linux内核⾥的DebugFSDebugging the Linux Kernel with debugfsdebugfs-seq_fileLinux Debugfs⽂件系统介绍及使⽤Linux ⽂件系统:procfs, sysfs, debugfs ⽤法简介⽤户空间与内核空间数据交换的⽅式(1)——debugfsLinux 运⽤debugfs调试⽅法2.4 relayfs⽂件系统relayfs 是⼀个快速的转发(relay)数据的⽂件系统, 它以其功能⽽得名. 它为那些需要从内核空间转发⼤量数据到⽤户空间的⼯具和应⽤提供了快速有效的转发机制.Channel 是 relayfs ⽂件系统定义的⼀个主要概念, 每⼀个 channel 由⼀组内核缓存组成, 每⼀个 CPU 有⼀个对应于该 channel 的内核缓存,每⼀个内核缓存⽤⼀个在 relayfs ⽂件系统中的⽂件⽂件表⽰, 内核使⽤ relayfs 提供的写函数把需要转发给⽤户空间的数据快速地写⼊当前CPU 上的 channel 内核缓存, ⽤户空间应⽤通过标准的⽂件 I/ O函数在对应的 channel ⽂件中可以快速地取得这些被转发出的数据 mmap 来. 写⼊到 channel 中的数据的格式完全取决于内核中创建channel 的模块或⼦系统.relayfs 的⽤户空间API :relayfs 实现了四个标准的⽂件 I/O 函数, open、mmap、poll和close函数描述open 打开⼀个 channel 在某⼀个 CPU 上的缓存对应的⽂件.mmap 把打开的 channel 缓存映射到调⽤者进程的内存空间.read 读取 channel 缓存, 随后的读操作将看不到被该函数消耗的字节, 如果 channel 的操作模式为⾮覆盖写, 那么⽤户空间应⽤在有内核模块写时仍可以读取, 但是如 channel 的操作模式为覆盖式, 那么在读操作期间如果有内核模块进⾏写,结果将⽆法预知, 因此对于覆盖式写的channel, ⽤户应当在确认在 channel 的写完全结束后再进⾏读.poll ⽤于通知⽤户空间应⽤转发数据跨越了⼦缓存的边界, ⽀持的轮询标志有 POLLIN、POLLRDNORM 和 POLLERRclose 关闭 open 函数返回的⽂件描述符, 如果没有进程或内核模块打开该 channel 缓存, close 函数将释放该channel 缓存注意 : ⽤户态应⽤在使⽤上述 API 时必须保证已经挂载了 relayfs ⽂件系统, 但内核在创建和使⽤ channel时不需要relayfs 已经挂载. 下⾯命令将把 relayfs ⽂件系统挂载到 /mnt/relay.⽤户空间与内核空间数据交换的⽅式(4)——relayfsRelay:⼀种内核到⽤户空间的⾼效数据传输技术2.5 seq_file⼀般地, 内核通过在 procfs ⽂件系统下建⽴⽂件来向⽤户空间提供输出信息, ⽤户空间可以通过任何⽂本阅读应⽤查看该⽂件信息, 但是procfs 有⼀个缺陷, 如果输出内容⼤于1个内存页, 需要多次读, 因此处理起来很难, 另外, 如果输出太⼤, 速度⽐较慢, 有时会出现⼀些意想不到的情况, Alexander Viro 实现了⼀套新的功能, 使得内核输出⼤⽂件信息更容易, 该功能出现在 2.4.15(包括 2.4.15)以后的所有 2.4 内核以及2.6 内核中, 尤其是在 2.6 内核中,已经⼤量地使⽤了该功能⽤户空间与内核空间数据交换的⽅式(3)——seq_file内核proc⽂件系统与seq接⼝(4)—seq_file接⼝编程浅析Linux内核中的seq操作seq_file源码分析⽤序列⽂件(seq_file)接⼝导出常⽤数据结构seq_file机制3 printk在内核调试技术之中, 最简单的就是 printk 的使⽤了, 它的⽤法和C语⾔应⽤程序中的 printf 使⽤类似, 在应⽤程序中依靠的是 stdio.h 中的库,⽽在 linux 内核中没有这个库, 所以在 linux 内核中, 实现了⾃⼰的⼀套库函数, printk 就是标准的输出函数linux内核调试技术之printk调整内核printk的打印级别linux设备驱动学习笔记–内核调试⽅法之printk4 ftrace && trace-cmd4.1 trace && ftraceLinux当前版本中, 功能最强⼤的调试、跟踪⼿段. 其最基本的功能是提供了动态和静态探测点, ⽤于探测内核中指定位置上的相关信息.静态探测点, 是在内核代码中调⽤ ftrace 提供的相应接⼝实现, 称之为静态是因为, 是在内核代码中写死的, 静态编译到内核代码中的, 在内核编译后, 就不能再动态修改. 在开启 ftrace 相关的内核配置选项后, 内核中已经在⼀些关键的地⽅设置了静态探测点, 需要使⽤时, 即可查看到相应的信息.动态探测点, 基本原理为 : 利⽤ mcount 机制, 在内核编译时, 在每个函数⼊⼝保留数个字节, 然后在使⽤ ftrace时, 将保留的字节替换为需要的指令, ⽐如跳转到需要的执⾏探测操作的代码。
嵌入式Linux内核裁剪与系统构建实验一、实验目的1、了解linux内核裁减过程,掌握内核的编译方法及在开发板下如何运行一个内核。
2、学会基于busybox的根文件系统的制作。
3、熟悉开发板及uboot的使用。
二、实验条件✓IBM-PC兼容机✓Redhat9.0或其他兼容的Linux操作系统✓OMAP3730开发板三、实验原理1、linux内核裁减编译://见教材6.3.2 (第一版p126-p130,第二版p143-p147)2、基于busybox根文件系统制作:见教材6.3.3 (第一版p131-p136,第二版p148-p154)3、Uboot使用:参照附录一4、内核裁剪编译参考步骤:参照附录二5、根文件系统制作参考步骤:参照附录三6、在开发板OMAP3730下运行内核参考步骤:参照附录四7、OMAP3730开发板:四、实验内容与实验步骤1、内核裁减编译。
2、基于busybox,制作根文件系统。
3、在开发板上运行自己裁减过的内核和文件系统。
备注:本实验默认在ubuntu下进行,在其他版本的linux中操作基本类似。
实验中用到的软件包均可在xmu_omap3730_lib1.tar.gz中找到。
输入命令$ tar zxvf xmu_omap3730_lib1.tar.gz。
注:这里的$,包括下文的#均表示一种系统用户权限,前者表示普通用户,后者表示超级用户;在ubuntu下可在命令前追加sudo命令来使用超级用户权限,在fedora下可输入su命令后,按提示输入密码即可切换超级用户。
一般来说普通用户能做的超级用户均有权限做。
五、实验报告要求实验报告中要包含以下几个部分:1、实验目的2、实验条件3、实验原理4、实验步骤分析5、实验结果与总结实验步骤要详细,关键步骤要有截图,运行结果也要有截图。
内核配置要求列出选择的内核配置选项,并说明它的功能。
说明编译出来的内核文件uImage大小附录一u-boot命令简介:printenv 用来打印u-boot中正在使用的所有环境变量(包括未保存的),可不带参数setenv 用来设置一个环境变量,参数用空格隔开。
基于ARM的嵌入式linux内核的裁剪与移植前言嵌入式系统一直是计算机行业中的领域之一。
在许多应用程序中,嵌入式系统越来越流行。
嵌入式系统通常使用嵌入式芯片,如ARM芯片,并且它们通常运行Linux内核。
Linux内核是一个开放源代码的操作系统内核。
在嵌入式领域,Linux 内核可以被用于实现各种应用程序。
本文将重点介绍如何基于ARM平台的嵌入式Linux内核进行裁剪和移植。
ARM平台ARM处理器是一种RISC(Reduced Instruction Set Computer)处理器。
这种类型的处理器可用于嵌入式系统开发,因为它具有较低的功耗和高效的性能。
ARM处理器有许多版本,其中包括ARMv6和ARMv7。
ARMv6通常用于嵌入式系统,而ARMv7则用于智能手机和平板电脑等高端设备。
Linux内核的裁剪在嵌入式系统中,Linux内核需要进行裁剪,以适应嵌入式设备的需求。
与桌面计算机相比,嵌入式系统拥有更少的资源,包括RAM、闪存和存储空间。
因此,在将Linux内核移植到嵌入式系统之前,必须将内核进行裁剪。
在裁剪内核之前,您必须确定哪些内核模块是必需的。
一些模块可以从内核中移除,以减少内核的大小。
通常,将不必要的模块和其他功能从内核中移除可以使内核变得更小并具有更好的性能。
另外,裁剪内核时应确保其他组件与内核兼容。
例如,在新内核中可能需要更改驱动程序或实用程序以适应修改后的内核。
裁剪内核可能是一项比较困难的工作,需要深刻了解Linux内核的各个方面,以确保正确地裁剪内核。
移植Linux内核到ARM移植内核是将Linux内核适应新硬件的过程。
在开始移植内核之前,您必须了解嵌入式设备的硬件架构以及所需的内核组件。
移植Linux内核到ARM可以分为以下步骤:1.选择合适的ARM平台和处理器并确定所需的内核选项。
2.下载最新的内核源代码。
3.配置内核选项,并使其适应新硬件。
4.使用交叉编译器编译内核。
嵌入式linux 裁剪编译摘要:1.嵌入式Linux简介2.嵌入式Linux的裁剪3.嵌入式Linux的编译4.实例演示5.总结与展望正文:嵌入式Linux是一种应用于嵌入式系统的Linux操作系统。
与传统的Linux系统相比,嵌入式Linux系统更加小巧、高效,适用于对资源有限的嵌入式设备。
在嵌入式系统中,Linux内核的裁剪和编译是定制嵌入式Linux系统的重要步骤。
本文将详细介绍如何对嵌入式Linux进行裁剪和编译,并提供一个实例演示。
一、嵌入式Linux简介嵌入式Linux是基于Linux内核的嵌入式操作系统。
它具有开源、稳定性高、可定制性强等优点。
嵌入式Linux可以应用于各种嵌入式设备,如智能家居、工业自动化、医疗设备等。
二、嵌入式Linux的裁剪1.确定裁剪目标:根据嵌入式设备的硬件资源和性能要求,确定需要保留的系统组件。
2.选用合适的工具:使用嵌入式Linux开发工具,如交叉编译工具链、嵌入式编译器等。
3.裁剪内核:根据需求,删除不必要的内核模块和驱动,以减小内核体积。
4.定制根文件系统:根据设备需求,选取合适的文件系统,如JFFS2、YAFFS等,并裁剪不必要的文件和目录。
5.生成定制镜像:将裁剪后的内核和根文件系统打包成镜像文件,便于烧写到嵌入式设备。
三、嵌入式Linux的编译1.搭建编译环境:搭建嵌入式Linux开发环境,包括交叉编译工具链、操作系统源码等。
2.配置内核:根据嵌入式设备的硬件配置,修改内核配置文件,启用或禁用相应的内核功能。
3.编译内核:使用交叉编译工具链,编译裁剪后的内核源码。
4.编译根文件系统:根据定制需求,编译嵌入式Linux的根文件系统。
5.集成镜像:将编译后的内核和根文件系统集成到一张镜像文件中,便于烧写到嵌入式设备。
四、实例演示以下将以一个简单的嵌入式Linux系统为例,演示如何进行裁剪和编译。
1.下载嵌入式Linux源码:从开源社区下载一个适用于嵌入式设备的Linux内核源码。
嵌入式linux内核移植步骤嵌入式Linux内核移植步骤嵌入式Linux内核移植是将Linux内核移植到特定的硬件平台上的过程。
在进行嵌入式Linux内核移植之前,需要先了解目标硬件平台的相关信息,包括处理器架构、硬件接口、设备驱动等。
本文将介绍嵌入式Linux内核移植的主要步骤,以帮助读者了解移植的过程。
1. 获取源代码需要从官方或其他可靠的渠道获取Linux内核的源代码。
可以选择下载最新版本的稳定内核,也可以根据需要选择特定版本的内核。
获取源代码后,解压到本地目录。
2. 配置内核在进行内核配置之前,需要根据目标硬件平台选择适当的配置文件。
内核配置文件包含了编译内核所需的各种选项和参数。
可以使用make menuconfig或make defconfig命令进行内核配置。
在配置过程中,需要根据目标硬件平台的特点进行相应的配置,如选择正确的处理器类型、设备驱动等。
3. 编译内核配置完成后,可以使用make命令编译内核。
编译过程可能会比较耗时,需要根据计算机性能进行相应的等待。
编译完成后,会生成vmlinuz和相关的模块文件。
4. 编译设备树设备树是描述硬件平台的一种数据结构,用于在内核启动时传递硬件信息给内核。
如果目标硬件平台需要使用设备树,需要将设备树源文件编译为二进制文件。
可以使用device tree compiler(dtc)工具来编译设备树。
5. 烧录内核内核编译完成后,需要将生成的vmlinuz文件烧录到目标硬件平台上。
根据硬件平台的不同,可以使用不同的烧录工具,如dd命令、fastboot等。
烧录完成后,可以通过串口或其他方式查看内核启动信息。
6. 配置文件系统内核烧录完成后,需要为目标硬件平台配置文件系统。
可以选择使用已有的文件系统,如busybox、buildroot等,也可以根据需求自行定制文件系统。
配置文件系统包括选择合适的文件系统类型、添加必要的应用程序和驱动、配置网络等。
嵌入式系统搭建过程中,对于系统平台搭建工程师在完成Bootloader 的调试之后就进入Kernel 裁减移植的阶段,其中最重要的一步是Kernel 启动的调试,在调试Kernel 过程中通常遇到最常见的问题是启动异常:Uncompressing Linux....................................................................................... done, booting the kernel.( 挂死在此处)注意:这里是arch/arm/boot/compressed/head.S的解压过程,调用了decompress_kernel()(同目录下的misc.c)->include/asm-arm/arch-xxx/uncompress.h的putc()实现。
这是在uboot中初始化的,用的是物理地址,因为此时内核还没有起来。
而printascii则是调用了汇编。
printascii()位于arch/arm/kernel/debug.S,他需要调用虚拟地址,此虚拟地址通过machine_start提供,而相关的宏在include/asm/arch-xxx/debug-macro.S实现,这下明白了。
10-05-14添加:debug.s里面需要判断一下当前是否打开了mmu,然后指定uart的基址。
在解压阶段的head.s,mmu是1:1映射,目的是加快速度。
到了内核的head.s,就是真正的mmu了,此时就是虚拟地址了。
导致驱动异常(启动挂死)的原因有很多,如基于EVM 板的硬件做了修改(如更改了FLASH 空间大小、地址和型号,更改了SDRAM 、DDR SDRAM 空间大小、地址和型号,更改了晶振频率等),板卡ID号不支持等。
那么如何进行调试那,其实有两种调试技术比较有效。
Kernel 启动调试技术- 使用printascii() 函数跟踪start_kernel() 有没运行,在booting the kernel 之后Kernel 最先执行的是start_kernel() 函数,确认start_kernel() 有否执行就是在其开始代码段添加printascii("start_kernel …") ,如果串口没有打印出start_kernel …,说明start_kernel() 没有运行,那么可能的原因有Bootloader 配置的启动参数错误、 Kernel 加载到(DDR) SDRAM 的地址不正确, Kernel 编译时指定的(DDR) SDRAM 运行地址不正确等。
int main(int argc, char **argv){char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */ if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */# Comment/uncomment the following line to disable/enable debuggingDEBUG = y# Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y)DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlineselseDEBFLAGS = -O2endifCFLAGS += $(DEBFLAGS)int scull_read_procmem(char *buf, char **start, off_t offset,int count, int *eof, void *data){int i, j, len = 0;int limit = count - 80; /* Don't print more than this */for (i = 0; i < scull_nr_devs && len <= limit; i++) {Scull_Dev *d = &scull_devices[ i];if (down_interruptible(&d->sem))return -ERESTARTSYS;len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n",i, d->qset, d->quantum, d->size);for (; d && len <= limit; d = d->next) { /* scan the list */ len += sprintf(buf+len, " item at %p, qset at %p\n", d,d->data);if (d->data && !d->next) /* dump only the last item- save space */for (j = 0; j < d->qset; j++) {if (d->data[j])len += sprintf(buf+len," % 4i: %8p\n",j,d->data[j]);}}static int scull_get_info(char *buf, char **start, off_t offset,int len, int unused){int eof = 0;return scull_read_procmem (buf, start, offset, len, &eof, NULL);}struct proc_dir_entry scull_proc_entry = {namelen: 8,name: "scullmem",mode: S_IFREG | S_IRUGO,nlink: 1,get_info: scull_get_info,};static void scull_create_proc(){proc_register_dynamic(&proc_root, &scull_proc_entry);}static void scull_remove_proc(){[...]open("/dev", O_RDONLY|O_NONBLOCK) = 4fcntl(4, F_SETFD, FD_CLOEXEC) = 0brk(0x8055000) = 0x8055000lseek(4, 0, SEEK_CUR) = 0getdents(4, /* 70 entries */, 3933) = 1260[...]getdents(4, /* 0 entries */, 3933) = 0close(4) = 0fstat(1, {st_mode=S_IFCHR|0664, st_rdev=makedev(253, 0), ...}) = 0 ioctl(1, TCGETS, 0xbffffa5c) = -1 ENOTTY (Inappropriate ioctlfor device) write(1, "MAKEDEV\natibm\naudio\naudio1\na"..., 4096) = 4000 write(1, "d2\nsdd3\nsdd4\nsdd5\nsdd6\nsdd7"..., 96) = 96write(1, "4\nsde5\nsde6\nsde7\nsde8\nsde9\n"..., 3325) = 3325close(1) = 0_exit(0) = ?Unable to handle kernel NULL pointer dereference at virtual address 00000000printing eip:Unable to handle kernel NULL pointer dereference at virtual address \00000000printing eip:c48370c3*pde = 00000000Oops: 0002CPU: 0EIP: 0010:[faulty:faulty_write+3/576]EFLAGS: 00010286eax: ffffffea ebx: c2c55ae0 ecx: c48370c0 edx: c2c55b00esi: 0804d038 edi: 0804d038 ebp: c2337f8c esp: c2337f8cds: 0018 es: 0018 ss: 0018Process cat (pid: 23413, stackpage=c2337000)Stack: 00000001 c01356e6 c2c55ae0 0804d038 00000001 c2c55b00 c2336000 \000000010804d038 bffffbd4 00000000 00000000 bffffbd4 c010b860 00000001 \0804d03800000001 00000001 0804d038 bffffbd4 00000004 0000002b 0000002b \00000004Call Trace: [sys_write+214/256] [system_call+52/56]Code: c7 05 00 00 00 00 00 00 00 00 31 c0 89 ec 5d c3 8d b6 00 00klogd 提供了大多数必要信息用于发现问题。
嵌入式系统搭建过程中,对于系统平台搭建工程师在完成Bootloader 的调试之后就进入Kernel 裁减移植的阶段,其中最重要的一步是Kernel 启动的调试,在调试Kernel 过程中通常遇到最常见的问题是启动异常:
Uncompressing Linux............................................................
........................... done, booting the kernel.( 挂死在此处)
注意:这里是arch/arm/boot/compressed/head.S的解压过程,调用了decompress_kernel()(同目录下的misc.c)->include/asm-arm/arch-xxx/uncompress.h的putc()实现。
这是在uboot中初始化的,用的是物理地址,因为此时内核还没有起来。
而printascii则是调用了汇编。
printascii()位于arch/arm/kernel/debug.S,他需要调用虚拟地址,此虚拟地址通过machine_start提供,而相关的宏在include/asm/arch-xxx/debug-macro.S实现,这下明白了。
10-05-14添加:debug.s里面需要判断一下当前是否打开了mmu,然后指定uart的基址。
在解压阶段的head.s,mmu是1:1映射,目的是加快速度。
到了内核的head.s,就是真正的mmu了,此时就是虚拟地址了。
导致驱动异常(启动挂死)的原因有很多,如基于EVM 板的硬件做了修改(如更改了FLASH 空间大小、地址和型号,更改了SDRAM 、DDR SDRAM 空间大小、地址和型号,更改了晶振频率等),板卡ID号不支持等。
那么如何进行调试那,其实有两种调试技术比较有效。
Kernel 启动调试技术- 使用printascii() 函数跟踪start_kernel() 有没运行,在booting the kernel 之后Kernel 最先执行的是start_kernel() 函数,确认start_kernel() 有否执行就是在其开始代码段添加printascii("start_kernel …") ,如果串口没有打印出start_kernel …,说明start_kernel() 没有运行,那么可能的原因有Bootloader 配置的启动参数错误、 Kernel 加载到(DDR) SDRAM 的地址不正确, Kernel 编译时指定的(DDR) SDRAM 运行地址不正确等。
这样就需要一项一项排查错误,当错误被排查完毕,通常打印出start_kernel …是种必然,如果打印出这仪信息说明 Kernel已进入到start_kernel() 执行,如果此时有串口启动打印就比较成功了,如果仍然没有打印启动信息,就需要另外一种调试技术。
附代码修改:init/main.c <<-
…
extern void printascii(const char*); // Modify
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
printascii("start_kernel …"); // Modify
smp_setup_processor_id();
…
->>
Kernel 启动调试技术- 使用printascii() 函数打印printk() 缓存信息,如果Kernel已进入到start_kernel() 执行,仍然没有启动信息打印出来,说明串口波特率出问题的可能性比较大,启动信息是暂时缓存到临时buffer--printk_buf 中的,进入start_kernel() 中会对串口波特率重新初始化,当初始化完成后,缓存的系统启动信息便打印出来,不能打印说明用于串口波特率初始化的系统时钟源没有初始化正确,通常是系统时钟源和实际的晶振频率不一致导致的,通常排查和解决这个问题后,系统启动信息是能正确打印的。
为了帮助解决问题,可以使用 printascii() 打印printk_buf 内容。
这样就能把printascii ()打印的系统信息和预想的系统信息进行比较,从而加快解决问题的进度。
附代码修改:kernel/printk.c <<-
…
extern void printascii(const char*); // Modify
static char printk_buf[1024]; // Modify
asmlinkage int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = vprintk(fmt, args);
va_end(args);
printascii(printk_buf); // Modify
return r;
}
…
static int recursion_bug;
static int new_text_line = 1;
//static char printk_buf[1024]; // Modify
…
->>
如上是Kernel 裁减移植过程中最重要的两个启动调试技术,灵活使用将带来工作效率的提升,不管硬件平台是那种ARM 或者其它类型的CPU ,也不管是哪个 Kernel 版本(如Linux-2.6.24 、
Linux-2.6.30 等都可以采用这两个启动调试技术解决实际问题。
为了支持 printascii() 函数,需要在 Kernel 裁减中(make menuconfig )添加Kernel hacking ->
Kernel low - level debugging functions 的支持。
我的补充:
1/ 可以在/kernel/head.s里添加打印看是否跑到mmu开启前:
__turn_mmu_on:
//打印一个字符a
mov r9,r0
mov r0,'a'
bl printascii //该函数位于arch/arm/kernel/debug.s,调用了 include/mach/debug-macro.S
mov r0,r9
//现在开启mmu
mov r0, r0
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
mov r3, r3
mov pc, r13 /*实际调用了__switch_data,在head-common.s*/
2/ 一般按楼上方法,在startkernel就可以打印出来,如果:在第一步可以打印,而开启mmu后不能打印,那绝对是虚拟地址映射问题,这个问题我搞了2天了....
3/ 如果还没有反应,就要检查串口打印那段 debug-macro.S 是否有问题了。
总结一下:
/compressed/head.s和/kernel/head.s基本上不用改,看文件头,2001年写的,就知道了.呵呵.。