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

简介:
基于 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,如需转载请自行联系原作者



相关文章
|
22天前
|
安全 Linux 数据安全/隐私保护
Vanilla OS:下一代安全 Linux 发行版
【10月更文挑战第30天】
42 0
Vanilla OS:下一代安全 Linux 发行版
|
2月前
|
人工智能 分布式计算 大数据
Linux操作系统:开源力量的崛起与影响###
一场技术革命的火种,如何燎原? 本文将带您深入探索Linux操作系统的诞生背景、核心特性及其对现代科技世界的深远影响。从1991年芬兰学生Linus Torvalds的一个小众项目,到如今成为支撑全球无数服务器、超级计算机及物联网设备的基石,Linux的发展既是一部技术创新史,也是开源文化胜利的见证。通过剖析其设计哲学、安全性、灵活性等关键优势,结合实例展示Linux在云计算、大数据处理等领域的广泛应用,本文旨在揭示Linux为何能在众多操作系统中脱颖而出,以及它如何塑造了我们今天的数字生活。 ###
|
2月前
|
安全 Linux 编译器
探索Linux内核的奥秘:从零构建操作系统####
本文旨在通过深入浅出的方式,带领读者踏上一段从零开始构建简化版Linux操作系统的旅程。我们将避开复杂的技术细节,以通俗易懂的语言,逐步揭开Linux内核的神秘面纱,探讨其工作原理、核心组件及如何通过实践加深理解。这既是一次对操作系统原理的深刻洞察,也是一场激发创新思维与实践能力的冒险。 ####
|
2天前
|
人工智能 安全 Linux
Linux操作系统的演变与未来趋势###
本文深入探讨了Linux操作系统从诞生至今的发展历程,分析了其开源模式对技术创新和IT行业的影响,并展望了Linux在未来技术生态中的角色。通过历史回顾、现状分析和未来预测,本文旨在为读者提供一个关于Linux操作系统全面而深入的视角。 ###
|
2天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
11 1
|
15天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
44 9
|
15天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
33 2
|
19天前
|
缓存 网络协议 Linux
Linux操作系统内核
Linux操作系统内核 1、进程管理: 进程调度 进程创建与销毁 进程间通信 2、内存管理: 内存分配与回收 虚拟内存管理 缓存管理 3、驱动管理: 设备驱动程序接口 硬件抽象层 中断处理 4、文件和网络管理: 文件系统管理 网络协议栈 网络安全及防火墙管理
38 4
|
18天前
|
安全 网络协议 Linux
Linux操作系统的内核升级与优化策略####
【10月更文挑战第29天】 本文深入探讨了Linux操作系统内核升级的重要性,并详细阐述了一系列优化策略,旨在帮助系统管理员和高级用户提升系统的稳定性、安全性和性能。通过实际案例分析,我们展示了如何安全有效地进行内核升级,以及如何利用调优技术充分发挥Linux系统的潜力。 ####
41 1
|
21天前
|
物联网 Linux 云计算
Linux操作系统的演变与未来趋势####
【10月更文挑战第29天】 本文深入探讨了Linux操作系统从诞生至今的发展历程,分析了其在服务器、桌面及嵌入式系统领域的应用现状,并展望了云计算、物联网时代下Linux的未来趋势。通过回顾历史、剖析现状、预测未来,本文旨在为读者提供一个全面而深入的视角,以理解Linux在当今技术生态中的重要地位及其发展潜力。 ####
下一篇
无影云桌面