只要活着,就要有目标。
2013年(109)
分类: 嵌入式
2013-06-17 15:25:59
关于如何构建嵌入式linux系统?网老师做出解答: 嵌入式系统目前主要有:Windows CE、VxWorks、QNX等,它们都具较好的实时性,系统可靠性,任务处理随机性等优点。但是它们的价格普遍偏高,很多开发商承受不起。因而,Linux操作系统成为嵌入式操作系统的首选,原因如下:
在精简内核在编译内核之前,首先要明确需要那些驱动和模块,然后只选择需要的驱动和模块,例如,如果系统不需要网络支持,则可以去掉网络模块。内核一般是以压缩方式存放的,在系统启动时会自行解压。内核都是常驻内存的,当需要调用应用程序时,再把需要的程序从磁盘 调入内存运行。
构建内核常用的命令包括:
◆ make config:内核配置,调用 ./scripts/Configure 按照 arch/i386/config.in 来进行配置。
◆ make dep:寻找依赖关系。
◆ make clean:清除以前构建内核所产生的所有目标文件、模块文件、以及一些临时文件等。
◆ make:构核,通过各目录的Makefile 文件将会在各个目录下产生许多目标文件。如果内核没有错误,将产生文件vmlinux,这就是构建的内核。
◆ make zImage:在make 的基础上产生压缩的内核映象文件。/arch/$(ARCH)/boot/zImage 以及在 ./arch/$(ARCH)/boot/compresed/目录下产生临时文件。
◆ make bzImage:在make 的基础上产生压缩比例更大的内核映象文件。/arch/$(ARCH)/boot/bzImage 以及在 ./arch/$(ARCH)/boot/compresed/目录下产生临时文件。
◆ make modules:编译模块文件,在make config 时所配置的所有模块将在这时编译,形成模块目标文件,并把这些目标文件存放在modules 目录中。
◆ make
modules_install:把上面编译好的模块目标文件放置在目录 ./lib/modules/$KERNEL_VERSION / 中。上面的编译内核是在没有改变源代码的情况下实现的,如果觉得源代码提供的功能在某些方面不能满足要求,就要修改源代码了。源代码中主要有以下几个关键部分:有关进程管理的task_struct
结构,这个结构几乎包括了与进程有关的所有文件内容,还有任务队列、时钟管理和中断管理,各种进程间的通信机制,内存管理中各种内存分配函数的实现,虚拟文件系统。
系统启动
引导启动程序主要包括以下三个文件:bootsect.s,head.s和setup.s
这三个文件虽然都是汇编程序,但确使用了两种语法格式。 bootsect.s和setup.s 采用了近似于Intel的汇编语言语法,需要使用Intel 8086
汇编器和连接器 as86和ld86。 head.s 则使用了GUN的汇编格式,并且运行在保护模式下,需要用GUN的as 进行编译。这是一种AT&T语法的汇编语言格式。
Bootsect.s代码时磁盘引导块程序,驻留在磁盘的第一个扇区中,在PC机加电ROM-BIOS自检后,引导扇区由BIOS加载到内存0x7C00 处,然后将自己移动到内存0x90000处。该程序的主要作用是首先将setup模块(由setup.s编译的)从磁盘加载到内存紧接着bootsect
的后面位置(0x90200),然后利用BIOS中断0x13取磁盘参数表中当前启动引导盘的参数,接着在屏幕上显示 “Loading system…”字符串。再将system模块从磁盘上加载到内存0x10000开始的地方。随后确定根文件系统的设备号。
Setup程序的作用主要是利用ROM-BIOS中断读取机器系统数据,并将这些数据保存到0x90000开始的位置(覆盖了bootsect程序所在的地方)。然后setup程序将system模块从0x10000整块向下移动到内存绝对地址0x0000处,接下来加载中断描述符表寄存器(idtr)和全局描述表寄存器(gdtr)。开启A20地址线,重新设置两个中断控制芯片8259A,将硬件中断号重新设置为0x20-0x2f。最后设置CPU的控制寄存器CR0(也称机器状态字),从而进入32位保护模式进行,并跳转到位于system模块最前面部分的head.s程序继续运行。
Head.s程序在被编译后,会被连接成system模块的最前面开始部分,即头部(head)程序。从这里开始,内核完全都是在保护模式下运行了。这段程序实际上处于内存绝对地址0处开始的地方。这个程序功能比较单一,首先是加载各个数据段寄存器,重新设置中断描述符表idt,共256项。然后重新设置中断描述符表gdt,接下来检测A20地址线是不是开启了,再检测PC机是否含有数学协处理器芯片,然后设置管理内存的分页处理机制,最后利用返回指令将预先放置在堆栈中的/init/main.c程序的入口地址弹出,去运行main()内核初始化程序。
设备驱动程序
设备驱动程序在Linux内核中扮演着特殊的角色,它们是一个个独立的“黑盒子”,使某个特定的硬件响应一个定义良好的内部编程接口,同时完全隐藏了设备的工作细节。用户操作通过一组标准化的调用完成,而这些调用是和特定的驱动程序无关的。设备驱动程序提供的功能是同外设进行数据传送。设备包括三种类型:字符设备、块设备和网络接口。每个模块通常实现其中一种类型,相应地,模块可分为字符模块(char
module)、块模块(block module)和网络模块(network module)三种。然而这种分类方式并不是十分严格,程序员可以构建一个大的模块,在其中实现不同类型的设备驱动程序。三种类型的设备如下:
字符模块
字符设备是能够象字节流(比如文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少需要实现open、close、 read和write的系统调用。字符终端(dev/console)和串口(/dev/ttySO以及设备类型)就是字符设备的两个例子,它们能够用流抽象很好地表示。
块设备和字符设备一样,块设备也是通过/dev目录下的文件系统节点被访问的。块设备(例如磁盘)上能够容纳文件系统。在大多数Unix系统中,块设备包括整数个块,而每块包含1KB或2的几次幂字节的数据。Linux允许应用程序如字符设备那样读写块设备,可以一次传递任意多字节的数据。因而,块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核和驱动程序的接口不同。块设备的接口必须支持挂装(mount)文件系统。
网络接口
任何网络事务都要经过一个网络接口,即一个能够和其它主机交换数据的设备。通常接口是个硬件设备,但也可能是个纯软件设备,比如回环(loopback)接口。网络接口由内核中的网络子系统驱动,负责发送和接收数据包,它必须了解每项事务是如何映射到实际传送的数据包的。尽管Telnet和FTP连接都是面向流的,它们都使用了同一个设备,但这个设备看到的只是数据包,而不是独立的流。
在Linux里,除了直接修改系统内核的源代码,把设备驱动程序加进内核以外,还可以把设备驱动程序作为可加载的模块,由系统管理员动态的加载和卸载,使之成为内核的一部分。Linux的模块可以用C语言编写,用gcc编译成目标文件(不进行链接,作为*.o文件存在),为此需要在gcc命令行里加上-c 的参数。由于在不链接时,gcc只允许一个输入文件,因此一个模块的所有部分都必须在一个文件里实现。编译好的模块*.o放在 / lib / modules / xxxx/misc下(xxxx表示内核版本),然后用depmod -a使此模块成为可加载模块。模块用insmod命令加载,用rmmod命令来卸载,并可以用lsmod命令来察看所有已经加载的模块的状态。编写模块时必须提供两个函数,一个是init_module(void),供insmod在加载的时候自动调用,负责进行设备驱动程序的初始化工作。Init_module返回0表示初始化成功,返回负数表示失败。另一个函数是void cleanup_module(void),载模块卸载时调用,负责进行设备驱动程序的清除工作。在成功的向系统注册了设备驱动程序后(调用register_chrdev成功后),就可以用mknod命令来把设备映射成一个特别文件,其它程序社用这个设备的时候,只要对此特别文件进行操作就可以了。