您的位置 首页 知识

根据ARM的嵌入式Linux移植实在体会(3)――操作系统

在笔者撰写的《C语言嵌入式系统编程修炼之道》一文中,主要陈诉的软件架构是单任务无操作系统平台的,而本文的侧重点则在于讲述操作系统嵌…

在笔者编撰的《C言语嵌入式体系编程修炼之道》一文中,首要报告的软件架构是单使命无操作体系渠道的,而本文的侧重点则在于叙述操作体系嵌入的软件架构,二者的差异如下图:

嵌入式操作体系并不总是有必要的,由于程序完全能够在裸板上运转。尽管如此,但关于杂乱的体系,为使其具有使命办理、定时器办理、存储器办理、资源办理、作业办理、体系办理、音讯办理、队伍办理和中止处理的才能,供给多使命处理,更好的分配体系资源的功用,很有必要针对特定的硬件渠道和实践运用移植操作体系。鉴于Linux的源代码开放性,它成为嵌入式操作体系范畴的很好挑选。国内外许多闻名大学、公司、研讨机构都参加了嵌入式Linux的研讨队伍,推出了一些闻名的版别:
Ø RT-Linux供给了一个精巧的实时内核,把规范的Linux中心作为实时中心的一个进程同用户的实时进程一同调度。RT-Linux已成功地运用于航天飞机的空间数据收集、科学仪器测控和电影特技图画处理等广泛的运用范畴。如NASA(美国国家宇航局)将装有RT-Linux的设备放在飞机上,以丈量Georage咫风的风速;
Ø uCLinux(Micro-Control-Linux,u表明Micro,C表明Control)去掉了MMU(内存办理)功用,运用于没有虚拟内存办理的微处理器/微操控器,它现已被成功地移植到了许多渠道上。
本章触及的mizi-linux由韩国mizi公司依据Linux 2.4内核移植而来,支撑S3C2410A处理器。
1.Linux内核要害
和其他操作体系相同,Linux包括进程调度与进程间通讯(IPC)、内存办理(MMU)、虚拟文件体系(VFS)、网络接口等,下图给出了Linux的组成及其联系:

Linux内核源代码包括多个目录:
(1)arch:包括硬件特定的内核代码,如arm、mips、i386等;
(2)drivers:包括硬件驱动代码,如char、cdrom、scsi、mtd等;
(3)include:通用头文件及针对不同渠道特定的头文件,如asm-i386、asm-arm等;
(4)init:内核初始化代码;
(5)ipc:进程间通讯代码;
(6)kernel:内核中心代码;
(7)mm:内存办理代码;
(8)net:与网络协议栈相关的代码,如ipv4、ipv6、ethernet等;
(9)fs:文件体系相关代码,如nfs、vfat等;
(10)lib:库文件,与渠道无关的strlen、strcpy等,如在string.c中包括:
char * strcpy(char * dest,const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != )
/* nothing */;
return tmp;
}
(11)Documentation:文档。
在Linux内核的完结中,有一些数据结构运用十分频频,对研读内核的人来说至为要害,它们是:
1.task_struct
Linux内核运用task_struct数据结构代表一个进程,用task_struct指针构成一个task数组。当树立新进程的时分,Linux为新的进程分配一个task_struct结构,然后将指针保存在task数组中。调度程序维护current指针,它指向当时正在运转的进程。
2.mm_struct
每个进程的虚拟内存由mm_struct结构代表。该结构中包括了一组指向vm-area_struct结构的指针,vm-area_struct结构描绘了虚拟内存的一个区域。
3.inode
Linux虚拟文件体系中的文件、目录等均由对应的索引节点(inode)代表。
2.Linux移植项目
mizi-linux现已依据Linux 2.4内核针对S3C2410A这一芯片进行了有针对性的移植作业,包括:
(1)修正根目录下的Makefile文件
a.指定方针渠道为ARM
#ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
ARCH := arm
b.指定穿插编译器:
CROSS_COMPILE = arm-linux-
(2)修正arch目录中的文件
依据本章第一节可知,Linux的arch目录寄存硬件相关的内核代码,因而,在Linux内核中添加对S3C2410的支撑,最首要便是要修正arch目录中的文件。
a.在arch/arm/Makefile文件中参加:
ifeq ($(CONFIG_ARCH_S3C2410),y)
TEXTADDR = 0xC0008000
MACHINE = s3c2410
Endif
b.在arch\arm\config.in文件中参加:
if [ “$CONFIG_ARCH_S3C2410” = “y” ]; then
comment S3C2410 Implementation
dep_bool SMDK (MERI TECH BOARD) CONFIG_S3C2410_SMDK $CONFIG_ARCH_S3C2410
dep_bool change AIJI CONFIG_SMDK_AIJI
dep_tristate S3C2410 USB function support CONFIG_S3C2410_USB $CONFIG_ARCH_S3C2100
dep_tristate Support for S3C2410 USB character device emulation CONFIG_S3C2410_USB_CHAR $CONFIG_S3C2410_USB
fi # /* CONFIG_ARCH_S3C2410 */
arch\arm\config.in文件还有几处针对S3C2410的修正。
c.在arch/arm/boot/Makefile文件中参加:
ifeq ($(CONFIG_ARCH_S3C2410),y)
ZTEXTADDR = 0x30008000
ZRELADDR = 0x30008000
endif
d.在linux/arch/arm/boot/compressed/Makefile文件中参加:
ifeq ($(CONFIG_ARCH_S3C2410),y)
OBJS += head-s3c2410.o
endif
参加的结果是head-s3c2410.S文件被编译为head-s3c2410.o。
e.参加arch\arm\boot\compressed\ head-s3c2410.S文件
#include
#include
#include
.section “.start”, #alloc, #execinstr
__S3C2410_start:
@ Preserve r8/r7 i.e. kernel entry values
@ What is it?
@ Nandy
@ Data cache, Intstruction cache, MMU might be active.
@ Be sure to flush kernel binary out of the cache,
@ whatever state it is, before it is turned off.
@ This is done by fetching through currently executed
@ memory to be sure we hit the same cache
bic r2, pc, #0x1f
add r3, r2, #0x4000 @ 16 kb is quite enough…
1: ldr r0, [r2], #32
teq r2, r3
bne 1b
mcr p15, 0, r0, c7, c10, 4 @ drain WB
mcr p15, 0, r0, c7, c7, 0 @ flush I & D caches
#if 0
@ disabling MMU and caches
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, r0, #0x05 @ disable D cache and MMU
bic r0, r0, #1000 @ disable I cache
mcr p15, 0, r0, c1, c0, 0
#endif
/*
* Pause for a short time so that we give enough time
* for the host to start a terminal up.
*/
mov r0, #0x00200000
1: subs r0, r0, #1
bne 1b
该文件中的汇编代码完结S3C2410特定硬件相关的初始化。
f.在arch\arm\def-configs目录中添加配置文件
g.在arch\arm\kernel\Makefile中添加对S3C2410的支撑
no-irq-arch := $(CONFIG_ARCH_INTEGRATOR) $(CONFIG_ARCH_CLPS711X) \
$(CONFIG_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) \
$(CONFIG_ARCH_SA1100) $(CONFIG_ARCH_CAMELOT) \
$(CONFIG_ARCH_S3C2400) $(CONFIG_ARCH_S3C2410) \
$(CONFIG_ARCH_MX1ADS) $(CONFIG_ARCH_PXA)
obj-$(CONFIG_MIZI) += event.o
obj-$(CONFIG_APM) += apm2.o
h.修正arch/arm/kernel/debug-armv.S文件,在恰当的方位添加如下关于S3C2410的代码:
#elif defined(CONFIG_ARCH_S3C2410)
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled ?
moveq \rx, #0x50000000 @ physical base address
movne \rx, #0xf0000000 @ virtual address
.endm
.macro senduart,rd,rx
str \rd, [\rx, #0x20] @ UTXH
.endm
.macro waituart,rd,rx
.endm
.macro busyuart,rd,rx
1001: ldr \rd, [\rx, #0x10] @ read UTRSTAT
tst \rd, #1 << 2 @ TX_EMPTY ?
beq 1001b
.endm
i.修正arch/arm/kernel/setup.c文件
此文件中的setup_arch十分要害,用来完结与体系结构相关的初始化:
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = NULL;
struct machine_desc *mdesc;
char *from = default_command_line;
ROOT_DEV = MKDEV(0, 255);
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup(“s”);
if (mdesc->param_offset)
tags = phys_to_virt(mdesc->param_offset);
/*
* Do the machine-specific fixups before we parse the
* parameters or tags.
*/
if (mdesc->fixup)
mdesc->fixup(mdesc, (struct param_struct *)tags,
&from, &meminfo);
/*
* If we have the old style parameters, convert them to
* a tag list before.
*/
if (tags && tags->hdr.tag != ATAG_CORE)
convert_to_tag_list((struct param_struct *)tags,
meminfo.nr_banks == 0);
if (tags && tags->hdr.tag == ATAG_CORE)
parse_tags(tags);
if (meminfo.nr_banks == 0) {
meminfo.nr_banks = 1;
meminfo.bank[0].start = PHYS_OFFSET;
meminfo.bank[0].size = MEM_SIZE;
}
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = ;
parse_cmdline(&meminfo, cmdline_p, from);
bootmem_init(&meminfo);
paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc);
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}
j.修正arch/arm/mm/mm-armv.c文件(arch/arm/mm/目录中的文件完结与ARM相关的MMU处理)
修正
init_maps->bufferable = 0;
init_maps->bufferable = 1;
要垂手可得地进行上述马拉松式的内核移植作业并非一件轻松的作业,需求对Linux内核有很好的把握,一起把握硬件特定的常识和相关的汇编。幸而mizi公司的开发者们现已合力为咱们完结了上述作业,这使得小弟们在将mizi-linux移植到本身开发的电路板的过程中只需求关怀如下几点:
(1)内核初始化:Linux内核的进口点是start_kernel()函数。它初始化内核的其他部分,包括捕获,IRQ通道,调度,设备驱动,标定推迟循环,最重要的是能够fork“init”进程,以发动整个多使命环境。
咱们能够在init中加上一些特定的内容。
(2)设备驱动:设备驱动占有了Linux内核很大部分。同其他操作体系相同,设备驱动为它们所操控的硬件设备和操作体系供给接口。
本文第四章将独自解说驱动程序的编写办法。
(3)文件体系:Linux最重要的特性之一便是对多种文件体系的支撑。这种特性使得Linux很容易地同其他操作体系共存。文件体系的概念使得用户能够检查存储设备上的文件和途径而无须考虑实践物理设备的文件体系类型。Linux通明的支撑许多不同的文件体系,将各种装置的文件和文件体系以一个完好的虚拟文件体系的方法出现给用户。
咱们能够在K9S1208 NAND FLASH上移植cramfs、jfss2、yaffs等FLASH文件体系。
3. init进程
在init函数中“加料”,能够使得Linux发动的时分做点什么,例如广州友善之臂公司的demo板在其间参加了公司信息:
static int init(void * unused)
{
lock_kernel();
do_basic_setup();
prepare_namespace();
/*
* Ok, we have completed the initial bootup, and
* were essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
free_initmem();
unlock_kernel();
if (open(“/dev/console”, O_RDWR, 0) < 0)
printk(“Warning: unable to open an initial console.\n”);
(void) dup(0);
(void) dup(0);
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
printk(“========================================\n”);
printk(“= Friendly-ARM Tech. Ltd. =\n”);
printk(“= [url]http://www.arm9.net [/url] =\n”);
printk(“= [url]http://www.arm9.com.cn [/url] =\n”);
printk(“========================================\n”);
if (execute_command)
execve(execute_command,argv_init,envp_init);
execve(“/sbin/init”,argv_init,envp_init);
execve(“/etc/init”,argv_init,envp_init);
execve(“/bin/init”,argv_init,envp_init);
execve(“/bin/sh”,argv_init,envp_init);
panic(“No init found. Try passing init= option to kernel.”);
}
这样在Linux的发动过程中,会额外地输出:
========================================
= Friendly-ARM Tech. Ltd. =
= [url]http://www.arm9.net [/url] =
= [url]http://www.arm9.com.cn [/url] =
========================================
4.文件体系移植
文件体系是根据被区分的存储设备上的逻辑上单位上的一种界说文件的命名、存储、安排及取出的办法。假如一个Linux没有根文件体系,它是不能被正确的发动的。因而,咱们需求为Linux创立根文件体系,咱们将其创立在K9S1208 NAND FLASH上。
Linux的根文件体系或许包括如下目录(或更多的目录):
(1)/bin (binary):包括着一切的规范指令和运用程序;
(2)/dev (device):包括外设的文件接口,在Linux下,文件和设备选用同种地办法拜访的,体系上的每个设备都在/dev里有一个对应的设备文件;
(3)/etc (etcetera):这个目录包括着体系设置文件和其他的体系文件,例如/etc/fstab(file system table)记录了发动时要mount 的filesystem;
(4)/home:寄存用户主目录;
(5)/lib(library):寄存体系最根本的库文件;
(6)/mnt:用户暂时挂载文件体系的当地;
(7)/proc:linux供给的一个虚拟体系,体系发动时在内存中发生,用户能够直接经过拜访这些文件来取得体系信息;
(8)/root:超级用户主目录;
(9)/sbin:这个目录寄存着体系办理程序,如fsck、mount等;
(10)/tmp(temporary):寄存不同的程序履行时发生的暂时文件;
(11)/usr(user):寄存用户运用程序和文件。
选用BusyBox是缩小根文件体系的好办法,由于其间供给了体系的许多根本指令可是其体积很小。众所周知,瑞士军刀以其细巧简便、功用很多而闻名世界,成为各国武士的必备东西,并广泛运用于民间,而BusyBox也被称为嵌入式Linux范畴的“瑞士军刀”。
此地址能够下载BusyBox:[url]http://www.busybox.net[/url],当时最新版别为1.1.3。编译好busybox后,将其放入/bin目录,若要运用其间的指令,只需求树立link,如:
ln -s ./busybox ls
ln -s ./busybox mkdir
4.1 cramfs
在根文件体系中,为维护体系的根本设置不被更改,能够选用cramfs格局,它是一种只读的闪存文件体系。制造cramfs文件体系的办法为:树立一个目录,将需求放到文件体系的文件copy到这个目录,运转“mkcramfs 目录名 image名”就能够生成一个cramfs文件体系的image文件。例如假如目录名为rootfs,则正确的指令为:
mkcramfs rootfs rootfs.ramfs
咱们运用下面的指令能够mount生成的rootfs.ramfs文件,并检查其间的内容:
mount -o loop -t cramfs rootfs.ramfs /mount/point
此地址能够下载mkcramfs东西:[url]http://sourceforge.net/projects/cramfs/[/url]。
4.2 jfss2
关于cramfs闪存文件体系,假如没有ramfs的支撑则只能读,而选用jfss2(The Journalling Flash File System version 2)文件体系则能够直接在闪存中读、写数据。jfss2 是一个日志结构(log-structured)的文件体系,包括数据和原数据(meta-data)的节点在闪存上次序地存储。jfss2记录了每个擦写块的擦写次数,当闪存上各个擦写块的擦写次数的距离超越某个预订的阀值,开端进行磨损平衡的调整。调整的战略是,在废物收回时将擦写次数小的擦写块上的数据迁移到擦写次数大的擦写块上以到达磨损平衡的意图。
与mkcramfs相似,相同有一个mkfs.jffs2东西能够将一个目录制造为jffs2文件体系。假定把/bin目录制造为jffs2文件体系,需求运转的指令为:
mkfs.jffs2 -d /bin -o jffs2.img
4.3 yaffs
yaffs 是一种专门为嵌入式体系中常用的闪存设备设计的一种可读写的文件体系,它比jffs2 文件体系具有更快的发动速度,对闪存运用寿命有更好的维护机制。为使Linux支撑yaffs文件体系,咱们需求将其对应的驱动参加到内核中fs/yaffs/,并修正内核配置文件。运用咱们运用mkyaffs东西能够将NAND FLASH中的分区格局化为yaffs格局(如/bin/mkyaffs /dev/mtdblock/0指令能够将第1个MTD块设备分区格局化为yaffs),而运用mkyaffsimage(相似于mkcramfs、mkfs.jffs2)则能够将某目录生成为yaffs文件体系镜像。
嵌入式Linux还能够运用NFS(网络文件体系)经过以太网挂接根文件体系,这是一种常常用来作为调试运用的文件体系发动方法。经过网络挂接的根文件体系,能够在主机上生成ARM 穿插编译版别的方针文件或二进制可履行文件,然后就能够直接装载或履行它,而不必频频地写入flash。
选用不同的文件体系发动时,要注意经过第二章介绍的BootLoader修正发动参数,如广州友善之臂的demo供给如下三种发动方法:
(1)从cramfs挂接根文件体系:root=/dev/bon/2();
(2)从移植的yaffs挂接根文件体系:root=/dev/mtdblock/0;
(3)从以太网挂接根文件体系:root=/dev/nfs。
5.小结
本章介绍了嵌入式Linux的布景、移植项目、init进程修正和文件体系移植,经过这些过程,咱们能够在嵌入式体系上发动一个根本的Linux。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/276991.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部