??xml version="1.0" encoding="utf-8" standalone="yes"?>一级女性全黄久久生活片免费,亚洲国产精品久久久久婷婷老年,久久精品国产精品亚洲人人http://www.shnenglu.com/yishanhante/articles/66774.htmljayjayWed, 12 Nov 2008 16:09:00 GMThttp://www.shnenglu.com/yishanhante/articles/66774.htmlhttp://www.shnenglu.com/yishanhante/comments/66774.htmlhttp://www.shnenglu.com/yishanhante/articles/66774.html#Feedback0http://www.shnenglu.com/yishanhante/comments/commentRss/66774.htmlhttp://www.shnenglu.com/yishanhante/services/trackbacks/66774.html 
本文详细Cl了Z嵌入式系l中?OS 启动加蝲E序 ―?Boot Loader 的概cY件设计的主要d以及l构框架{内宏V?br>
一、引a

在专用的嵌入式板子运?GNU/Linux pȝ已经变得来流行。一个嵌入式 Linux pȝ从Y件的角度看通常可以分ؓ(f)四个层次Q?
  • 引导加蝲E序。包括固化在Zg(firmware)中的 boot 代码(可?Q和 Boot Loader 两大部分?
  • Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数?
  • 文gpȝ。包括根文gpȝ和徏立于 Flash 内存讑֤之上文gpȝ。通常?ram disk 来作?root fs?
  • 用户应用E序。特定于用户的应用程序。有时在用户应用E序和内核层之间可能q会(x)包括一个嵌入式囑Ş用户界面。常用的嵌入?GUI 有:(x)MicroWindows ?MiniGUI 懂?

引导加蝲E序是系l加?sh)后q行的第一DY件代码。回忆一?PC 的体pȝ构我们可以知道,PC Z的引导加载程序由 BIOS(其本质就是一D固件程?和位于硬?MBR 中的 OS Boot LoaderQ比如,LILO ?GRUB {)一L(fng)成。BIOS 在完成硬件检和资源分配后,硬?MBR 中的 Boot Loader dpȝ?RAM 中,然后控制权交给 OS Boot Loader。Boot Loader 的主要运行Q务就是将内核映象从硬盘上d RAM 中,然后跌{到内核的入口点去q行Q也卛_始启动操作系l?

而在嵌入式系l中Q通常q没有像 BIOS 那样的固件程序(注,有的嵌入?CPU 也会(x)内嵌一D늟的启动E序Q,因此整个pȝ的加载启动Q务就完全?Boot Loader 来完成。比如在一个基?ARM7TDMI core 的嵌入式pȝ中,pȝ在上甉|复位旉常都从地址 0x00000000 处开始执行,而在q个地址处安排的通常是pȝ?Boot Loader E序?

本文从 Boot Loader 的概cBoot Loader 的主要Q务、Boot Loader 的框架结构以?Boot Loader 的安装等四个斚w来讨论嵌入式pȝ?Boot Loader?

?/font>?/strong>Boot Loader 的概?br>
单地_Boot Loader 是在操作系l内核运行之前运行的一D小E序。通过q段程序,我们可以初始化硬件设备、徏立内存空间的映射图,从而将pȝ的Yg环境带到一个合适的状态,以便为最l调用操作系l内核准备好正确的环境?

通常QBoot Loader 是严重地依赖于硬件而实现的Q特别是在嵌入式世界。因此,在嵌入式世界里徏立一个通用?Boot Loader 几乎是不可能的。尽如此,我们仍然可以?Boot Loader 归纳Z些通用的概忉|Q以指导用户特定?Boot Loader 设计与实现?

1. Boot Loader 所支持?CPU 和嵌入式?br>
每种不同?CPU 体系l构都有不同?Boot Loader。有?Boot Loader 也支持多U体pȝ构的 CPUQ比?U-Boot 同时支?ARM 体系l构和MIPS 体系l构。除了依赖于 CPU 的体pȝ构外QBoot Loader 实际上也依赖于具体的嵌入式板U设备的配置。这也就是说Q对于两块不同的嵌入式板而言Q即使它们是Z同一U?CPU 而构建的Q要惌q行在一块板子上?Boot Loader E序也能q行在另一块板子上Q通常也都需要修?Boot Loader 的源E序?

2. Boot Loader 的安装媒介(Installation MediumQ?br>
pȝ加电(sh)或复位后Q所有的 CPU 通常都从某个?CPU 刉商预先安排的地址上取指o。比如,Z ARM7TDMI core ?CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。而基?CPU 构徏的嵌入式pȝ通常都有某种cd的固态存储设?比如QROM、EEPROM ?FLASH {?被映到q个预先安排的地址上。因此在pȝ加电(sh)后,CPU 首先执?Boot Loader E序?
下图1是一个同时装?Boot Loader、内核的启动参数、内核映像和Ҏ(gu)件系l映像的固态存储设备的典型I间分配l构图?

? 固态存储设备的典型I间分配l构

http://linux.chinaunix.net/mirror/www-128.ibm.com/developerworks/cn/linux/l-btloader/images/image001.gif



3. 用来控制 Boot Loader 的设备或机制

L和目标机之间一般通过串口建立q接QBoot Loader 软g在执行时通常?x)通过串口来进?I/OQ比如:(x)输出打印信息C口,从串口读取用h制字W等?

4. Boot Loader 的启动过E是单阶D(Single StageQ还是多阶段QMulti-StageQ?br>
通常多阶D늚 Boot Loader 能提供更为复杂的功能Q以及更好的可移植性。从固态存储设备上启动?Boot Loader 大多都是 2 阶段的启动过E,也即启动q程可以分ؓ(f) stage 1 ?stage 2 两部分。而至于在 stage 1 ?stage 2 具体完成哪些d在下面讨论?

5. Boot Loader 的操作模?(Operation Mode)

大多?Boot Loader 都包含两U不同的操作模式Q?启动加蝲"模式?下蝲"模式Q这U区别仅对于开发h员才有意义。但从最l用L(fng)角度看,Boot Loader 的作用就是用来加载操作系l,而ƈ不存在所谓的启动加蝲模式与下载工作模式的区别?

启动加蝲QBoot loadingQ模式:(x)q种模式也称?自主" QAutonomousQ模式。也?Boot Loader 从目标机上的某个固态存储设备上操作系l加载到 RAM 中运行,整个q程q没有用L(fng)介入。这U模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 昄必须工作在这U模式下?

下蝲QDownloadingQ模式:(x)在这U模式下Q目标机上的 Boot Loader 通过串口q接或网l连接等通信手段从主机(HostQ下载文Ӟ比如Q下载内核映像和Ҏ(gu)件系l映像等。从L下蝲的文仉常首先?Boot Loader 保存到目标机?RAM 中,然后再被 Boot Loader 写到目标Z的FLASH cd态存储设备中。Boot Loader 的这U模式通常在第一ơ安装内怸Ҏ(gu)件系l时被用;此外Q以后的pȝ更新也会(x)使用 Boot Loader 的这U工作模式。工作于q种模式下的 Boot Loader 通常都会(x)向它的终端用h供一个简单的命o行接口?

?Blob ?U-Boot {这样功能强大的 Boot Loader 通常同时支持q两U工作模式,而且允许用户在这两种工作模式之间q行切换。比如,Blob 在启动时处于正常的启动加载模式,但是它会(x)延时 10 U等待终端用h下Q意键而将 blob 切换C载模式。如果在 10 U内没有用户按键Q则 blob l箋启动 Linux 内核?

6. BootLoader 与主Z间进行文件传输所用的通信讑֤及协?br>
最常见的情况就是,目标Z?Boot Loader 通过串口与主Z间进行文件传输,传输协议通常?xmodemQymodemQzmodem 协议中的一U。但是,串口传输的速度是有限的Q因此通过以太|连接ƈ借助 TFTP 协议来下载文件是个更好的选择?
此外Q在论及q个话题ӞLҎ(gu)用的软g也要考虑。比如,在通过以太|连接和 TFTP 协议来下载文件时Q主机方必须有一个Y件用来的提供 TFTP 服务?

在讨Z BootLoader 的上q概念后Q下面我们来具体看看 BootLoader 的应该完成哪些Q务?

三、Boot Loader 的主要Q务与典型l构框架

在l本节的讨论之前Q首先我们做一个假定,那就是:(x)假定内核映像与根文gpȝ映像都被加蝲?RAM 中运行。之所以提样一个假讑։提是因ؓ(f)Q在嵌入式系l中内核映像与根文gpȝ映像也可以直接在 ROM ?Flash q样的固态存储设备中直接q行。但q种做法无疑是以q行速度的牺牲ؓ(f)代h(hun)的?

从操作系l的角度看,Boot Loader 的ȝ标就是正地调用内核来执行?

另外Q由?Boot Loader 的实C赖于 CPU 的体pȝ构,因此大多?Boot Loader 都分?stage1 ?stage2 两大部分。依赖于 CPU 体系l构的代码,比如讑֤初始化代码等Q通常都放?stage1 中,而且通常都用汇编语言来实玎ͼ以达到短精(zhn)的目的。?stage2 则通常用C语言来实玎ͼq样可以实现l复杂的功能Q而且代码?x)具有更好的可读性和可移植性?

Boot Loader ?stage1 通常包括以下步骤(以执行的先后序)Q?
  • g讑֤初始化?/font>
  • 为加?Boot Loader ?stage2 准备 RAM I间?/font>
  • 拯 Boot Loader ?stage2 ?RAM I间中?/font>
  • 讄好堆栈?/font>
  • 跌{?stage2 ?C 入口炏V?/font>

Boot Loader ?stage2 通常包括以下步骤(以执行的先后序)Q?
  • 初始化本阶段要用到的硬件设备?/font>
  • 系l内存映?memory map)?/font>
  • ?kernel 映像和根文gpȝ映像?flash 上读?RAM I间中?/font>
  • 为内核设|启动参数?/font>
  • 调用内核?/font>

3.1 Boot Loader ?stage1

3.1.1 基本的硬件初始化

q是 Boot Loader 一开始就执行的操作,其目的是?stage2 的执行以及随后的 kernel 的执行准备好一些基本的g环境。它通常包括以下步骤Q以执行的先后顺序)Q?

  • 屏蔽所有的中断。ؓ(f)中断提供服务通常?OS 讑֤驱动E序的责任,因此?Boot Loader 的执行全q程中可以不必响应Q何中断。中断屏蔽可以通过?CPU 的中断屏蔽寄存器或状态寄存器Q比?ARM ?CPSR 寄存器)来完成?
  • 讄 CPU 的速度和时钟频率?
  • RAM 初始化。包括正地讄pȝ的内存控制器的功能寄存器以及各内存库控制寄存器等?
  • 初始?LED。典型地Q通过 GPIO 来驱?LEDQ其目的是表明系l的状态是 OK q是 Error。如果板子上没有 LEDQ那么也可以通过初始?UART 向串口打?Boot Loader ?Logo 字符信息来完成这一炏V?
  • 关闭 CPU 内部指oQ数?cache?

3.1.2 为加?stage2 准备 RAM I间

Z获得更快的执行速度Q通常?stage2 加蝲?RAM I间中来执行Q因此必Mؓ(f)加蝲 Boot Loader ?stage2 准备好一D可用的 RAM I间范围?

׃ stage2 通常?C 语言执行代码Q因此在考虑I间大小Ӟ除了 stage2 可执行映象的大小外,q必L堆栈I间也考虑q来。此外,I间大小最好是 memory page 大小(通常?4KB)的倍数。一般而言Q?M ?RAM I间已经_了。具体的地址范围可以L安排Q比?blob 将它的 stage2 可执行映像安排到从系l?RAM 起始地址 0xc0200000 开始的 1M I间内执行。但是,?stage2 安排到整?RAM I间的最?1MB(也即(RamEnd-1MB) - RamEnd)是一U值得推荐的方法?

Z后面的叙q方便,q里把所安排?RAM I间范围的大记为:(x)stage2_size(字节)Q把起始地址和终止地址分别Cؓ(f)Qstage2_start ?stage2_end(q两个地址均以 4 字节边界寚w)。因此:(x)

stage2_endQstage2_startQstage2_size

另外Q还必须保所安排的地址范围的的确是可d?RAM I间Q因此,必须对你所安排的地址范围q行试。具体的试Ҏ(gu)可以采用cM?blob 的方法,也即Q以 memory page 试单位Q测试每?memory page 开始的两个字是否是可读写的。ؓ(f)了后面叙q的方便Q我们记q个算法ؓ(f)Qtest_mempageQ其具体步骤如下Q?

1Q?先保?memory page 一开始两个字的内宏V?
2Q?向这两个字中写入L的数字。比如:(x)向第一个字写入 0x55Q第 2 个字写入 0xaa?
3Q?然后Q立卛_q两个字的内容读回。显Ӟ我们d的内容应该分别是 0x55 ?0xaa。如果不是,则说明这?memory page 所占据的地址范围不是一D|效的 RAM I间?
4Q?再向q两个字中写入Q意的数字。比如:(x)向第一个字写入 0xaaQ第 2 个字中写?0x55?
5Q?然后Q立卛_q两个字的内容立卌回。显Ӟ我们d的内容应该分别是 0xaa ?0x55。如果不是,则说明这?memory page 所占据的地址范围不是一D|效的 RAM I间?
6Q?恢复q两个字的原始内宏V测试完毕?

Z得到一D干净?RAM I间范围Q我们也可以所安排?RAM I间范围q行清零操作?

3.1.3 拯 stage2 ?RAM ?/strong>

拯时要定两点Q?1) stage2 的可执行映象在固态存储设备的存放起始地址和终止地址Q?2) RAM I间的v始地址?

3.1.4 讄堆栈指针 sp

堆栈指针的设|是Z执行 C 语言代码作好准备。通常我们可以?sp 的D|ؓ(f)(stage2_end-4)Q也卛_ 3.1.2 节所安排的那?1MB ?RAM I间的最端(堆栈向下生长)?

此外Q在讄堆栈指针 sp 之前Q也可以关闭 led 灯,以提C用h们准备蟩转到 stage2?
l过上述q些执行步骤后,pȝ的物理内存布局应该如下?所C?

3.1.5 跌{?stage2 ?C 入口?/strong>

在上qC切都qA后,可以蟩转到 Boot Loader ?stage2 L行了。比如,?ARM pȝ中,q可以通过修改 PC 寄存器ؓ(f)合适的地址来实现?

? bootloader ?stage2 可执行映象刚被拷贝到 RAM I间时的pȝ内存布局

http://linux.chinaunix.net/mirror/www-128.ibm.com/developerworks/cn/linux/l-btloader/images/image002.gif



3.2 Boot Loader ?stage2

正如前面所_stage2 的代码通常?C 语言来实玎ͼ以便于实现更复杂的功能和取得更好的代码可L和可移植性。但是与普?C 语言应用E序不同的是Q在~译和链?boot loader q样的程序时Q我们不能?glibc 库中的Q何支持函数。其原因是显而易见的。这q我们带来一个问题,那就是从那里跌{q?main() 函数呢?直接?main() 函数的v始地址作ؓ(f)整个 stage2 执行映像的入口点或许是最直接的想法。但是这样做有两个缺点:(x)1)无法通过main() 函数传递函数参敎ͼ2)无法处理 main() 函数q回的情c一U更为y妙的Ҏ(gu)是利?trampoline(弹簧?的概c也卻I用汇~语a写一Dtrampoline 程序,q将q段 trampoline 程序来作ؓ(f) stage2 可执行映象的执行入口炏V然后我们可以在 trampoline 汇编程序中?CPU 跌{指o跛_ main() 函数中去执行Q而当 main() 函数q回ӞCPU 执行路径昄再次回到我们?trampoline E序。简而言之,q种Ҏ(gu)的思想是Q用q段 trampoline 程序来作ؓ(f) main() 函数的外部包?external wrapper)?

下面l出一个简单的 trampoline E序CZ(来自blob)Q?

.text

.globl _trampoline
_trampoline:
        bl        main
        /* if main ever returns we just call it again */
        b        _trampoline




可以看出Q当 main() 函数q回后,我们又用一条蟩转指令重新执?trampoline E序――当然也重新执?main() 函数Q这也就?trampoline(弹簧?一词的意思所在?

3.2.1初始化本阶段要用到的硬件设?/strong>

q通常包括Q(1Q初始化臛_一个串口,以便和终端用戯?I/O 输出信息Q(2Q初始化计时器等?
在初始化q些讑֤之前Q也可以重新?LED 灯点亮,以表明我们已l进?main() 函数执行?
讑֤初始化完成后Q可以输Z些打C息,E序名字字符丌Ӏ版本号{?

3.2.2 系l的内存映射Qmemory mapQ?/strong>

所谓内存映就是指在整?4GB 物理地址I间中有哪些地址范围被分配用来寻址pȝ?RAM 单元。比如,?SA-1100 CPU 中,?0xC000,0000 开始的 512M 地址I间被用作系l的 RAM 地址I间Q而在 Samsung S3C44B0X CPU 中,?0x0c00,0000 ?0x1000,0000 之间?64M 地址I间被用作系l的 RAM 地址I间。虽?CPU 通常预留Z大段_的地址I间l系l?RAMQ但是在搭徏具体的嵌入式pȝ时却不一定会(x)实现 CPU 预留的全?RAM 地址I间。也是_具体的嵌入式pȝ往往只把 CPU 预留的全?RAM 地址I间中的一部分映射?RAM 单元上,而让剩下的那部分预留 RAM 地址I间处于未用状态?׃上述q个事实Q因?Boot Loader ?stage2 必须在它惛_点什?(比如Q将存储?flash 上的内核映像d RAM I间? 之前整个系l的内存映射情况Q也卛_必须知道 CPU 预留的全?RAM 地址I间中的哪些被真正映到 RAM 地址单元Q哪些是处于 "unused" 状态的?/strong>

(1) 内存映射的描q?/strong>

可以用如下数据结构来描述 RAM 地址I间中的一D连l?continuous)的地址范围Q?br>

typedef struct memory_area_struct {
        u32 start; /* the base address of the memory region */
        u32 size; /* the byte number of the memory region */
        int used;
} memory_area_t;




q段 RAM 地址I间中的q箋地址范围可以处于两种状态之一Q?1)used=1Q则说明q段q箋的地址范围已被实现Q也即真正地被映到 RAM 单元上?2)used=0Q则说明q段q箋的地址范围q未被系l所实现Q而是处于未用状态?
Z上述 memory_area_t 数据l构Q整?CPU 预留?RAM 地址I间可以用一?memory_area_t cd的数l来表示Q如下所C:(x)


memory_area_t memory_map[NUM_MEM_AREAS] = {
        [0 ... (NUM_MEM_AREAS - 1)] = {
                .start = 0,
                .size = 0,
                .used = 0
        },
};





(2) 内存映射的检?/strong>

下面我们l出一个可用来整?RAM 地址I间内存映射情况的简单而有效的法Q?br>


/* 数组初始?*/
for(i = 0; i < NUM_MEM_AREAS; i++)
        memory_map.used = 0;

/* first write a 0 to all memory locations */
for(addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE)
        * (u32 *)addr = 0;

for(i = 0, addr = MEM_START; addr < MEM_END; addr += PAGE_SIZE) {
     /*
      * 从基地址 MEM_START+i*PAGE_SIZE 开?大小?br>* PAGE_SIZE 的地址I间是否是有效的RAM地址I间?br>      */
     调用3.1.2节中的算法test_mempage()Q?br>     if ( current memory page isnot a valid ram page) {
                /* no RAM here */
                if(memory_map.used )
                        i++;
                continue;
        }
        
        /*
         * 当前已l是一个被映射?RAM 的有效地址范围
         * 但是q要看看当前|否只?4GB 地址I间中某个地址늚别名Q?br>         */
        if(* (u32 *)addr != 0) { /* alias? */
                /* q个内存| 4GB 地址I间中某个地址늚别名 */
                if ( memory_map.used )
                        i++;
                continue;
        }
        
        /*
         * 当前已l是一个被映射?RAM 的有效地址范围
         * 而且它也不是 4GB 地址I间中某个地址늚别名?br>         */
        if (memory_map.used == 0) {
                memory_map.start = addr;
                memory_map.size = PAGE_SIZE;
                memory_map.used = 1;
        } else {
                memory_map.size += PAGE_SIZE;
        }
} /* end of for (…) */





在用上述法完pȝ的内存映情况后QBoot Loader 也可以将内存映射的详l信息打印到串口?br>
3.2.3 加蝲内核映像和根文gpȝ映像

(1) 规划内存占用的布局

q里包括两个斚wQ?1)内核映像所占用的内存范_Q?Q根文gpȝ所占用的内存范围。在规划内存占用的布局Ӟ主要考虑基地址和映像的大小两个斚w?

对于内核映像Q一般将其拷贝到?MEM_STARTQ?x8000) q个基地址开始的大约1MB大小的内存范围内(嵌入?Linux 的内怸般都不操q?1MB)。ؓ(f)什么要把从 MEM_START ?MEM_STARTQ?x8000 q段 32KB 大小的内存空出来呢?q是因ؓ(f) Linux 内核要在q段内存中放|一些全局数据l构Q如Q启动参数和内核表{信息?

而对于根文gpȝ映像Q则一般将其拷贝到 MEM_START+0x0010,0000 开始的地方。如果用 Ramdisk 作ؓ(f)Ҏ(gu)件系l映像,则其解压后的大小一般是1MB?

Q?Q从 Flash 上拷?/strong>

׃?ARM q样的嵌入式 CPU 通常都是在统一的内存地址I间中寻址 Flash {固态存储设备的Q因此从 Flash 上读取数据与?RAM 单元中读取数据ƈ没有什么不同。用一个简单的循环可以完成从 Flash 讑֤上拷贝映像的工作Q?

while(count) {
*dest++ = *src++; /* they are all aligned with word boundary */
count -= 4; /* byte number */
};

3.2.4 讄内核的启动参?/strong>

应该_在将内核映像和根文gpȝ映像拯?RAM I间中后Q就可以准备启动 Linux 内核了。但是在调用内核之前Q应该作一步准备工作,卻I(x)讄 Linux 内核的启动参数?

Linux 2.4.x 以后的内栔R期望以标记列?tagged list)的Ş式来传递启动参数。启动参数标记列表以标记 ATAG_CORE 开始,以标?ATAG_NONE l束。每个标记由标识被传递参数的 tag_header l构以及随后的参数值数据结构来l成。数据结?tag ?tag_header 定义?Linux 内核源码的include/asm/setup.h 头文件中Q?


/* The list ends with an ATAG_NONE node. */
#define ATAG_NONE        0x00000000

struct tag_header {
        u32 size; /* 注意Q这里size是字Cؓ(f)单位?*/
        u32 tag;
};
……
struct tag {
        struct tag_header hdr;
        union {
                struct tag_core                core;
                struct tag_mem32        mem;
                struct tag_videotext        videotext;
                struct tag_ramdisk        ramdisk;
                struct tag_initrd        initrd;
                struct tag_serialnr        serialnr;
                struct tag_revision        revision;
                struct tag_videolfb        videolfb;
                struct tag_cmdline        cmdline;

                /*
                 * Acorn specific
                 */
                struct tag_acorn        acorn;

                /*
                 * DC21285 specific
                 */
                struct tag_memclk        memclk;
        } u;
};





在嵌入式 Linux pȝ中,通常需要由 Boot Loader 讄的常见启动参数有QATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD{?

比如Q设|?ATAG_CORE 的代码如下:(x)



params = (struct tag *)BOOT_PARAMS;

        params->hdr.tag = ATAG_CORE;
        params->hdr.size = tag_size(tag_core);

        params->u.core.flags = 0;
        params->u.core.pagesize = 0;
        params->u.core.rootdev = 0;

        params = tag_next(params);


其中QBOOT_PARAMS 表示内核启动参数在内存中的v始基地址Q指?params 是一?struct tag cd的指针。宏 tag_next() 以指向当前标记的指针ؓ(f)参数Q计紧临当前标记的下一个标记的起始地址。注意,内核的根文gpȝ所在的讑֤ID是在这里设|的?

下面是设|内存映情늚CZ代码Q?



for(i = 0; i < NUM_MEM_AREAS; i++) {
                if(memory_map.used) {
                        params->hdr.tag = ATAG_MEM;
                        params->hdr.size = tag_size(tag_mem32);

                        params->u.mem.start = memory_map.start;
                        params->u.mem.size = memory_map.size;
                        
                        params = tag_next(params);
                }
}




可以看出Q在 memory_mapQ]数组中,每一个有效的内存D都对应一?ATAG_MEM 参数标记?
Linux 内核在启动时可以以命令行参数的Ş式来接收信息Q利用这一Ҏ(gu)们可以向内核提供那些内核不能自己的g参数信息Q或者重?override)内核自己到的信息。比如,我们用这样一个命令行参数字符?console=ttyS0,115200n8"来通知内核?ttyS0 作ؓ(f)控制収ͼ且串口采?"115200bps、无奇偶校验?位数据位"q样的设|。下面是一D设|调用内核命令行参数字符串的CZ代码Q?


char *p;

        /* eat leading white space */
        for(p = commandline; *p == ' '; p++)
                ;

        /* skip non-existent command lines so the kernel will still
    * use its default command line.
         */
        if(*p == '\0')
                return;

        params->hdr.tag = ATAG_CMDLINE;
        params->hdr.size = (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2;

        strcpy(params->u.cmdline.cmdline, p);

        params = tag_next(params);




h意在上述代码中,讄 tag_header 的大时Q必d括字W串的终止符'\0'Q此外还要将字节数向上圆?个字节,因ؓ(f) tag_header l构中的size 成员表示的是字数?

下面是设|?ATAG_INITRD 的示例代码,它告诉内核在 RAM 中的什么地方可以找?initrd 映象(压羃格式)以及它的大小Q?

  params->hdr.tag = ATAG_INITRD2;
params->hdr.size = tag_size(tag_initrd);

params->u.initrd.start = RAMDISK_RAM_BASE;
params->u.initrd.size = INITRD_LEN;

params = tag_next(params);

下面是设|?ATAG_RAMDISK 的示例代码,它告诉内核解压后?Ramdisk 有多大(单位是KBQ:(x)


params->hdr.tag = ATAG_RAMDISK;
params->hdr.size = tag_size(tag_ramdisk);
        
params->u.ramdisk.start = 0;
params->u.ramdisk.size = RAMDISK_SIZE; /* h意,单位是KB */
params->u.ramdisk.flags = 1; /* automatically load ramdisk */
        
params = tag_next(params);




最后,讄 ATAG_NONE 标记Q结束整个启动参数列表:(x)


static void setup_end_tag(void)
{
        params->hdr.tag = ATAG_NONE;
        params->hdr.size = 0;
}



3.2.5 调用内核

Boot Loader 调用 Linux 内核的方法是直接跌{到内核的W一条指令处Q也即直接蟩转到 MEM_STARTQ?x8000 地址处。在跌{Ӟ下列条g要满I(x)

1Q?CPU 寄存器的讄Q?
  • R0Q?Q?/font>
  • R1Q机器类?IDQ关?Machine Type NumberQ可以参?linux/arch/arm/tools/mach-types?/strong>
  • R2Q启动参数标记列表在 RAM 中v始基地址Q?/font>

2Q?CPU 模式Q?
  • 必须止中断QIRQs和FIQsQ;
  • CPU 必须 SVC 模式Q?/font>

3Q?Cache ?MMU 的设|:(x)
  • MMU 必须关闭Q?/font>
  • 指o Cache 可以打开也可以关闭;
  • 数据 Cache 必须关闭Q?/font>

如果?C 语言Q可以像下列CZ代码q样来调用内核:(x)



void (*theKernel)(int zero, int arch, u32 params_addr) = (void (*)(int, int, u32))KERNEL_RAM_BASE;
……
theKernel(0, ARCH_NUMBER, (u32) kernel_params_start);




注意QtheKernel()函数调用应该永远不返回的。如果这个调用返回,则说明出错?

四、关于串口终?br>
?boot loader E序的设计与实现中,没有什么能够比从串口终端正地收到打印信息能更令hȀ动了。此外,向串口终端打C息也是一个非帔R要而又有效的调试手Dc但是,我们l常?x)碰C口终端显CZؕ码或Ҏ(gu)没有昄的问题。造成q个问题主要有两U原因:(x)(1) boot loader 对串口的初始化设|不正确?2) q行?host 端的l端仿真E序对串口的讄不正,q包括:(x)波特率、奇偶校验、数据位和停止位{方面的讄?

此外Q有时也?x)碰到这L(fng)问题Q那是Q在 boot loader 的运行过E中我们可以正确地向串口l端输出信息Q但?boot loader 启动内核后却无法看到内核的启动输Z息。对q一问题的原因可以从以下几个斚w来考虑Q?

(1) 首先L(fng)认你的内核在~译旉|了对串口终端的支持Qƈ配置了正的串口驱动E序?
(2) 你的 boot loader 对串口的初始化设|可能会(x)和内核对串口的初始化讄不一致。此外,对于诸如 s3c44b0x q样?CPUQCPU 旉频率的设|也?x)媄响串口,因此如?boot loader 和内核对?CPU 旉频率的设|不一_也会(x)使串口终端无法正显CZ息?
(3) 最后,q要认 boot loader 所用的内核基地址必须和内核映像在~译时所用的q行基地址一_其是对?uClinux 而言。假设你的内核映像在~译时用的基地址?0xc0008000Q但你的 boot loader 却将它加载到 0xc0010000 处去执行Q那么内核映像当然不能正地执行了?

五、结束语

Boot Loader 的设计与实现是一个非常复杂的q程。如果不能从串口收到那激动h心的"uncompressing linux.................. done, booting the kernel……"内核启动信息Q恐怕谁也不能说Q?嗨,我的 boot loader 已经成功地{h了!"?/font>


jay 2008-11-13 00:09 发表评论
]]>
uClinuxpȝ?/title><link>http://www.shnenglu.com/yishanhante/articles/65692.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Sat, 01 Nov 2008 06:28:00 GMT</pubDate><guid>http://www.shnenglu.com/yishanhante/articles/65692.html</guid><wfw:comment>http://www.shnenglu.com/yishanhante/comments/65692.html</wfw:comment><comments>http://www.shnenglu.com/yishanhante/articles/65692.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/yishanhante/comments/commentRss/65692.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/yishanhante/services/trackbacks/65692.html</trackback:ping><description><![CDATA[<span style="font-family: simsun; ">目主页: <a target="_blank">http://www.uclinux.org/</a></span> <div><span style="font-family: simsun; "><p><strong>?/strong><br>Linux是一U很受欢q的操作pȝQ它与UNIXpȝ兼容Q开放源代码。它原本被设计ؓ(f)桌面pȝQ现在广泛应用于服务器领域。而更大的影响在于它正逐渐的应用于嵌入式设备。uClinux正是在这U氛围下产生的。在uClinuxq个英文单词中u表示MicroQ小的意思,C表示ControlQ控制的意思,所以uClinux是Micro-Control-LinuxQ字面上的理解就?针对微控刉域而设计的Linuxpȝ"?br><br><strong>uClinux型化的做法</strong><br><br><strong>标准Linux可能采用的小型化Ҏ(gu)</strong><br>1. 重新~译内核<br>Linux内核采用模块化的设计Q即很多功能块可以独立的加上或卸下,开发h员在设计内核时把q些内核模块作ؓ(f)可选的选项Q可以在~译pȝ内核时指定。因此一U较通用的做法是对Linux内核重新~译Q在~译时仔l的选择嵌入式设备所需要的功能支持模块Q同时删除不需要的功能。通过对内核的重新配置Q可以ɾpȝq行所需要的内核显著减小Q从而羃减资源用量?br>2. 制作root文gpȝ映象<br>Linuxpȝ在启动时必须加蝲根(rootQ文件系l,因此剪裁pȝ同时包括root file system的剪裁。在x86pȝ下,Linux可以在Dos下,使用Loadlin文g加蝲启动Q?br><strong>uClinux采用的小型化Ҏ(gu)</strong><br>1QuClinux的内核加载方?br>uClinux的内核有两种可选的q行方式Q可以在flash上直接运行,也可以加载到内存中运行。这U做法可以减内存需要?br>Flashq行方式Q把内核的可执行映象烧写到flash上,pȝ启动时从flash的某个地址开始逐句执行。这U方法实际上是很多嵌入式pȝ采用的方法?br>内核加蝲方式Q把内核的压~文件存攑֜flash上,pȝ启动时读取压~文件在内存里解压,然后开始执行,q种方式相对复杂一些,但是q行速度可能更快Qram的存取速率要比flash高)。同时这也是标准Linuxpȝ采用的启动方式?br>2QuClinux的根QrootQ文件系l?br>uClinuxpȝ采用romfs文gpȝQ这U文件系l相对于一般的ext2文gpȝ要求更少的空间。空间的节约来自于两个方面,首先内核支持romfs文gpȝ比支持ext2文gpȝ需要更的代码Q其ơromfs文gpȝ相对单,在徏立文件系l超U块QsuperblockQ需要更的存储I间。Romfs文gpȝ不支持动态擦写保存,对于pȝ需要动态保存的数据采用虚拟ram盘的Ҏ(gu)q行处理Qram盘将采用ext2文gpȝQ?br>3QuClinux的应用程序库<br>uClinux型化的另一个做法是重写了应用程序库Q相对于来大且越来越全的glibc库,uClibc对libc做了_?br>uClinux对用L(fng)序采用静态连接的形式Q这U做法会(x)使应用程序变大,但是Z内存理的问题,不得不这样做Q这在下文对uClinux内存理展开分析时进行说明)Q同时这U做法也更接q于通常嵌入式系l的做法?br><br><strong>uClinux的开发环?/strong><br><br><strong>GNU开发套?/strong><br>Gnu开发套件作为通用的Linux开攑֥Ӟ包括一pd的开发调试工兗主要组Ӟ(x)<br>GccQ?~译器,可以做成交叉~译的Ş式,卛_宿主Z开发编译目标上可运行的二进制文件?br>BinutilsQ一些辅助工P包括objdumpQ可以反~译二进制文ӞQasQ汇~编译器Q,ldQ连接器Q等{?br>GdbQ调试器Q可使用多种交叉调试方式Qgdb-bdmQ背景调试工PQgdbserverQ用以太网l调试)?br>uClinux的打印终?br>通常情况下,uClinux的默认终端是串口Q内核在启动时所有的信息都打印到串口l端Q用printk函数打印Q,同时也可以通过串口l端与系l交互?br>uClinux在启动时启动了telnetdQ远E登录服务)Q操作者可以远E登录上pȝQ从而控制系l的q行。至于是否允许远E登录可以通过烧写romfs文gpȝ时有用户军_是否启动q程d服务?br><strong>交叉~译调试工具</strong><br>支持一U新的处理器Q必d备一些编译,汇编工具Q用这些工具可以Ş成可q行于这U处理器的二q制文g。对于内怋用的~译工具同应用程序用的有所不同。在解释不同点之前,需要对gccq接做一些说明:(x) <br>.ldQlink descriptionQ文Ӟ(x)ld文g是指接时内存映象格式的文件?br>crt0.SQ应用程序编译连接时需要的启动文gQ主要是初始化应用程序栈?br>picQposition independence code Q与位置无关的二q制格式文gQ在E序D中必须包括relocD,从而的代码加载时可以q行重新定位?br>内核~译q接Ӟ使用ucsimm.ld文gQŞ成可执行文g映象Q所形成的代码段既可以用间接寻址方式Q即使用relocD进行寻址Q,也可以用绝对寻址方式。这样可以给~译器更多的优化I间。因为内核可能用绝对寻址Q所以内核加载到的内存地址I间必须与ld文g中给定的内存I间完全相同?#160;<br>应用E序的连接与内核q接方式不同。应用程序由内核加蝲Q可执行文g加蝲器将在后面讨论)Q由于应用程序的ld文gl出的内存空间与应用E序实际被加载的内存位置可能不同Q这样在应用E序加蝲的过E中需要一个重新地位的q程Q即对relocD进行修正,使得E序q行间接d时不至于出错。(q个问题在i386{高U处理器上方法有所不同Q本文将在后面进一步分析)?br>׃q讨论,臛_需要两套编译连接工兗在讨论quClinux的内存管理后本文给出整个系l的工作程以及pȝ在flash和ram中的I间分布?br><strong>可执行文件格?/strong><br>先对一些名词作一些说明:(x) <br>coffQcommon object file formatQ:(x)一U通用的对象文件格?br>elfQexcutive linked fileQ:(x)一Uؓ(f)Linuxpȝ所采用的通用文g格式Q支持动态连?br>flatQelf格式有很大的文g_flat文gҎ(gu)件头和一些段信息做了?br>uClinuxpȝ使用flat可执行文件格式,gcc的编译器不能直接形成q种文g格式Q但是可以Ş成coff或elf格式的可执行文gQ这两种文g需要coff2flt或elf2flt工具q行格式转化QŞ成flat文g?br>当用h行一个应用时Q内核的执行文g加蝲器将对flat文gq行q一步处理,主要是对relocD进行修正(可执行文件加载器的详见fs/binfmt_flat.cQ。以下对relocD进一步讨论?br>需要relocD늚Ҏ(gu)原因是,E序在连接时q接器所假定的程序运行空间与实际E序加蝲到的内存I间不同。假如有q样一条指令:(x)<br>jsr app_start;<br>q一条指令采用直接寻址Q蟩转到app_start地址处执行,q接E序在~译完成是计出app_start的实际地址Q设若实际地址?x10000Q,q个实际地址是根据ld文g计算出来Q因接器假定该程序将被加载到由ld文g指明的内存空_。但实际上由于内存分配的关系Q操作系l在加蝲时无法保证程序将按ld文g加蝲。这时如果程序仍然蟩转到l对地址0x10000处执行,通常情况q是不正的。一个解军_法是增加一个存储空_用于存储app_start的实际地址Q设若用变量addr表示q个存储I间。则以上q句E序改为:(x)<br>movl addr, a0;<br>jsr (a0);<br>增加的变量addr在数据D中占用一?字节的空_q接器将app_start的绝对地址存储到该变量。在可执行文件加载时Q可执行文g加蝲器根据程序将要加载的内存I间计算出app_start在内存中的实际位|,写入addr变量。系l在实际处理是不需要知道这个变量的切存储位置Q也不可能知道)Q系l只要对整个relocD进行处理就可以了(relocD|标识Q系l可以读出来Q。处理很单只需要对relocD中存储的值统一加上一个偏|(如果加蝲的空间比预想的要靠前Q实际上是减M个偏U量Q。偏|由实际的物理地址起始值同ld文g指定的地址起始值相减计出?br>q种reloc的方式部分是由uClinux的内存分配问题引L(fng)Q这一点将在uClinux内存理分析时说明?br><strong>针对实时性的解决Ҏ(gu)</strong><br>uClinux本nq没有关注实旉题,它ƈ不是ZLinux的实时性而提出的。另外有一ULinux--Rt-linuxx实时问题。Rt-linux执行理器把普通Linux的内核当成一个Q务运行,同时q管理了实时q程。而非实时q程则交l普通Linux内核处理。这U方法已l应用于很多的操作系l用于增强操作系l的实时性,包括一些商用版UNIXpȝQW(xu)indows NT{等。这U方法优点之一是实现简单,且实时性能Ҏ(gu)验。优点之二是׃非实时进E运行于标准LinuxpȝQ同其它Linux商用版本之间保持了很大的兼容性。优点之三是可以支持实时时钟的应用。uClinux可以使用Rt-linux的patchQ从而增强uClinux的实时性,使得uClinux可以应用于工业控制、进E控制等一些实时要求较高的应用?br></p><p><strong>uClinux的内存管?/strong><br>应该说uClinux同标准Linux的最大区别就在于内存理Q同时也׃uClinux的内存管理引发了一些标准Linux所不会(x)出现的问题。本文将把uClinux内存理同标准Linux的那内存理部分q行比较分析?br>标准Linux使用的虚拟存储器技?br>标准Linux使用虚拟存储器技术,q种技术用于提供比计算机系l中实际使用的物理内存大得多的内存空间。用者将感觉到好像程序可以用非常大的内存空_从而得编Eh员在写程序时不用考虑计算Z的物理内存的实际定w?/p><p>Z支持虚拟存储理器的理QLinuxpȝ采用分页QpagingQ的方式来蝲入进E。所谓分|是把实际的存储器分割为相同大的D,例如每个D?024个字节,q样1024个字节大的D便UCؓ(f)一个页面(pageQ?#160;<br>虚拟存储器由存储器管理机制及一个大定w的快速硬盘存储器支持。它的实现基于局部性原理,当一个程序在q行之前Q没有必要全部装入内存,而是仅将那些当前要运行的那些部分面或段装入内存q行Qcopy-on-writeQ,其余暂时留在盘上程序运行时如果它所要访问的(D)已存在,则程序l运行,如果发现不存在的(D)Q操作系l将产生一个页错误Qpage faultQ,q个错误D操作pȝ把需要运行的部分加蝲到内存中。必要时操作pȝq可以把不需要的内存(D)交换到磁盘上。利用这L(fng)方式理存储器,便可把一个进E所需要用到的存储器以化整为零的方式,视需求分批蝲入,而核心程序则凭借属于每个页面的늠来完成寻址各个存储器区D늚工作?br>标准Linux是针Ҏ(gu)内存理单元的处理器设计的。在q种处理器上Q虚拟地址被送到内存理单元QMMUQ,把虚拟地址映射为物理地址?br>通过赋予每个d不同的虚?-物理地址转换映射Q支持不同Q务之间的保护。地址转换函数在每一个Q务中定义Q在一个Q务中的虚拟地址I间映射到物理内存的一个部分,而另一个Q务的虚拟地址I间映射到物理存储器中的另外区域。计机的存储管理单元(MMUQ一般有一l寄存器来标识当前运行的q程的{换表。在当前q程CPU攑ּl另一个进E时Q一ơ上下文切换Q,内核通过指向新进E地址转换表的指针加蝲q些寄存器。MMU寄存器是有特权的Q只能在内核态才能访问。这׃证了一个进E只能访问自qL(fng)间内的地址Q而不?x)访问和修改其它q程的空间。当可执行文件被加蝲Ӟ加蝲器根据缺省的ld文gQ把E序加蝲到虚拟内存的一个空_因ؓ(f)q个原因实际上很多程序的虚拟地址I间是相同的Q但是由于{换函C同,所以实际所处的内存区域也不同。而对于多q程理当处理器q行q程切换q执行一个新dӞ一个重要部分就是ؓ(f)CQ务切换Q务{换表。我们可以看到Linuxpȝ的内存管理至实C以下功能Q?br>q行比内存还要大的程序。理x况下应该可以q行L大小的程?br>◇可以运行只加蝲了部分的E序Q羃短了E序启动的时?br>◇可以多个E序同时ȝ在内存中提高CPU的利用率<br>◇可以运行重定位E序。即E序可以方于内存中的M一处,而且可以在执行过E中Ud?br>◇写机器无关的代码。程序不必事先约定机器的配置情况?br>◇减ȝ序员分配和管理内存资源的负担?br>◇可以进行共?-例如Q如果两个进E运行同一个程序,它们应该可以׃nE序代码的同一个副本?br>◇提供内存保护,q程不能以非授权方式讉K或修攚w面,内核保护单个q程的数据和代码以防止其它进E修改它们。否则,用户E序可能?x)偶Ӟ或恶意)的破坏内核或其它用户E序?#160;<br>虚存pȝq不是没有代L(fng)。内存管理需要地址转换表和其他一些数据结构,留给E序的内存减了。地址转换增加了每一条指令的执行旉Q而对于有额外内存操作的指令会(x)更严重。当q程讉K不在内存的页面时Q系l发生失效。系l处理该失效Qƈ页面加载到内存中,q需要极耗时间的盘I(y)/O操作。M内存理zd占用了相当一部分cpu旉Q在较忙的系l中大约?0Q)?br>uClinux针对NOMMU的特D处?br>对于uClinux来说Q其设计针对没有MMU的处理器Q即uClinux不能使用处理器的虚拟内存理技术(应该说这U不带有MMU的处理器在嵌入式讑֤中相当普偏)。uClinux仍然采用存储器的分页理Q系l在启动时把实际存储器进行分c在加蝲应用E序时程序分加载。但是由于没有MMU理Q所以实际上uClinux采用实存储器理{略Qreal memeory managementQ。这一点媄响了pȝ工作的很多方面?br>uClinuxpȝ对于内存的访问是直接的,Q它对地址的访问不需要经qMMUQ而是直接送到地址U上输出Q,所有程序中讉K的地址都是实际的物理地址。操作系l对内存I间没有保护Q这实际上是很多嵌入式系l的特点Q,各个q程实际上共享一个运行空_没有独立的地址转换表)?#160;<br>一个进E在执行前,pȝ必须E分配够的q箋地址I间Q然后全部蝲入主存储器的q箋I间中。与之相对应的是标准Linuxpȝ在分配内存时没有必要保证实际物理存储I间是连l的Q而只要保证虚存地址I间q箋可以了。另外一个方面程序加载地址与预期(ld文g中指出的Q通常都不相同Q这样relocationq程是必须的。此外磁盘交换空间也是无法用的Q系l执行时如果~少内存无法通过盘交换来得到改善?br>uClinux对内存的理减少同时q开发h员提Z更高的要求。如果从易用性这一Ҏ(gu)_uClinux的内存管理是一U倒退Q退回了CUNIX早期或是Dospȝ时代。开发h员不得不参与pȝ的内存管理。从~译内核开始,开发h员必d诉系l这块开发板到底拥有多少的内存(假如你欺骗了pȝQ那在后面q行E序时受到惩|)Q从而系l将在启动的初始化阶D对内存q行分页Qƈ且标记已使用的和未用的内存。系l将在运行应用时使用q些分页内存?br>׃应用E序加蝲时必d配连l的地址I间Q而针对不同硬件^台的可一ơ成块(q箋地址Q分配内存大限制是不同Q目前针对ez328处理器的uClinux?28kQ而针对coldfire处理器的pȝ内存则无此限ӞQ所以开发h员在开发应用程序时必须考虑内存的分配情况ƈx应用E序需要运行空间的大小。另外由于采用实存储器管理策略,用户E序同内总及其它用L(fng)序在一个地址I间Q程序开发时要保证不늊其它E序的地址I间Q以使得E序不至于破坏系l的正常工作Q或D其它E序的运行异常?br>从内存的讉K角度来看Q开发h员的权利增大了(开发h员在~程时可以访问Q意的地址I间Q,但与此同时系l的安全性也大ؓ(f)下降。此外,pȝ对多q程的管理将有很大的变化Q这一点将在uClinux的多q程理中说明?br>虽然uClinux的内存管理与标准Linuxpȝ相比功能相差很多Q但应该说这是嵌入式讑֤的选择。在嵌入式设备中Q由于成本等敏感因素的媄响,普偏的采用不带有MMU的处理器Q这军_了系l没有够的g支持实现虚拟存储理技术。从嵌入式设备实现的功能来看Q嵌入式讑֤通常在某一特定的环境下q行Q只要实现特定的功能Q其功能相对单,内存理的要求完全可以由开发h员考虑?br>标准Linuxpȝ的进E、线E?br>q程Q进E是一个运行程序ƈ为其提供执行环境的实体,它包括一个地址I间和至一个控制点Q进E在q个地址I间上执行单一指o序列。进E地址I间包括可以讉K或引用的内存单元的集合,q程控制炚w过一个一般称为程序计数器Qprogram counter,PCQ的g寄存器控制和跟踪q程指o序列?br>forkQ由于进Eؓ(f)执行E序的环境,因此在执行程序前必须先徏立这个能"?E序的环境。Linuxpȝ提供pȝ调用拯现行q程的内容,以生新的进E,调用fork的进E称为父q程Q而所产生的新q程则称为子q程。子q程?x)承袭父q程的一切特性,但是它有自己的数据段Q也是_管子进E改变了所属的变量Q却不会(x)影响到父q程的变量倹{?br>父进E和子进E共享一个程序段Q但是各自拥有自q堆栈、数据段、用L(fng)间以及进E控制块。换a之,两个q程执行的程序代码是一L(fng)Q但是各有各的程序计数器与自qUh数据?#160;<br>当内核收到forkhӞ它会(x)先查怸件事Q首先检查存储器是不是够;其次是进E表是否仍有I缺Q最后则是看看用h否徏立了太多的子q程。如果上q说三个条g满Q那么操作系l会(x)l子q程一个进E识别码Qƈ且设定cpu旉Q接着讑֮与父q程׃n的段Q同时将父进E的inode拯一份给子进E运用,最l子q程?x)返回数?以表C它是子q程Q至于父q程Q它可能{待子进E的执行l束Q或与子q程各做个的?br>execpȝ调用Q该pȝ调用提供一个进E去执行另一个进E的能力Qexecpȝ调用是采用覆盖旧有进E存储器内容的方式,所以原来程序的堆栈、数据段与程序段都会(x)被修改,只有用户区维持不变?br>vforkpȝ调用Q由于在使用forkӞ内核?x)将父进E拷贝一份给子进E,但是q样的做法相当浪Ҏ(gu)_因ؓ(f)大多数的情Ş都是E序在调用fork后就立即调用execQ这样刚拯来的q程区域又立卌新的数据覆盖掉。因此Linuxpȝ提供一个系l调用vforkQvfork假定pȝ在调用完成vfork后会(x)马上执行execQ因此vfork不拷贝父q程的页面,只是初始化私有的数据l构与准备够的分页表。这样实际在vfork调用完成后父子进E事实上׃n同一块存储器Q在子进E调用exec或是exit之前Q,因此子进E可以更改父q程的数据及堆栈信息Q因此vforkpȝ调用完成后,父进E进入睡眠,直到子进E执行exec。当子进E执行execӞ׃exec要用被执行E序的数据,代码覆盖子进E的存储区域Q这样将产生写保护错误(do_wp_pageQ(q个时候子q程写的实际上是父进E的存储区域Q,<br>q个错误D内核为子q程重新分配存储I间。当子进E正开始执行后Q将唤醒父进E,使得父进El往后执行?br>uClinux的多q程处理<br>uClinux没有mmu理存储器,在实现多个进E时Qfork调用生成子进E)需要实现数据保护?br>uClinux的fork和vforkQuClinux的fork{于vfork。实际上uClinux的多q程理通过vfork来实现。这意味着uClinuxpȝfork调用完程后,要么子进E代替父q程执行Q此时父q程已经sleepQ直到子q程调用exit退出,要么调用exec执行一个新的进E,q个时候将产生可执行文件的加蝲Q即使这个进E只是父q程的拷贝,q个q程也不能避免。当子进E执行exit或exec后,子进E用wakeup把父q程唤醒Q父q程l箋往下执行?br>uClinux的这U多q程实现机制同它的内存管理紧密相兟뀂uClinux针对nommu处理器开发,所以被q用一Uflat方式的内存管理模式,启动新的应用E序时系l必Mؓ(f)应用E序分配存储I间Qƈ立即把应用程序加载到内存。缺了MMU的内存重映射机制QuClinux必须在可执行文g加蝲阶段对可执行文greloc处理Q得程序执行时能够直接使用物理内存?/p></span></div><img src ="http://www.shnenglu.com/yishanhante/aggbug/65692.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/yishanhante/" target="_blank">jay</a> 2008-11-01 14:28 <a href="http://www.shnenglu.com/yishanhante/articles/65692.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.amgtm.cn" target="_blank">պݺݾþ͵͵ɫۺ </a>| <a href="http://www.xldgdq.cn" target="_blank">һaƬþëƬ</a>| <a href="http://www.chemzt.cn" target="_blank">þˬˬˬ˾þþ</a>| <a href="http://www.txt115.cn" target="_blank">Ʒþ޲</a>| <a href="http://www.gdswky.cn" target="_blank">2021þþƷ</a>| <a href="http://www.wuchui.cn" target="_blank">һþۺ³³</a>| <a href="http://www.21chem.cn" target="_blank">þ޾ƷAV</a>| <a href="http://www.sskca.cn" target="_blank">ճˮþ޾Ʒtv</a>| <a href="http://www.lzjsyg.cn" target="_blank">þþŮ붯ȺëƬ </a>| <a href="http://www.bagscheap.cn" target="_blank">Ƶþ</a>| <a href="http://www.site5d.cn" target="_blank">ɫ͵͵88888ŷƷþþ </a>| <a href="http://www.corporateavenue.cn" target="_blank">þþþþAvӰԺ</a>| <a href="http://www.cc5ujj.cn" target="_blank">ƷþĻ</a>| <a href="http://www.padshow.cn" target="_blank">޾þþþþ77777</a>| <a href="http://www.rahd.cn" target="_blank">ƷŮþþ</a>| <a href="http://www.pingyaonews.cn" target="_blank">þþҹƷ</a>| <a href="http://www.k1822.cn" target="_blank">þۺϾɫۺ97_þþ</a>| <a href="http://www.zawin.cn" target="_blank">þþƷav鶹ͼƬ</a>| <a href="http://www.yaojiji.cn" target="_blank">99þùۺϾƷ</a>| <a href="http://www.mrbuy.com.cn" target="_blank">þþƷަvDz</a>| <a href="http://www.ejectorpin.cn" target="_blank">ƷŮþþþavˬ</a>| <a href="http://www.obsessions.cn" target="_blank">ղƷþþþþþɫ</a>| <a href="http://www.365si.cn" target="_blank">þۺ϶</a>| <a href="http://www.box6.cn" target="_blank">91Ʒɫ۾þ</a>| <a href="http://www.gfba.cn" target="_blank">ݺɫþþۺ</a>| <a href="http://www.qingjian8.cn" target="_blank">޾Ʒþþþsm</a>| <a href="http://www.ifson.cn" target="_blank">պŷһþþþ </a>| <a href="http://www.fnhihs.cn" target="_blank">Ʒ99þѹۿ</a>| <a href="http://www.qysf88.cn" target="_blank">þþƷ2020</a>| <a href="http://www.ag202.cn" target="_blank">˾þۺӰԺ</a>| <a href="http://www.oq21.cn" target="_blank">þþþþþþ</a>| <a href="http://www.tpjqrj.cn" target="_blank">ŷһþþƷ</a>| <a href="http://www.bbs020.cn" target="_blank">˾þùѹۿƵ</a>| <a href="http://www.abctoy.com.cn" target="_blank">þþžžþƷ</a>| <a href="http://www.renshushu.cn" target="_blank">þ99ƷþþþþҰ </a>| <a href="http://www.gajl.cn" target="_blank">þù޾Ʒ</a>| <a href="http://www.t6s.com.cn" target="_blank">ҹ޾þþþþþþ</a>| <a href="http://www.wzwsbz.cn" target="_blank">þþ뾫Ʒպ˳</a>| <a href="http://www.ak14.cn" target="_blank">޹˾þþƷ99</a>| <a href="http://www.ekgb.cn" target="_blank">һþۺ³³</a>| <a href="http://www.hdmi-cable.cn" target="_blank">ŷԴսþþþþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>