基于ARM的嵌入式Linux移植真实体验(3――操作系统
宋宝华[email]21cnbao@21cn.com[/email] 出处:dev.yesky.com
在笔者撰写的《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++) != '\0')
              /* 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 <linux/config.h>
#include <linux/linkage.h>
#include <asm/mach-types.h>
 
       .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] = '\0';
       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
        * we're 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。
本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120246,如需转载请自行联系原作者

基于ARM的嵌入式Linux移植真实体验(3)――操作系统相关推荐

  1. 基于ARM的嵌入式Linux移植真实体验(1)――基本概念

    基于 ARM 的嵌入式 Linux 移植真实体验( 1 ) ――基本概念 宋宝华   [email]21cnbao@21cn.com[/email] 出处:dev.yesky.com 1. 引言 AR ...

  2. 基于ARM的嵌入式Linux移植真实体验(4)――设备驱动

    基于 ARM 的嵌入式 Linux 移植真实体验( 4 ) ――设备驱动 宋宝华 [email]21cnbao@21cn.com[/email] 出处:dev.yesky.com 设备驱动程序是操作系 ...

  3. 基于ARM的嵌入式Linux移植真实体验1-基本概念

    1.引言 ARM是Advanced RISC Machines(高级精简指令系统处理器)的缩写,是ARM公司提供的一种微处理器知识产权(IP)核. ARM的应用已遍及工业控制.消费类电子产品.通信系统 ...

  4. linux 挂iscisc存储,基于arm的嵌入式linux操作系统的移植研究-通信与信息系统专业论文.docx...

    基于arm的嵌入式linux操作系统的移植研究-通信与信息系统专业论文 Classified Index: TP316.8 U.D.C: 621.38 Dissertation for the Mas ...

  5. ARM的嵌入式Linux移植体验之基本概念

    作者: 宋宝华 引言 ARM是Advanced RISC Machines(高级精简指令系统处理器)的缩写,是ARM公司提供的一种微处理器知识产权(IP)核. ARM的应用已遍及工业控制.消费类电子产 ...

  6. ARM的嵌入式Linux移植体验之操作系统

    转自:[url]http://dev.yesky.com/153/2527653.shtml[/url] 06-08-13 08:00 作者: 宋宝华 出处: 天极开发 责任编辑:方舟 在笔者撰写的& ...

  7. 基于ARM的嵌入式linux 内核的裁剪与移植

    0 引言     微处理器的产生为价格低廉.结构小巧的CPU和外设的连 接提供了稳定可靠的硬件架构,这样,限制嵌入式系统发展的瓶颈就突出表现在了软件方面.尽管从八十年代末开始,已经陆续出现了一些嵌入式 ...

  8. 基于ARM的嵌入式Linux开发平台

    在进行嵌入式系统的开发时,首先面临的难题就是如何挑选一个适合自己的开发平台.ARM系列处理器是专门针对嵌入式设备设计的,是目前构造嵌入式系统硬件平台的首选.本文将介绍如何挑选适合实际需要的嵌入式开发平 ...

  9. 嵌入式linux汽车仪表,基于ARM和嵌入式Linux的汽车仪表盘研制

    摘要: 汽车仪表是驾驶员获取汽车状态信息的关键设备,对汽车的安全行驶起着重要的作用.近年来,随着计算机.微电子和各种现场总线通信技术的广泛应用,汽车电子技术得到了迅猛的发展,汽车仪表盘上显示的信息不断 ...

最新文章

  1. IT十八掌作业_java基础第六天_接口与适配器模式、多态、内部类
  2. 2020年第十一届蓝桥杯 - 省赛 - Python大学组 - C.跑步锻炼
  3. 性能测试工具curl-loader(linux)
  4. 对于数据库表排他更新的理解
  5. 3 帮助命令、用户管理、压缩
  6. CSS 状态管理,玩出花样了!
  7. settimeout需要清除吗_前端20个真正灵魂拷问,前端初级到中级你还需要这个!
  8. 储粮过冬?消息称中芯国际大举向设备、零件商囤货
  9. CMOS工作原理和概念
  10. μc/os-II原理简介(笔记)
  11. Autoit 打开文件的几种方式(run/Fileopen/ShellExecute)
  12. 【Java】使用JavaMail发送qq邮件
  13. Multi-attributed heterogeneous graph convolutional network for bot detection(SCI CCF B)
  14. 博物馆 库房rfid_博物馆RFID综合管理解决方案
  15. Codeforces1696 C. Fishingprince Plays With Array
  16. 基于FPGA的数据采集、通讯和存储系统设计(即FPGA+RTL8211千兆以太网+SD卡存储+RTC+Uart+AD7606数模转换+电流放大采集等硬件设计及程序验证)
  17. Google 地图切片URL地址解析
  18. 王半仙儿的日记-0003
  19. springcloud:微服务涉及哪些技术、有哪些核心组件(二)
  20. vissim跟驰模型_VISSIM,PARAMICS,TSIS仿真软件对比分析 -

热门文章

  1. java中获取路径_java中获取路径的几种基本的方法
  2. python函数打印乘法表_Python中内置函数来打印数学乘法表
  3. [dhtmlxGantt]浮动Milestone显示
  4. Spring+Hibernate 零散知识点
  5. 计算机教育调查报告,关于计算机类的教育实习调查报告
  6. abap中读取excel中不同的sheet数据_Python 如何将数据写入Excel的不同或同一个工作簿中...
  7. 用WSL安装Docker镜像
  8. html让后端删除数据库,Chrome在请求之间删除WebSQL数据库
  9. python选择排序_Python实现选择排序
  10. php二分查找例题,PHP实现的二分查找算法实例分析