uClinux内核与驱动开发

时间:2021-01-20 04:52:32
uClinux内核简介
• uCLinux 是Linux 2.0核心的分支,是针对没有
MMU管理单元的微控制器
• 继承了Linux的大多数特性
• 多数的Linux下应用程序和驱动程序都可以在uClinux下
运行
• 内核精简
• Kernel < 512KB
• Kernel+root < 900KB
• 并入linux 2.6内核中

uClinux与linux的区别(1)
内存管理
• uClinux运行于没有MMU的处理器上
• 使用平坦式(flat)内存管理模式,虚拟内
存到物理内存是一对一的映射关系
• 对于应用程序,使用固定的栈空间。加载应
用程序的时候需要重新定位
• 可用内存空间的大小受到物理内存的限制
uClinux与linux的区别(2)
fork与vfork
• uClinux 的多进程管理通过vfork 来实
现,uClinux只能支持vfork
• vfork使父进程锁定直到子进程exec()
或exit()
注:uClinux的应用程序的多线程可以依赖
于标准C库来实现(uClibc)

uClinux与uCOS-II(1)
• uClinux源于linux,是一个很完整的系统,包
括:
• 多任务调度
• 内存管理
• 文件系统(及接口)
• 设备驱动程序
• 完整的TCP/IP的支持
• 源码开放,支持广泛(GUI、FS、驱动程序等等)
uClinux与uCOS-II(2)
uCOS-II
• 源码开放,内核简单,易于学习和移植
• 占先式内核,实时性好
• 只有多任务调度的简单内核
• 内存管理过于简单,几乎没有动态内存管理功能
• 文件系统和图形界面需要外挂
• 对于设备驱动程序没有专门统一的接口
uClinux的实时性问题
• uClinux内核不关心实时性问题
• 可以和RTLinux配合来实现实时
• RTLinux处理实时任务,非实时任务由linux完

• RTLinux是为linux提供实时性的方法,同样也
适用于uClinux。通过RTLinux的patch,可以满
足uClinux对实时性的需求
uClinux的移植版本
• Motorola DragonBall、68K
• Motorola Coldfire
• ARM7TDMI、ARM9TDMI(ARM940T)
• Altera Nios
• …
www.uClinux.org
uClinux 的内核组成
• 初始化程序段(init) 32k 左右
• 数据段(data) 50~100k 左右
• 未初始化数据段(bss) 100k~150k 左右
• 代码段(text) 300k 左右
• (init、data、bss 和text 的地址是由编译
链接时的定位文件vmlinux.lds 决定的)
• 文件系统(romfs) > 80k 左右
移植uClinux的主要工作
• 选择处理器对应的交叉编译器
• 选择并修改Bootloader
• 修改链接文件(vmlinux-armv.lds.in),定位各
个数据段
• 定义系统定时器、控制台(Console)
• 编写中断的控制函数
• 定义根文件系统
• 编写其他系统设备驱动…

mic 2006-07-01 01:42
编译器的选择
uClinux内核在ARM上的编译
• 交叉编译器
• arm-elf-gcc、arm-linux-gcc
• 标准C库——应用程序
• uC-libc、uClibc
(uC)linux的bootloader
• 系统配置、中断接管、引导
• 装载内核、根文件系统、参数传递、内核调试、
内核和根文件系统的下载等等
• 常见的uClinux(Linux)的Bootloader:
• Redboot
• Blob
• Vivi
• Uboot
• armBoot…


读懂uClinux内核源码
• (uC)linux内核庞大,结构复杂
• 对uClinux内核的统计:接近1万个文件,4百万
行代码
• 内核编程习惯(技巧)不同于应用程序

mic 2006-07-01 01:45
(uC)linux内核的C代码
• Linux内核的主体使用GNU C,在ANSI C上进
行了扩充
• Linux内核必须由gcc编译编译
• gcc和linux内核版本并行发展,对于版本的依
赖性强
• 内核代码中使用的一些编程技巧,在通常的
应用程序中很少遇到
GNU C的扩充举例
• 从C++中吸收了inline和const关键字
• ANSI C代码与GNU C中的保留关键字冲突的
问题可以通过双下划线(_ _)解决
• 例如:inline 等价于__inline__、asm等价于
__asm__
• 结构体(struct)的初始化
结构体初始化
struct sample {
int member_int;
char *member_str;
void (*member_fun)(void);
};
ANSI C中的实现
struct sample inst_c={
100, //member_int
NULL, //*member_str;
myfunc //void (*member_fun)(void);
};
C99中的实现
struct sample inst_c99 = {
.member_int = 100,
.member_fun = myfun,
};
GCC中的实现
struct sample inst_gcc = {
member_fun: myfun,
member_int: 100,
};
与C99中的用法类似,不必关心struct定义的
中的实际的顺序和其他未定义的数据,在复
杂的结构体初始化的时候很有优势。

mic 2006-07-01 01:46
宏定义的灵活使用(1)
• 虽然GCC中定义了inline关键字,但是,宏
操作(#define)仍然在系统中大量使用
• 举例:
#define DUMP_WRITE(addr,nr) do/
{ memcpy(bufp,addr,nr); bufp += nr; } while(0)
应用DUMP_WRITE,就像使用C的函数一样:
if(addr)
DUMP_WRITE(addr, nr);
else…
但是,如果如通过下的定义,都不能满足上述的情况
定义1:
#define DUMP_WRITE(addr,nr) memcpy(bufp,addr,nr);/
bufp += nr ;
定义2:
#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr);/
bufp += nr;}
宏定义的灵活使用(2)
#define OFFSETOF(strct, elem) /
((long)&(((struct strct *)0)->elem))
• 1、((struct strct *)0) 结构体strct的指针
• 2、&((struct strct *)0)->elem)成员的地址,
也就是相对于0的偏移
• 3、结果:OFFSETOF(strct,elem)返回的是,结构
体strct中成员elem的偏移量
C语言中goto的使用
• 在应用程序的C编程中,为了保证程序的模
块化,建议不使用goto
• 内核代码需要兼顾到效率,所以,大量使用
goto
• 整个内核的比例大概是每260行一个goto语句—
—速度优先
• 短距离的goto——在函数中

mic 2006-07-01 01:49
(uC)linux的驱动程序
• Linux下对外设的访问只能通过驱动程序
• Linux对于驱动程序有统一的接口,以文件的形式
定义系统的驱动程序:
• Open、Release、read、write、ioctl…
• 驱动程序是内核的一部分,可以使用中断、DMA等
操作
• 驱动程序需要在用户态和内核态之间传递数据
• uClinux下可以在应用层直接访问外设,操作寄存
器口,但是无法处理中断——不推荐使用
• uClinux不支持模块加载
内核功能的划分
• 进程管理(进程之间的通讯与同步)
• 内存管理(malloc/free)
• 文件系统
• 设备控制
• 网络功能(网络通讯协议等)
Linux下设备和模块的分类
按照上述系统内核的功能,Linux中把系统
的设备定义成如下三类:
• 字符设备
• 块设备
• 网络设备
Linux下的设备
• Linux的设备以文件的形式存在于/dev目录下
• 设备文件是特殊文件,使用ls /dev -l命令可以
看到:
crw------- 1 root root 10, 7 Aug 31 2002 amigamouse1
crw------- 1 root root 10, 134 Aug 31 2002 apm_bios
brw-rw---- 1 root disk 29, 0 Aug 31 2002 aztcd
主设备号和次设备号
• 主设备号标识设备对应的驱动程序
• 一个驱动程序可以控制若干个设备,次设备号提
供了一种区分它们的方法
• 系统增加一个驱动程序就要赋予它一个主设备
号。这一赋值过程在驱动程序的初始化过程中
int register_chrdev(unsigned int major, const char
*name,struct file_operations *fops);
创建设备节点
• 设备已经注册到内核表中,对于设备的访问通过
设备文件(设备文件与设备驱动程序的主设备号
匹配),内核会调用驱动程序中的正确函数
• 给程序一个它们可以请求设备驱动程序的名字。
这个名字必须插入到/dev目录中,并与驱动程序
的主设备号和次设备号相连
• 使用mknod在文件系统上创建一个设备节点
mknod
/dev/mydevice
c 254 0
动态分配设备号
• 在Documentation/device.txt文件中可以找
到已经静态分配给大部分设备的列表
• 由于许多数字已经分配了,为新设备选择一
个唯一的号码是很困难的
• 如果调用register_chrdev时的major为零,
函数就会选择一个空闲号码并做为返回值返

动态分配的问题
动态分配的主设备号不能保证总是一样的,
无法事先创建设备节点
• 可以从/proc/devices读取
cat /proc/devices
• 利用脚本动态创建设备文件节点
设备管理的问题
如今,Linux 支持很多不同种类的硬件。这
意味着/dev中都有数百个特殊文件来表示所有
这些设备。而且,这些特殊文件中大多数甚至
不会映射到系统中存在的设备上
使用devfs
• 在Linux 2.4的内核里引入了devfs来解决
linux下设备文件管理的问题
• 在驱动程序中通过devfs_register()函数创
建设备文件系统的节点
• 系统启动的时候mount设备文件系统
• 所有需要的设备节点都由内核自动管理。
/dev目录下只有挂载的设备