注: 填空或者选择,必考题。
嵌入式系统是以应用为中心,以计算机技术为基础,软件硬件可裁剪,适用于对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。
主机-目标板模式(PPT47)
注: 经常会考填空、选择或简答题
注: 经常会考简答题
嵌入式产品开发流程与8位机开发流程类似,但增加了RTOS移植部分
非操作系统模型架构和操作系统模型架构(PPT49-65)
注: 重点掌握操作系统模型架构,要会画架构图,会解释具体的内容
图一
图二
区别 | 非操作系统模型 | 操作系统模型 |
---|---|---|
典型架构 | 图1 | 图2 |
编写驱动 | 含有操作系统相关的接口 | |
编写应用程序 | // ledapp.c void main() { ledconfig(); for(…) { ledon();delay(); ledoff();delay(); } } |
// ledapp.c #define LEDON 1 #define LEDOFF 0 void main() { fd=open(“/dev/led”);//打开设备 for(…) { ioctl(fd,LEDON);sleep(5); ioctl(fd,LEDOFF);sleep(5); } } |
使用工具 | 开发工具多使用ADS或REALVIEW软件 | 开发工具多使用arm-linux-gcc软件开发套件或者像codeblocks等集成开发套件来完成 |
系统调试 | 使用硬件调试工具H-JTAG在开发板上进行调试 | 在开发板上进行调试(OS) |
示例总结 | ·此类开发应用于单任务模式 ·能够共享的代码较少,所使用到的软件代码均要重新开发,代码开发量较大,开发周期较长 ·对软件升级维护人员有较高要求 ·具有优秀的实时性能 |
·此类开发应用于多任务模式 ·能够使用操作系统或用户库提供的代码,代码开发量较小,开发周期较短 ·对软件升级维护人员要求较低 ·实时性能较好 |
了解什么叫大端存储系统,什么叫小端存储系统,分别是如何存储数据的?
注: 经常会考填空或者简答题(PPT21)
注: 经常会考填空题(PPT31)
电源管理模式:正常,闲置,停止,深度停止和睡眠模式
注: 一定要充分吸收相关内容,一般考一道编程题目,用到的更多的是微机原理课程上的内容。(PPT55-56)
注: 考试的时候通常是会将相应的接口内容直接给出,能够查询会用即可。
更高要求:学会位操作
注: 建议重点观看相关教学视频,掌握相关内容。(PPT57)
注: PPT中以GPM口为例做了详细的介绍,相关内容要重点理解和掌握。(PPT61-62)
重点是控制寄存器和数据寄存器的使用
功能–配置相应端口的功能
数据寄存器每个bit位对应相应端口的IO线,如果该IO线被用做输入口引脚,该bit位随着引脚线电平的变化而变化(其对应值为0或者1);如果该IO线被用作输出口引脚,则该bit位值为1时,外面对应引脚线为高电平(一般为3.3v左右),则该bit位值为0时,外面对应引脚线为低电平(一般为略大于0V)
a) 确定使用到哪些CPU引脚
b) 要使某个灯亮,就是要使对应引脚输出一个低电平(灭:高电平)
注: 具体要看电路图才知道什么时候灯亮或灭
c) 确定这四个引脚为输出口
d) 查芯片手册,就可知道如何设置
注: 详细讲解请见PPT71页相关的教学视频
配置M口M1引脚为输出口---leddrv.c
#define rGPMCON *((volatile int *) 0x7f008820 )
void ledconfig()
{
tmp= rGPMCON;//读出端口寄存器值
tmp &=! (0xF<<4);//把bit4~7清0
tmp |= (1<<4);//把bit4~7写入0x1值
rGPMCON = tmp;//写回到端口寄存器
}
点亮--配置M口M1输出低电平---leddrv.c
#define rGPMDAT *((volatile int *) 0x7f008824 )
void ledon()
{
tmp= rGPMDAT;//读出端口寄存器值
tmp &=! (0x1<<1);//把bit1清0
tmp |= (0<<1);//把bit4~7写入0x0值
rGPMDAT = tmp;//写回到端口寄存器
}
熄灭--配置M口M1输出高电平---leddrv.c
void ledoff()
{
tmp= rGPMDAT;//读出端口寄存器值
tmp &=! (0x1<<1);//把bit1清0
tmp |= (1<<1);//把bit4~7写入0x1值
rGPMDAT = tmp;//写回到端口寄存器
}
注: 一定要结合看门狗的使用程序学习掌握
假设预分频值为255,分频系数为128,使能看门狗,关闭中断并且使能RESET复位信号产生单元,则看门狗控制寄存器设置代码如下:
rWATCON &=0X0;
rWATCON |=(0xff<<8)|(1<<5)|(3<<3)|(0<<2)|(1<<0);
看门狗计数寄存器值假设设置为最大值,即0XFFFF,设置如下:
rWATCNT=0XFFFF;
rWATDAT=0XFFFF;
注: 经常考简答题(PPT6)
1.–c
(PPT17)
只对文件进行编译和汇编,但是并不进行链接,也就是说只把程序做成obj文件。
例:
arm-linux -gcc -c hello.c –o hello.o
2.–o
(PPT18)
指定目标名称,缺省的时候,gcc 编译出来的文件是a.out
例:
arm-linux -gcc hello.c
arm-linux -gcc -o hello.bin hello.c
arm-linux -gcc -o hello.s -S hello.c
3.–I
(PPT20)
-I dir
如果使用#include“file”
的时候,gcc/g++会先在当前目录查找你所指定的头文件,如果没有找到,编译器会到缺省的头文件目录找;
如果使用-I指定了目录, 编译器会先到你所指定的目录查找,然后再按常规的顺序去找。
例:
arm-linux -gcc -o hello -I/xxx/include hello.c
-I
就是取消前一个参数的功能,所以一般在-Idir
之后使用
4.–l
-L
(PPT22)
-l库名
指定编译的时候使用的库
例:
arm-linux-gcc -lpthread hello.c
-L目录
指定编译的时候,搜索库的路径。比如自己的库,就可以用它指定到你的库所在的目录,不然编译器将只在标准库的目录找。这个dir就是目录的名称。
例:
arm-linux-gcc –L./ hello.c –o hello
5.–O
(PPT23)
gcc 提供了为了满足用户不同程度的的优化需要,提供了近百种优化选项,用来对{编译时间,目标文件长度,执行效率}这个三维模型进行不同的取舍和平衡。优化的方法不一而足,总体上将有以下几类
1)精简操作指令;
2)尽量满足cpu的流水操作;
3)通过对程序行为地猜测,重新调整代码的执行顺序;
4)充分使用寄存器;
5)对简单的调用进行展开等
-O0 /O1 /O2 /O3
O0
:不做任何优化,这是默认的编译选项
O3
:优化级别最高
6.–g
(PPT24)
-g
编译器在编译的时候产生调试信息。
例:
arm-linux –gcc –o hello -g hello.c
注: 通过规则举例(PPT29)的相关教学视频重点理解(PPT28)
当变量名为单个字符时,可以省略括号
make处理变量时会扫描一遍整个makefile,确定所有变量的值,因此变量的使用可以在定义之后,而且使用的是最后一次赋予的值。
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
help NAME
显示指定命令的帮助信息
file FILE
装载指定的可执行文件进行调试
kill
终止正被调试的程序
list
显示源代码段
break NUM
在指定的行上设置断点
run
执行当前被调试的程序
continue
继续执行正在调试的程序
step
向前执行一行源代码,遇到函数的情况下,进入函数内部执行
stepi
执行一条机器指令
next
单步执行一条语句,不单步执行进入函数体
nexti
单步执行一条指令,如果该指令是函数调用,那么程序执行直到该函数调用结束时才停止
set
:设置程序中变量的值。使用格式:set 变量=表达式
set 变量:=表达式
display EXPR
每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
print
:打印变量或表达式的值。使用格式:print 变量或表达式`
info break
显示当前断点清单,包括到达断点处的次数等。
info files
显示被调试文件的详细信息。
info func
显示所有的函数名称。
info local
显示当函数中的局部变量信息。
info prog
显示被调试程序的执行状态。
info var
显示所有的全局和静态变量名称。
delete
:清除断点或自动显示的表达式。使用格式:delete 断点号
disable
:让指定断点失效。使用格式:disable 断点号列表
断点号之间用空格间隔开。
enable
:和disable相反,恢复失效的断点。使用格式:enable 断点编号列表
ignore
:忽略断点。使用格式:ignore 断点号 忽略次数
quit
:退出gdb。
第一步:用-g选项编译程序
第二步:gdb 程序
第三步:查看源码
list
第四步:在main函数设置断点
break main
第五步:运行
run
第六步:调试
step/next/break/cont/print/display
第七步:退出
quit
在开发板上运行gdbserver
在PC端执行
bootloader(可执行文件)+内核(可执行文件)+文件系统(压缩文件)
BIOS—可执行程序
(1)自检硬件
(2)加载grub后控制权交给grub,grub开始执行
GRUB—可执行程序
(1)继续初始化硬件
(2)加载内核到内存,将控制权交给内核文件,内核开始执行
bootloader(如uboot.bin)
(1)自检设备、初始化硬件
(2)加载操作系统内核到内存,启动内核执行
注: 经常会考简答题
bootloader是操作系统内核运行前运行地一段小程序。
功能
1.初始化硬件设备,如对CPU、SDRAM、Flash、串口等进行初始化;
2.加载内核到内存并执行内核代码;
3.提供一些命令工具,如操作flash、从PC机搬运内核到开发板等功能。
注: 经常会考简答题
#make smdk2440
#make
Uboot源代码比较大,其命令也较多,功能比较强大,是现在较为通用的嵌入式bootloader。
语法形式
setenv 变量 值
功能
设置开发板上的环境变量值
例
Uboot> setenv ethaddr 12:34:56:78:9A:BC
Uboot> setenv ipaddr 192.168.1.1
Uboot> setenv serverip 192.168.1.254
(tftp服务器的地址)
语法形式
tftp 待加载到的内存地址 待加载文件名
功能
通过网络将位于PC机上tftp服务器目录(如为/tftpdroot) 下的文件加载到开发板内存地址
例
Uboot> tftp 32000000 vmlinux
解释:把server(IP=环境变量中设置的serverip)中/tftpdroot/ 下的vmlinux通过TFTP读入到物理内存32000000处
语法形式
bootm 内核内存地址
功能
将位于内核内存地址处的内核启动起来
例
Uboot> #bootm 0x30008000
##Booting image at 30008000 ...
Starting kernel ...
Uncompressing
Linux......................................................................
done, .
语法形式
nand 命令码 参数
功能
根据命令码对nandflash设备进行相关操作,命令码有以下几种
write
语法形式: nand write FromMemAddr ToNandAddr size
功能:将内存地址为FromMemAddr处size字节数据写到nand设备ToNandAddr地址处。
负责管理CPU资源,以便让各个进程可以以尽量公平的方式访问CPU。进程管理还包括控制活动进程如何
内存管理是一个操作系统必不可少,并且非常重要的一环。Linux的成功和它优秀的内存管理联系非常密切,因为一个系统的高效性欲稳定性往往决定于它的内存管理机制。
对每个程序员来说.他们都希望有无穷大的快速的内存,然而,现阶段是不可能的。
为了解决无穷大,Linux 引入了虚拟存储系统;为了解决快速,Linux 引入了cache、交换机制等等,以使的存储系统,在容量上接近硬盘,在速度上接近cache。
内核协议栈为Linux提供了丰富的网络协议实现。
其中TCP/IP协议栈提供了所有操作系统中最高效最简洁的实现模式。
VFS隐藏各种文件系统的具体细节,为文件操作提供统一的接口。
Linux 内核中有大量代码(约60%)都在设备驱动程序中,它们以一种特定的模式管理底层硬件设备并以统一的接口向上层进程提供底层硬件的使用。
中断管理系统是计算机运行的基石,linux中断管理系统是根据处理器底层硬件中断系统来建立合理高效的软硬件响应机制。
Linux中断管理系统包括中断管理架构的建立、中断服务程序的编写以及包括对硬件中断的响应等多个部分。
系统调用层为用户空间提供了一套标准的系统调用函数来访问Linux内核,搭起了用户空间到内核空间的桥梁。
从功能上来看内核
内核就是一系列功能模块构成,包括系统调、进程管理、内存管理、文件系统、协议栈、设备驱动等关键功能模块,每个模块独立完成相应功能,并能与其它模块相互配合。
从 C 代码的角度来看
内核如果从 C 代码的角度来看,其实是 C 函数和全局数据的集合体。linux 内核中包含了上万个 C 函数,如大家常用的 sys_fork(),sys_open,sys_close,kmalloc()等。此外,内核中包含了大量的全局数据,如管理进程的 task_struct 链,每个节点描述一个进程相关信息,管理中断的 irq_desc 数组,数组中的每一项对应一个硬件中断管理,管理文件系统的链表,链表头由 file_systems 全局变量指向它,管理内存的 mem_map 数组等。
从可执行文件的角度来看内核
普通应用程序是由若干个 C 函数构成,并必须包含一个 main 函数,并经过编译器构成一个可执行程序。内核同样包含很多 C 函数,并含有同 main 相类似的 start_kernel 函数,最终经过编译后形成可执行文件 zImage,同普通的可执行程序没有太大区别。从执行的角度来看,应用程序从 main 函数处开始执行并结束,内核可执行文件也是从 start_kernel 函数处开始执行的,它首先构建各种管理系统,并最终手动创建第一个进程 init,并启动文件系统,并实时监视用户的请求并响应,如果没有任何任务运行,则运行 idle 进程,直至用户关机。
从用户层看内核
从用户层来看内核,内核提供若干服务功能函数,用户可以通过调用这些功能函数来使用内核,内核对用户来说是受到严格保护的,仅能通过这些系统调用函数来使用内核,这样避免了用户滥用内核,使得内核的安全得到极大保护。
从底层硬件看内核
从底层硬件的角度看内核,内核就是将底层硬件管理起来,并以 C 函数的形式提供给内核模块使用底层硬件,这些 C 函数就是我们平时说的设备驱动代码,通过设备驱动代码,将内核和硬件分割开来,屏蔽了底层硬件的实现细节。
Linux内核源代码采用树形结构进行组织,非常合理地把功能相关的文件都放在同一个子目录下,使得程序更具可读性。 也是从源代码角度看待内核
源码目录 | 相关解释说明 |
---|---|
include/ | 内核头文件,需要提供给外部模块(例如用户空间代码)使用。 |
kernel/ | Linux内核的核心代码,包含了3.2小节所描述的进程调度子系统,以及和进程调度相关的模块。 |
mm/ | 内存管理子系统(3.3小节)。 |
fs/ | VFS子系统(3.4小节)。 |
net/ | 不包括网络设备驱动的网络子系统(3.5小节)。 |
ipc/ | IPC(进程间通信)子系统。 |
arch// | 体系结构相关的代码,例如arm,x86等等。 |
arch//mach | 具体的machine/board相关的代码。 |
arch//include/asm | 体系结构相关的头文件。 |
arch//boot/dts | 设备树(Device Tree)文件。 |
init/ | Linux系统启动初始化相关的代码。 |
block/ | 提供块设备的层次。 |
sound/ | 音频相关的驱动及子系统,可以看作“音频子系统”。 |
driv | 设备驱动(在Linux kernel 3.10中,设备驱动占了49.4的代码量)。 |
lib/ | 实现需要在内核中使用的库函数,例如CRC、FIFO、list、MD5等。 |
… | … |
(顶层Makefile、子目录Makefile、.config、Kconfig)
1.顶层Makefile
所有源码子目录Makefile文件的核心,从总体上控制着内核的编译、连接。
arch/$(ARCH)/Makefile
对应体系结构的Makefile(如arm架构,对应arch/arm/Makefile文件),它用来决定哪些体系结构相关的文件参与内核的生成,并提供一些规则来生成特定格式的内核映像。
2.子目录Makefile
各级子目录下的Makefile,它们相对简单,被上一层目录里的Makefile调用来编译当前目录的文件。
scripts/Makefile.*
Makefile共用的通用规则、脚本等。
3.config
配置文件,在配置内核时生成。所有Makefile文件(包括顶层目录及各级子目录)都是根据.config来决定使用哪些文件。
4.Kconfig
一个文本形式的文件,其中主要作用实在内核配置(用make menuconfig配置内核哦)时候,作为配置选项。这种文件的语法相对简单。
其实Makefile十分重要的,决定了要编译哪些文件,怎么编译这些文件,按什么顺序连接这些文件。尤其是顶层的Makefile,大家最终一定要弄懂,因为3.8版本内核源码顶层的Makefile就有1400多行了,一下子给大家灌输太多的知识也会适得其反的,所以在后面要用到关于Makefile的相关知识时,才给大家细说相关内容。
解压
先把linux-3.8_webee210.tar.gz 从 windows拷贝到虚拟机里,然后执行下面命令:
tar xf linux-3.8_webee210.tar.gz
这样当前目录就会多了个 linux-3.8_webee210文件夹:
拷贝配置文件
去到内核的根目录下,用命令:
cd linux-3.8_webee210
把内核源码顶层目录里的webee_defconfig拷贝为.config文件,执行命令:
cpwebee defconfig.config
.config里面包含内核的配置信息,待会编译的时候会用到。
拷贝 ulmage制作工具
把之前的U-Boot源码中的tools目录中的mkimage文件复制到根目录的bin文件夹里。
编译
在编译之前,必须要事先构建好交叉编译环境,构建交叉编译环境的方法在使用手册里有详细说明。
编译内核并生成内核映像文件,用命令:
make ulmage
此时,编译器会根据内核根目录下和各子目录下的所有Makefile文件里的规则来编译内核,而根目录的 Makefile又会依赖于根目录下的.config文件的功能配置信息编译出你想要的内核。
编译完成。生成的ulmage映像文件在内核的arch/arm/boot目录下。
a)掌握内核裁剪的步骤
b)会在Kconfig文件中添加bool类型的代码
c)会在相应的makefile文件中添加相应的代码
注:必考内容。
Linux 编程实践中,经常需要向内核添加或者裁剪代码,在前面的描述中,内核包括源代码、Kconfig 文件和 Makefile,所以增删内核代码也同样需要关注这些文件。下面以实例为基础讲解内核裁剪方法。
例:在 linux 内核中添加加密算法,要求能够通过图形进行增删
步骤 1:
在 linux-2.6.38 目录下找到 crpty 目录,在该目录下新建一个 rc4.c 文件
文件内容为:
#include
void rc4ENCrypt()
{
printk("THIS IS A SIMPLE TEST,NO USE\n");
return;
}
步骤2:
打开 crpty 目录下的 Kconfig 文件,添加如下代码:
config RC4ENCRYPT
bool "RC4 ENCRYPT SUPORT"
help
this is a simple test ,no use
步骤3:
在 linux-2.6.38 目录下执行命令 make menuconfig
,在其中找到"RC4 ENCRYPT SUPORT"选项,并选中,退出配置,在子菜单中选择 RC4 ENCRYPT SUPORT 选项,保存退出后,在裁剪结果的宏文件.config 中可以看到宏的定义。
步骤4:
打开 crypt 目录下的 Makefile 文件,添加如下代码
obj-$(CONFIG_CRYPTO_WORKQUEUE) += crypto_wq.o
obj-$(CONFIG_CRYPTO_FIPS) += fips.o
obj-$(CONFIG_RC4ENCRYPT) +=rc4.o
步骤5:编译内核
cp config_linux_mini6410 .config
make zImage
编译结束后,会在 arch/arm/boot 目录下生成 linux 内核映象文件 zImage.同时可以验证,内核编译成功后,在 crpty 目录下有 rc4.o 文件。
(PPT57 另,视频中有详细的说明)
目录 | 说明 |
---|---|
/ | Linux系统根目录。 |
/bin | Binary的缩写,存放用户的可执行程序,例如ls,cp,也包含其它的SHELLR·如: bash:等。 |
/boot | 包含vmlinuz,initrd.img等启动文件,随便改动可能无法正常开机哦 |
/dev | 接口设备文件目录,如你的硬盘: hda . |
/etc | passwd这样有关系统设置与管理的文件。 |
/etc/x11 | x Windows·System的设置目录。 |
/home | 一般用户的主目录,如FTP目录等。 |
/lib | 包含执行/bin·和/sbin·目录的二进制文件时所需的共享函数库library . |
/mnt | 各项装置的文件系统加载点,例如:/mnt/cdrom是光驱的加载点。 |
/opt | 提供空间,叫较大的且固定的应用程序存储文件之用。 |
/proc | PS·命令查询的信息与这里的相同,都是系统内核与程序执行的信息, |
/root | 管理员的主目录; |
/sbin | lilo等系统启动时所需的二进制程序。 |
/tmp | Temporary,存放暂存盘的目录。 |
/usr | 存放用户使用系统命令和应用程序等信息。 |
/usr/bin | 存放用户可执行程序,如grep, mdir等。 |
/usr/doc | 存放各式程序文件的目录 |
/usr/include | 保存提供C语言加载的header-文件。 |
/usr/include/X11 | 保存提供x-Windows程序加载的header文件。 |
/usr/info | GNU程序文件目录。 |
/usr/lib | 函数库。 |
/usr/lib/X11 | 函数库。 |
/usr/local | 提供自行安装的应用程序位置。 |
/usr/man | 存放在线说明文件目录。 |
/usr/sbin | 存放经常使用的程序,如showmount . |
/usr/src | 保存程序的原始文件。 |
/usr/X11R6/bin | 存放XWindows System的执行程序。 |
/var | Variable,具有变动性质的相关程序目录,如log |
a) 解压缩 tar
# tar xjvf busybox-1.1.0.tar.bz2
b) 裁剪make menuconfig
c) 编译 make
d) 安装make install
安装完成后,在/armsys2410/myfs目录下存放着busybox生成的内容
必须要添加相应的库文件,否则会出现诸如下列的错误:Kernel panic: No init found. Try passing init= option to kernel.
省流:用readelf/ldd将应用程序所使用到的库全部找出来,并将其复制到相应的根文件系统的lib目录下,这样就完成了库文件的裁减。
如何确定需要哪些库?
# arm-linux-readelf -a busybox | grep "Shared library"
0x00000001 (NEEDED) Shared library: [libcrypt.so.1]
0x00000001 (NEEDED) Shared library: [libc.so.6]
从上面可以看出busybox使用到了libcrypt.so.1和libc.so.6两个库文件
# arm-uclibc-ldd busybox
libcrypt.so.1 =>/root/Myrootfs /lib/libc.crypt.so.1
lic.so.6=>/root/Myrootfs /lib/libc.so.6
“=>”左边表示busybox所需要的共享库名称,右边为该库文件在根文件系统中的实际位置。
棕上所述,可以将应用程序所使用到的库全部找出来,并将其复制到相综上所述,可以将应用程序所使用到的库全部找出来,并将其复制到相应的根文件系统的 lib目录下,这样就完成了库文件的裁减。
在 BIOS主菜单中选择功能号[y],开始下载yaffs根文件系统映象文件
点击“USB Port->Transmit/Restore”选项,并选择打开相应的文件系统映象文件root_qtopia.img(该文件位于光盘的images’linux目录)开始下载。
根文件系统映象文件说明:
root atopia-64Mimg