导航:首页 > 源码编译 > 编译器superblock

编译器superblock

发布时间:2022-09-20 11:52:27

A. uc/os操作系统是怎样启动的

uc/os和uclinux操作系统是两种性能优良源码公开且被广泛应用的的免费嵌入
式操作系统,可以作为研究实时操作系统和非实时操作系统的典范。本文通过对
uc/os和uclinux的对比,分析和总结了嵌入式操作系统应用中的若干重要问题,
归纳了嵌入式系统开发中操作系统的选型依据。
两种开源嵌入式操作系统介绍
uc/os和uclinux操作系统,是当前得到广泛应用的两种免费且公开源码的嵌入
式操作系统。uc/os适合小型控制系统,具有执行效率高、占用空间小、实时性
能优良和可扩展性强等特点,最小内核可编译至2k。uclinux则是继承标准linux
的优良特性,针对嵌入式处理器的特点设计的一种操作系统,具有内嵌网络协议、
支持多种文件系统,开发者可利用标准linux先验知识等优势。其编译后目标文
件可控制在几百k量级。
uc/os是一种免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。
其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服
务等功能。
uclinux是一种优秀的嵌入式linux版本。uclinux是micro-conrol-linux的缩写。
同标准linux相比,它集成了标准linux操作系统的稳定性、强大网络功能和出
色的文件系统等主要优点。但是由于没有mmu(内存管理单元),其多任务的实
现需要一定技巧。
两种嵌入式操作系统主要性能比较
嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法
组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有
软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来
实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化
操作。
嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般
需要经过针对专门平台的移植操作系统才能正常工作。
进程调度、文件系统支持和系统移植是在嵌入式操作系统实际应用中最常见的问
题,下文就从这几个角度入手对uc/os和uclinux进行分析比较。
进程调度
任务调度主要是协调任务对计算机系统内资源(如内存、i/o设备、cpu)的争夺
使用。进程调度又称为cpu调度,其根本任务是按照某种原则为处于就绪状态
的进程分配cpu。由于嵌入式系统中内存和i/o设备一般都和cpu同时归属于
某进程,所以任务调度和进程调度概念相近,很多场合不加区分,下文中提到的
任务其实就是进程的概念。
进程调度可分为"剥夺型调度"和"非剥夺型调度"两种基本方式。所谓"非剥夺型
调度"是指:一旦某个进程被调度执行,则该进程一直执行下去直至该进程结束,
或由于某种原因自行放弃cpu进入等待状态,才将cpu重新分配给其他进程。
所谓"剥夺型调度"是指:一旦就绪状态中出现优先权更高的进程,或者运行的进
程已用满了规定的时间片时,便立即剥夺当前进程的运行(将其放回就绪状态),
把cpu分配给其他进程。
作为实时操作系统,uc/os是采用的可剥夺型实时多任务内核。可剥夺型的实时
内核在任何时候都运行就绪了的最高优先级的任务。uc/os中最多可以支持64
个任务,分别对应优先级0"63,其中0为最高优先级。调度工作的内容可以分
为两部分:最高优先级任务的寻找和任务切换。
其最高优先级任务的寻找是通过建立就绪任务表来实现的。uc/os中的每一个任
务都有独立的堆栈空间,并有一个称为任务控制块tcb(task control block)数据
结构,其中第一个成员变量就是保存的任务堆栈指针。任务调度模块首先用变量
ostcbhighrdy记录当前最高级就绪任务的tcb地址,然后调用os_task_sw()
函数来进行任务切换。
uclinux的进程调度沿用了linux的传统,系统每隔一定时间挂起进程,同时系
统产生快速和周期性的时钟计时中断,并通过调度函数(定时器处理函数)决定进
程什么时候拥有它的时间片。然后进行相关进程切换,这是通过父进程调用fork
函数生成子进程来实现的。
uclinux系统fork调用完成后,要么子进程代替父进程执行(此时父进程已经
sleep),直到子进程调用exit退出;要么调用exec执行一个新的进程,这个时候
产生可执行文件的加载,即使这个进程只是父进程的拷贝,这个过程也不可避免。
当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,使父进程继续
往下执行。
uclinux由于没有mmu管理存储器,其对内存的访问是直接的,所有程序中访
问的地址都是实际的物理地址。操作系统队内存空间没有保护,各个进程实际上
共享一个运行空间。这就需要实现多进程时进行数据保护,也导致了用户程序使
用的空间可能占用到系统内核空间,这些问题在编程时都需要多加注意,否则容
易导致系统崩溃。
由上述分析可以得知,uc/os内核是针对实时系统的要求设计实现的,相对简单,
可以满足较高的实时性要求。而uclinux则在结构上继承了标准linux的多任务
实现方式,仅针对嵌入式处理器特点进行改良。其要实现实时性效果则需要使系
统在实时内核的控制下运行,rt-linux就是可以实现这一个功能的一种实时内
核。
文件系统
所谓文件系统是指负责存取和管理文件信息的机构,也可以说是负责文件的建
立、撤销、组织、读写、修改、复制及对文件管理所需要的资源(如目录表、存
储介质等)实施管理的软件部分。
uc/os是面向中小型嵌入式系统的,如果包含全部功能(信号量、消息邮箱、消
息队列及相关函数),编译后的uc/os内核仅有6"10kb,所以系统本身并没有
对文件系统的支持。但是uc/os具有良好的扩展性能,如果需要的话也可自行
加入文件系统的内容。
uclinux则是继承了linux完善的文件系统性能。其采用的是romfs文件系统,
这种文件系统相对于一般的ext2文件系统要求更少的空间。空间的节约来自于
两个方面,首先内核支持romfs文件系统比支持ext2文件系统需要更少的代码,
其次romfs文件系统相对简单,在建立文件系统超级块(superblock)需要更少的存
储空间。romfs文件系统不支持动态擦写保存,对于系统需要动态保存的数据采
用虚拟ram盘的方法进行处理(ram盘将采用ext2文件系统)。
uclinux还继承了linux网络操作系统的优势,可以很方便的支持网络文件系统
且内嵌tcp/ip协议,这为uclinux开发网络接入设备提供了便利。
由两种操作系统对文件系统的支持可知,在复杂的需要较多文件处理的嵌入式系
统中uclinux是一个不错的选择。而uc/os则主要适合一些控制系统。
操作系统的移植
嵌入式操作系统移植的目的是指使操作系统能在某个微处理器或微控制器上运
行。uc/os和uclinux都是源码公开的操作系统,且其结构化设计便于把与处理
器相关的部分分离出来,所以被移植到新的处理器上是可能的。
以下对两种系统的移植分别予以说明。
(1)uc/os的移植
要移植uc/os,目标处理器必须满足以下要求;
·处理器的c编译器能产生可重入代码,且用c语言就可以打开和关闭中断;
·处理器支持中断,并能产生定时中断;
·处理器支持足够的ram(几k字节),作为多任务环境下的任务堆栈;
·处理器有将堆栈指针和其他cpu寄存器读出和存储到堆栈或内存中的指令。
在理解了处理器和c编译器的技术细节后,uc/os的移植只需要修改与处理器
相关的代码就可以了。具体有如下内容:
·os_cpu.h中需要设置一个常量来标识堆栈增长方向;
·os_cpu.h中需要声明几个用于开关中断和任务切换的宏;
·os_cpu.h中需要针对具体处理器的字长重新定义一系列数据类型;
·os_cpu_a.asm需要改写4个汇编语言的函数;
·os_cpu_c.c需要用c语言编写6个简单函数;
·修改主头文件include.h,将上面的三个文件和其他自己的头文件加入。
(2)uclinux的移植
由于uclinux其实是linux针对嵌入式系统的一种改良,其结构比较复杂,相对
uc/os,uclinux的移植也复杂得多。一般而言要移植uclinux,目标处理器除了
应满足上述uc/os应满足的条件外,还需要具有足够容量(几百k字节以上)外
部rom和ram。
uclinux的移植大致可以分为3个层次:
·结构层次的移植,如果待移植处理器的结构不同于任何已经支持的处理器结构,
则需要修改linux/arch目录下相关处理器结构的文件。虽然uclinux内核代码的
大部分是独立于处理器和其体系结构的,但是其最低级的代码也是特定于各个系
统的。这主要表现在它们的中断处理上下文、内存映射的维护、任务上下文和初
始化过程都是独特的。这些例行程序位于linux/arch/目录下。由于linux所支持
体系结构的种类繁多,所以对一个新型的体系,其低级例程可以模仿与其相似的
体系例程编写。
·平台层次的移植,如果待移植处理器是某种uclinux已支持体系的分支处理器,
则需要在相关体系结构目录下建立相应目录并编写相应代码。如mc68ez328就
是基于无mmu的m68k内核的。此时的移植需要创建
linux/arch/m68knommu/platform/ mc68ez328目录并在其下编写跟踪程序(实现
用户程序到内核函数的接口等功能)、中断控制调度程序和向量初始化程序等。
·板级移植,如果你所用处理器已被uclinux支持的话,就只需要板级移植了。板
级移植需要在linux/arch/?platform/中建立一个相应板的目录,再在其中建立相应
的启动代码crt0_rom.s或crt0_ram.s和链接描述文档rom.ld或ram.ld就可以了。
板级移植还包括驱动程序的编写和环境变量设置等内容。

通过对uc/os和uclinux的比较,可以看出这两种操作系统在应用方面各有优劣。
uc/os占用空间少,执行效率高,实时性能优良,且针对新处理器的移植相对简
单。uclinux则占用空间相对较大,实时性能一般,针对新处理器的移植相对复
杂。但是,uclinux具有对多种文件系统的支持能力、内嵌了tcp/ip协议,可
以借鉴linux丰富的资源,对一些复杂的应用,uclinux具有相当优势。例如cisco
公司的 2500/3000/4000 路由器就是基于uclinux操作系统开发的。
总之,操作系统的选择是由嵌入式系统的需求决定的。简单的说就是,小型控制
系统可充分利用uc/os小巧且实时性强的优势,如果开发pda和互联网连接终
端等较为复杂的系统则uclinux是不错的选择。

B. 为什么DEBUG版本正确,Release版本错误

一、Debug 和 Release 编译方式的本质区别

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)

Debug 版本:
/MDd /MLd 或 /MTd 使用 Debug runtime library(调试版本的运行时刻函数库)
/Od 关闭优化开关
/D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关(主要针对
assert函数)
/ZI 创建 Edit and continue(编辑继续)数据库,这样在调试过
程中如果修改了源代码不需重新编译
/GZ 可以帮助捕获内存错误
/Gm 打开最小化重链接开关,减少链接时间

Release 版本:
/MD /ML 或 /MT 使用发布版本的运行时刻函数库
/O1 或 /O2 优化开关,使程序最小或最快
/D "NDEBUG" 关闭条件编译调试代码开关(即不编译assert函数)
/GF 合并重复的字符串,并将字符串常量放到只读内存,防止
被修改

实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

二、哪些情况下 Release 版会出错

有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的

1. Runtime Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。

2. 优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:

(1) 帧指针(Frame Pointer)省略(简称 FPO ):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误————但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入 /Oy- 编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:

● MFC 消息响应函数书写错误。正确的应为
afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam);
ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include "afxwin.h"之后),函数原形错误时编译会报错
#undef ON_MESSAGE
#define ON_MESSAGE(message, memberFxn) { message, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&memberFxn) },

(2) volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。

(3) 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有:
● 非法访问,包括数组越界、指针错误等。例如
void fn(void)
{
int i;
i = 1;
int a[4];
{
int j;
j = 1;
}
a[-1] = 1;//当然错误不会这么明显,例如下标是变量
a[4] = 1;
}
j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而 Release 版由于 i、j 并未其很大作用可能会被优化掉,从而使栈被破坏。

3. _DEBUG 与 NDEBUG :当定义了 _DEBUG 时,assert() 函数会被编译,而 NDEBUG 时不被编译。除此之外,VC++中还有一系列断言宏。这包括:

ANSI C 断言 void assert(int expression );
C Runtime Lib 断言 _ASSERT( booleanExpression );
_ASSERTE( booleanExpression );
MFC 断言 ASSERT( booleanExpression );
VERIFY( booleanExpression );
ASSERT_VALID( pObject );
ASSERT_KINDOF( classname, pobject );
ATL 断言 ATLASSERT( booleanExpression );
此外,TRACE() 宏的编译也受 _DEBUG 控制。

所有这些断言都只在 Debug版中才被编译,而在 Release 版中被忽略。唯一的例外是 VERIFY() 。事实上,这些宏都是调用了 assert() 函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用 等),那么 Release 版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用 VC++ 的 Find in Files 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入 #ifdef _DEBUG 之类的条件编译,也要注意一下。
顺便值得一提的是 VERIFY() 宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查 Windows API 的返回值。有些人可能为这个原因而滥用 VERIFY() ,事实上这是危险的,因为 VERIFY() 违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。

4. /GZ 选项:这个选项会做以下这些事

(1) 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用 0 来初始化变量,这是错误的(而且这样很不利于查找错误)。
(2) 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)
(3) 函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )

通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。

除此之外,/Gm /GF 等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。
三、怎样“调试” Release 版的程序

遇到 Debug 成功但 Release 失败,显然是一件很沮丧的事,而且往往无从下手。如果你看了以上的分析,结合错误的具体表现,很快找出了错误,固然很好。但如果一时找不出,以下给出了一些在这种情况下的策略。

1. 前面已经提过,Debug 和 Release 只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改 Release 版的编译选项来缩小错误范围。如上所述,可以把 Release 的选项逐个改为与之相对的 Debug 选项,如 /MD 改为 /MDd、/O1 改为 /Od,或运行时间优化改为程序大小优化。注意,一次只改一个选项,看改哪个选项时错误消失,再对应该选项相关的错误,针对性地查找。这些选项在 Project\Settings... 中都可以直接通过列表选取,通常不要手动修改。由于以上的分析已相当全面,这个方法是最有效的。

2. 在编程过程中就要时常注意测试 Release 版本,以免最后代码太多,时间又很紧。

3. 在 Debug 版中使用 /W4 警告级别,这样可以从编译器获得最大限度的错误信息,比如 if( i =0 )就会引起 /W4 警告。不要忽略这些警告,通常这是你程序中的 Bug 引起的。但有时 /W4 会带来很多冗余信息,如 未使用的函数参数 警告,而很多消息处理函数都会忽略某些参数。我们可以用
#progma warning(disable: 4702) //禁止
//...
#progma warning(default: 4702) //重新允许
来暂时禁止某个警告,或使用
#progma warning(push, 3) //设置警告级别为 /W3
//...
#progma warning(pop) //重设为 /W4
来暂时改变警告级别,有时你可以只在认为可疑的那一部分代码使用 /W4。

4.你也可以像 Debug 一样调试你的 Release 版,只要加入调试符号。在 Project/Settings... 中,选中 Settings for "Win32 Release",选中 C/C++ 标签,Category 选 General,Debug Info 选 Program Database。再在 Link 标签 Project options 最后加上 "/OPT:REF" (引号不要输)。这样调试器就能使用 pdb 文件中的调试符号。但调试时你会发现断点很难设置,变量也很难找到——这些都被优化过了。不过令人庆幸的是,Call Stack 窗口仍然工作正常,即使帧指针被优化,栈信息(特别是返回地址)仍然能找到。这对定位错误很有帮助。

C. 如果通了uC/OS II,对学习LINUX操作系统有多少帮助

uc/os和uclinux操作系统是两种性能优良源码公开且被广泛应用的的免费嵌入 式操作系统,可以作为研究实时操作系统和非实时操作系统的典范。本文通过对 uc/os和uclinux的对比,分析和总结了嵌入式操作系统应用中的若干重要问题, 归纳了嵌入式系统开发中操作系统的选型依据。 两种开源嵌入式操作系统介绍 uc/os和uclinux操作系统,是当前得到广泛应用的两种免费且公开源码的嵌入 式操作系统。uc/os适合小型控制系统,具有执行效率高、占用空间小、实时性 能优良和可扩展性强等特点,最小内核可编译至2k。uclinux则是继承标准linux 的优良特性,针对嵌入式处理器的特点设计的一种操作系统,具有内嵌中国络协议、 支持多种文件系统,开发者可利用标准linux先验知识等优势。其编译后目标文 件可控制在几百k量级。 uc/os是一种免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。 其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服 务等功能。 uclinux是一种优秀的嵌入式linux版本。uclinux是micro-conrol-linux的缩写。 同标准linux相比,它集成了标准linux操作系统的稳定性、强大中国络功能和出 色的文件系统等主要优点。但是由于没有mmu(内存管理单元),其多任务的实 现需要一定技巧。 两种嵌入式操作系统主要性能比较 嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法 组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有 软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来 实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化 操作。 嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般 需要经过针对专门平台的移植操作系统才能正常工作。 进程调度、文件系统支持和系统移植是在嵌入式操作系统实际应用中最常见的问 题,下文就从这几个角度入手对uc/os和uclinux进行分析比较。 进程调度 任务调度主要是协调任务对计算机系统内资源(如内存、i/o设备、cpu)的争夺 使用。进程调度又称为cpu调度,其根本任务是按照某种原则为处于就绪状态 的进程分配cpu。由于嵌入式系统中内存和i/o设备一般都和cpu同时归属于 某进程,所以任务调度和进程调度概念相近,很多场合不加区分,下文中提到的 任务其实就是进程的概念。 进程调度可分为"剥夺型调度"和"非剥夺型调度"两种基本方式。所谓"非剥夺型 调度"是指:一旦某个进程被调度执行,则该进程一直执行下去直至该进程结束, 或由于某种原因自行放弃cpu进入等待状态,才将cpu重新分配给其他进程。 所谓"剥夺型调度"是指:一旦就绪状态中出现优先权更高的进程,或者运行的进 程已用满了规定的时间片时,便立即剥夺当前进程的运行(将其放回就绪状态), 把cpu分配给其他进程。 作为实时操作系统,uc/os是采用的可剥夺型实时多任务内核。可剥夺型的实时 内核在任何时候都运行就绪了的最高优先级的任务。uc/os中最多可以支持64 个任务,分别对应优先级0"63,其中0为最高优先级。调度工作的内容可以分 为两部分:最高优先级任务的寻找和任务切换。 其最高优先级任务的寻找是通过建立就绪任务表来实现的。uc/os中的每一个任 务都有独立的堆栈空间,并有一个称为任务控制块tcb(task control block)数据 结构,其中第一个成员变量就是保存的任务堆栈指针。任务调度模块首先用变量 ostcbhighrdy记录当前最高级就绪任务的tcb地址,然后调用os_task_sw() 函数来进行任务切换。 uclinux的进程调度沿用了linux的传统,系统每隔一定时间挂起进程,同时系 统产生快速和周期性的时钟计时中断,并通过调度函数(定时器处理函数)决定进 程什么时候拥有它的时间片。然后进行相关进程切换,这是通过父进程调用fork 函数生成子进程来实现的。 uclinux系统fork调用完成后,要么子进程代替父进程执行(此时父进程已经 sleep),直到子进程调用exit退出;要么调用exec执行一个新的进程,这个时候 产生可执行文件的加载,即使这个进程只是父进程的拷贝,这个过程也不可避免。 当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,使父进程继续 往下执行。 uclinux由于没有mmu管理存储器,其对内存的访问是直接的,所有程序中访 问的地址都是实际的物理地址。操作系统队内存空间没有保护,各个进程实际上 共享一个运行空间。这就需要实现多进程时进行数据保护,也导致了用户程序使 用的空间可能占用到系统内核空间,这些问题在编程时都需要多加注意,否则容 易导致系统崩溃。 由上述分析可以得知,uc/os内核是针对实时系统的要求设计实现的,相对简单, 可以满足较高的实时性要求。而uclinux则在结构上继承了标准linux的多任务 实现方式,仅针对嵌入式处理器特点进行改良。其要实现实时性效果则需要使系 统在实时内核的控制下运行,rt-linux就是可以实现这一个功能的一种实时内 核。 文件系统 所谓文件系统是指负责存取和管理文件信息的机构,也可以说是负责文件的建 立、撤销、组织、读写、修改、复制及对文件管理所需要的资源(如目录表、存 储介质等)实施管理的软件部分。 uc/os是面向中小型嵌入式系统的,如果包含全部功能(信号量、消息邮箱、消 息队列及相关函数),编译后的uc/os内核仅有6"10kb,所以系统本身并没有 对文件系统的支持。但是uc/os具有良好的扩展性能,如果需要的话也可自行 加入文件系统的内容。 uclinux则是继承了linux完善的文件系统性能。其采用的是romfs文件系统, 这种文件系统相对于一般的ext2文件系统要求更少的空间。空间的节约来自于 两个方面,首先内核支持romfs文件系统比支持ext2文件系统需要更少的代码, 其次romfs文件系统相对简单,在建立文件系统超级块(superblock)需要更少的存 储空间。romfs文件系统不支持动态擦写保存,对于系统需要动态保存的数据采 用虚拟ram盘的方法进行处理(ram盘将采用ext2文件系统)。 uclinux还继承了linux中国络操作系统的优势,可以很方便的支持中国络文件系统 且内嵌tcp/ip协议,这为uclinux开发中国络接入设备提供了便利。 由两种操作系统对文件系统的支持可知,在复杂的需要较多文件处理的嵌入式系 统中uclinux是一个不错的选择。而uc/os则主要适合一些控制系统。 操作系统的移植 嵌入式操作系统移植的目的是指使操作系统能在某个微处理器或微控制器上运 行。uc/os和uclinux都是源码公开的操作系统,且其结构化设计便于把与处理 器相关的部分分离出来,所以被移植到新的处理器上是可能的。 以下对两种系统的移植分别予以说明。 (1)uc/os的移植 要移植uc/os,目标处理器必须满足以下要求; ·处理器的c编译器能产生可重入代码,且用c语言就可以打开和关闭中断; ·处理器支持中断,并能产生定时中断; ·处理器支持足够的ram(几k字节),作为多任务环境下的任务堆栈; ·处理器有将堆栈指针和其他cpu寄存器读出和存储到堆栈或内存中的指令。 在理解了处理器和c编译器的技术细节后,uc/os的移植只需要修改与处理器 相关的代码就可以了。具体有如下内容: ·os_cpu.h中需要设置一个常量来标识堆栈增长方向; ·os_cpu.h中需要声明几个用于开关中断和任务切换的宏; ·os_cpu.h中需要针对具体处理器的字长重新定义一系列数据类型; ·os_cpu_a.asm需要改写4个汇编语言的函数; ·os_cpu_c.c需要用c语言编写6个简单函数; ·修改主头文件include.h,将上面的三个文件和其他自己的头文件加入。 (2)uclinux的移植 由于uclinux其实是linux针对嵌入式系统的一种改良,其结构比较复杂,相对 uc/os,uclinux的移植也复杂得多。一般而言要移植uclinux,目标处理器除了 应满足上述uc/os应满足的条件外,还需要具有足够容量(几百k字节以上)外 部rom和ram。 uclinux的移植大致可以分为3个层次: ·结构层次的移植,如果待移植处理器的结构不同于任何已经支持的处理器结构, 则需要修改linux/arch目录下相关处理器结构的文件。虽然uclinux内核代码的 大部分是独立于处理器和其体系结构的,但是其最低级的代码也是特定于各个系 统的。这主要表现在它们的中断处理上下文、内存映射的维护、任务上下文和初 始化过程都是独特的。这些例行程序位于linux/arch/目录下。由于linux所支持 体系结构的种类繁多,所以对一个新型的体系,其低级例程可以模仿与其相似的 体系例程编写。 ·平台层次的移植,如果待移植处理器是某种uclinux已支持体系的分支处理器, 则需要在相关体系结构目录下建立相应目录并编写相应代码。如mc68ez328就 是基于无mmu的m68k内核的。此时的移植需要创建 linux/arch/m68knommu/platform/ mc68ez328目录并在其下编写跟踪程序(实现 用户程序到内核函数的接口等功能)、中断控制调度程序和向量初始化程序等。 ·板级移植,如果你所用处理器已被uclinux支持的话,就只需要板级移植了。板 级移植需要在linux/arch/?platform/中建立一个相应板的目录,再在其中建立相应 的启动代码crt0_rom.s或crt0_ram.s和链接描述文档rom.ld或ram.ld就可以了。 板级移植还包括驱动程序的编写和环境变量设置等内容。 通过对uc/os和uclinux的比较,可以看出这两种操作系统在应用方面各有优劣。 uc/os占用空间少,执行效率高,实时性能优良,且针对新处理器的移植相对简 单。uclinux则占用空间相对较大,实时性能一般,针对新处理器的移植相对复 杂。但是,uclinux具有对多种文件系统的支持能力、内嵌了tcp/ip协议,可 以借鉴linux丰富的资源,对一些复杂的应用,uclinux具有相当优势。例如cisco 公司的 2500/3000/4000 路由器就是基于uclinux操作系统开发的。 总之,操作系统的选择是由嵌入式系统的需求决定的。简单的说就是,小型控制 系统可充分利用uc/os小巧且实时性强的优势,如果开发pda和互联中国连接终 端等较为复杂的系统则uclinux是不错的选择。 本回答由电脑中国络分类达人 李孝忠推荐

D. C或C++的a.out

方法非常的多,你可以阅读下面这个地址的文章来了解可用的方法:

我下面把文本附在后面,但是是文本文件的,不然你的地址的文章看起来舒服

查看源文件预处理结果2010-06-05 19:16
编译C/C++源代码时,源代码首先会被预处理器(preprocessor)进行预处理(preprocess)。
预处理器执行源代码中的预处理指令,如:
——文件包含
#include
——条件编译
#if、 #ifdef、 #ifndef、 #elif、 #else、 #endif
——宏
#define、 #undef、宏标识符、宏扩展
——其他
#error、#line、#pragma
……

预处理之后的结果(即将提交给编译器)与程序员看到的源代码也许会有很大的差异。
尤其在源代码中含有许多错综复杂的宏与条件编译时。
当我们被这些狂乱的宏与条件编译折磨的时候, 如果能看到预处理的结果, 也许会有很大的帮助。

下面将以一个示例说明msvc与gcc中得到预处理结果的方式。

零、 示例

假设我们需要查看 _MSC_VER 与 __GUNC__ 两个宏最终被扩展出的文本:
int main() {
int result =
#if defined(__GNUC__)
__GNUC__
#elif defined(_MSC_VER)
_MSC_VER
#else
#error unknown compiler
#endif
;
return result;
}

该程序很简单, main函数返回一个result,然后立即退出。
而result的值, 根据条件编译得到:
1. 如果是GCC编译器, 那么result赋值为__GNUC__
2. 否则如果是VC编译器, 那么result赋值为_MSC_VER
3. 否则是一个未知的编译器, 错误

接下来, 我们来看看_MSC_VER与__GNUC__这2个宏最终到底被扩展为什么文本。

--------------------------------------------------------------------------------

一、 GCC

1、 -E 选项

-E选项将把预处理的结果,写入stdout。
也就是说, 执行如下命令
gcc -E preporcess_only.c

就能在控制台中得到预处理后的结果,大致如下: # 1 "../preprocess_only.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "../preprocess_only.c"
int main() {
int result =3;
return result;
}

可以看到, __GUNC__ 宏最终被扩展为整数字面量3(GCC 3)。

如果源代码很长, 输出到命令行窗口中查看也许不方便。
如何将其输出到一个文件中呢?

1.1、 重定向

因为-E是输出到stdout, 显然可以将其重定向到另一个文件, 如执行如下命令:
gcc -E preprocess_only.c >stdout.txt

那么stdout.txt中, 就能得到刚才命令行窗口中的内容。

1.2、 -o (小写) 选项

-o选项用于指定出文件名。
对于-c, -o指定的是目标文件名。
对于-S ,-o指定的是汇编文件名。
对于-E, -o自然也可以指定预处理文件名, 如执行如下命令:
gcc -E preprocess_only.c -o output.txt

那么output.txt中会得到“一.1.1”中的stdout.txt和“一.1”中控制台窗口一样的结果。

2、-save-temps 选项

-save-temps选项将保留中间文件:如预处理后的结果文件、汇编代码文件与目标文件。
其中的预处理结果文件(通常有.i后缀)是我们所需要的。

举例:
1、 gcc -save-temps -E preprocess_only.c
0个中间文件。
输出预处理结果, 同“一.1”一样, 输出到控制台窗口中。
对比如下命令:

1.1、 gcc -save-temps -E preprocess_only.c -o temp_output.txt
1.2、 gcc -save-temps -E preprocess_only.c >temp_output.txt
可以看出, -E选项不产生中间文件。 预处理结果就是最终结果。
同时可以使用 -o选项或者重定向, 把结果保存到一个文件中。

2、 gcc -save-temps -S preprocess_only.c
1个中间文件: preprocess_only.i(预处理结果)
1个输出文件:preprocess_only.s(汇编代码)
对比如下命令:

2.1、 gcc -save-temps -S preprocess_only.c -o unknown
得到preprocess_only.i文件,内容是预处理结果,是中间文件。
得到unknown文件,内容是汇编代码, 是最终结果文件。

3、 gcc -save-temps -c preprocess_only.c
2个中间文件: preprocess_only.i与preprocess_only.s。
1个输出文件: preprocess_only.o(目标代码)
对比如下命令:

3.1、 gcc -save-temps -c preprocess_only.c -o unknown
得到preprocess_only.i 与 preprocess_only.s文件,内容分别是预处理结果与汇编代码,是中间结果。
unknown文件, 内容是目标代码,是最终结果文件。

4、 gcc -save-temps preprocess_only.c
3个中间文件: preprocess_only.i、preprocess_only.s、preprocess_only.o。
1个输出文件: a.out(a.exe with mingw)。
对比如下命令:

4.1、 gcc -save-temps preprocess_only.c -o what
得到上述3个文件, 是中间文件。
what文件(what.exe with mingw), 内容是可执行代码, 是最终结果文件。

二、 MSVC

VC6、8、9中与查看预处理相关的选项可以通过如下命令查看:
cl /help

在输出中, 找 -PREPROCESSOR- 这个类别。
其中与预处理结果相关的有如下一些选项:

二.1、/E 选项

/E preprocess to stdout
/E 将预处理定向到 stdout

显然, 这和“一.1”是等价的, 如:
cl /E preprocess_only.c

在命令行窗口中将得到类似结果: #line 1 "preprocess_only.c"
int main() {
int result =
#line 6 "preprocess_only.c"
1200
#line 10 "preprocess_only.c" ;
return result;
}

可以看到, _MSC_VER宏最终被扩展为整数字面值1200(VC6)。

对于较长的源文件, 我们同样希望将结果输出到一个文件中。

二.1.1、重定向

执行:
cl /E preprocess_only.c >stdout.txt

stdout.txt将保存上面的结果。

注意: 在msvc中,没有“一.1.2”的对应物。
执行:
cl /help

在输出中找到-OUTPUT FILES-类别, 可以看到没有命名预处理结果的方式。有两个相似的选项:
/Fe 命名可执行文件。
/Fp 命名预编译头文件。
但不是我们需要的选项。

也许VC认为通过 “/E + 重定向”就可以达到命名输出文件的目的。
所以就没有设计达到此目的的另一种方法。

二.2、/P 选项

/P preprocess to file
/P 预处理到文件

执行:
cl /P preprocess_only.c

将得到 preprocess_only.i

/P会将对 xxx.suffix 的预处理结果输出到 xxx.i 文件中。
没有指定文件名的方式。 如果需要指定输出文件名, 可以使用 “/E + 重定向”

二.3 /EP 选项

/E与/P选项都将保留一部分(源文件)行信息,如“二.1”所示。
如果这是不需要的, 可以使用 /EP选项。

/EP preprocess to stdout, no #line
/EP 预处理到标准输出,没有 #line

如:
cl /EP preprocess_only.c

将得到如下输出:

int main() {
int result = 1200;
return result;
}

同样, 如果需要输出到指定文件, 可以使用重定向。

二.4 其他一些有趣的选项

1. /C (大写)
don't strip comments(不抽出注释)
如果保留注释对理解预处理结果有帮助, 可以使用这个选项。

2. /U /u
/u remove all predefined macros
/u 移除所有预定义的宏

/U<name> remove predefined macro
/U<name> 移除预定义的宏

比如可以通过:
cl /u preprocess_only.c
cl /U_MSC_VER preprocess_only.c

来得到一个 unknown complier错误囧……

3. /D
/D<name><text> define macro
/D<name><text> 定义宏

可以通过:
cl /D__GUNC__=3 preprocess_only.c

来假装gcc编译器

E. Ubuntu编译了新的内核,进入新内核时一直显示载入Linux 5.6.7,载入初始化内存盘咋回事

概述====1)当内核配置了内存盘时, 内核在初始化时可以将软盘加载到内存盘中作为根盘.当同时配置了初始化内存盘(Initail RAM Disk)时, 内核在初始化时可以在安装主盘之前,通过引导程序所加载的initrd文件建立一个内存初始化盘, 首先将它安装成根文件系统, 然后执行其根目录下的linuxrc 文件,可用于在安装主盘之前加载一些内核模块. 等到linuxrc 程序退出后, 再将主盘安装成根文件系统,并将内存初始化盘转移安装到其/initrd目录下.2)当主盘就是initrd所生成的内存初始化盘时, 不再进行重新安装,在DOS下用loadlin加载的抢救盘就是这种工作方式.3)引导程序所加载的initrd为文件系统的映象文件, 可以是gzip压缩的, 也可以是不压缩的.能够识别的文件系统有minix,ext2,romfs三种.4)当内核的根盘为软盘时,内核初始化时会测试软盘的指定部位是否存在文件系统或压缩文件映象, 然后将之加载或解压到内存盘中作为根盘. 这是单张抢救软盘的工作方式.有关代码========; init/main.c#ifdef CONFIG_BLK_DEV_INITRDkdev_t real_root_dev; 启动参数所设定的根盘设备#endifasmlinkage void __init start_kernel(void){ char * command_line; unsigned long mempages; extern char saved_command_line[]; lock_kernel(); printk(linux_banner); setup_arch(&command_line);arch/i386/kernel/setup.c中,初始化initrd_start和initrd_end两个变量 ...#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { ; min_low_pfn为内核末端_end所开始的物理页号,initrd_start,initrd_end在rd.c中定义 printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT); initrd_start = 0; }#endif ... kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); 创建init进程 unlock_kernel(); current->need_resched = 1; cpu_idle();}static int init(void * unused){ lock_kernel(); do_basic_setup(); /* * 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) p(0); (void) p(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. */ 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.");}static void __init do_basic_setup(void){#ifdef CONFIG_BLK_DEV_INITRD int real_root_mountflags;#endif ...#ifdef CONFIG_BLK_DEV_INITRD real_root_dev = ROOT_DEV; ROOT_DEV为所请求根文件系统的块设备 real_root_mountflags = root_mountflags; if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY; else mount_initrd =0; #endif start_context_thread(); do_initcalls(); 会调用partition_setup()中加载内存盘 /* .. filesystems .. */ filesystem_setup(); /* Mount the root filesystem.. */ mount_root(); mount_devfs_fs ();#ifdef CONFIG_BLK_DEV_INITRD root_mountflags = real_root_mountflags; if (mount_initrd && ROOT_DEV != real_root_dev && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) { ; 如果当前根盘为initrd所建立的内存盘 int error; int i, pid; pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); 创建新的任务去执行程序/linuxrc if (pid>0) while (pid != wait(&i)); 等待linuxrc进程退出 if (MAJOR(real_root_dev) != RAMDISK_MAJOR || MINOR(real_root_dev) != 0) { ; 如果原来的根盘不是0号内存盘,则使用原来的根文件系统, ; 并且将内存盘转移到其/initrd目录下 error = change_root(real_root_dev,"/initrd"); if (error) printk(KERN_ERR "Change root to /initrd: " "error %d/n",error); } }#endif}#ifdef CONFIG_BLK_DEV_INITRDstatic int do_linuxrc(void * shell){ static char *argv[] = { "linuxrc", NULL, }; close(0);close(1);close(2); setsid(); 设置新的session号 (void) open("/dev/console",O_RDWR,0); (void) p(0); (void) p(0); return execve(shell, argv, envp_init);}#endif; arch/i386/kernel/setup.c#define RAMDISK_IMAGE_START_MASK 0x07FF#define RAMDISK_PROMPT_FLAG 0x8000#define RAMDISK_LOAD_FLAG 0x4000 #define PARAM ((unsigned char *)empty_zero_page)#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) 可用rdev设置的参数#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))#define INITRD_START (*(unsigned long *) (PARAM+0x218)) 初始化盘映象起始物理地址#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) 初始化盘字节数void __init setup_arch(char **cmdline_p){ ...#ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; 以块为单位 rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);#endif ...#ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { ; max_low_pfn表示内核空间1G范围以下最大允许的物理页号 reserve_bootmem(INITRD_START, INITRD_SIZE); initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0; 转变为内核逻辑地址 initrd_end = initrd_start+INITRD_SIZE; } else { printk("initrd extends beyond end of memory " "(0x%08lx > 0x%08lx)/ndisabling initrd/n", INITRD_START + INITRD_SIZE, max_low_pfn << PAGE_SHIFT); initrd_start = 0; } }#endif ...}; fs/partitions/check.c:int __init partition_setup(void){ device_init(); 包含ramdisk设备的初始化#ifdef CONFIG_BLK_DEV_RAM#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && mount_initrd) initrd_load(); ;如果启动时加载了initrd文件,则用它去初始化根内存盘 else#endif rd_load(); 如果内核配置了内存盘并且根盘指定为软盘则试图将软盘加载为根内存盘#endif return 0;}__initcall(partition_setup);; drivers/block/rd.c:int rd_doload; /* 1 = load RAM disk, 0 = don't load */int rd_prompt = 1; /* 1 = prompt for RAM disk, 0 = don't prompt */int rd_image_start; /* starting block # of image */#ifdef CONFIG_BLK_DEV_INITRDunsigned long initrd_start, initrd_end;int mount_initrd = 1; /* zero if initrd should not be mounted */int initrd_below_start_ok;void __init rd_load(void){ rd_load_disk(0); 加载到0号内存盘}void __init rd_load_secondary(void){ rd_load_disk(1); 加载到1号内存盘}static void __init rd_load_disk(int n){#ifdef CONFIG_BLK_DEV_INITRD extern kdev_t real_root_dev;#endif if (rd_doload == 0) return; if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR 如果根盘是不软盘#ifdef CONFIG_BLK_DEV_INITRD && MAJOR(real_root_dev) != FLOPPY_MAJOR#endif ) return; if (rd_prompt) {#ifdef CONFIG_BLK_DEV_FD floppy_eject();#endif#ifdef CONFIG_MAC_FLOPPY if(MAJOR(ROOT_DEV) == FLOPPY_MAJOR) swim3_fd_eject(MINOR(ROOT_DEV)); else if(MAJOR(real_root_dev) == FLOPPY_MAJOR) swim3_fd_eject(MINOR(real_root_dev));#endif printk(KERN_NOTICE "VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER/n"); wait_for_keypress(); } rd_load_image(ROOT_DEV,rd_image_start, n); 将根软盘加载到n号内存盘}void __init initrd_load(void){ ; 使用initrd设备盘作为源盘去建立内存根盘 rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),rd_image_start,0);}static void __init rd_load_image(kdev_t device, int offset, int unit){ struct inode *inode, *out_inode; struct file infile, outfile; struct dentry in_dentry, out_dentry; mm_segment_t fs; kdev_t ram_device; int nblocks, i; char *buf; unsigned short rotate = 0; unsigned short devblocks = 0; char rotator[4] = { '|' , '/' , '-' , '//' }; ram_device = MKDEV(MAJOR_NR, unit); 建立输出内存盘设备号 if ((inode = get_empty_inode()) == NULL) return; memset(&infile, 0, sizeof(infile)); memset(&in_dentry, 0, sizeof(in_dentry)); infile.f_mode = 1; /* read only */ infile.f_dentry = &in_dentry; in_dentry.d_inode = inode; infile.f_op = &def_blk_fops; init_special_inode(inode, S_IFBLK | S_IRUSR, kdev_t_to_nr(device)); if ((out_inode = get_empty_inode()) == NULL) goto free_inode; memset(&outfile, 0, sizeof(outfile)); memset(&out_dentry, 0, sizeof(out_dentry)); outfile.f_mode = 3; /* read/write */ outfile.f_dentry = &out_dentry; out_dentry.d_inode = out_inode; outfile.f_op = &def_blk_fops; init_special_inode(out_inode, S_IFBLK | S_IRUSR | S_IWUSR, kdev_t_to_nr(ram_device)); if (blkdev_open(inode, &infile) != 0) 打开输入盘文件 goto free_inode; if (blkdev_open(out_inode, &outfile) != 0) 打开输出内存盘文件 goto free_inodes; fs = get_fs(); set_fs(KERNEL_DS); nblocks = identify_ramdisk_image(device, &infile, offset); 鉴定输入盘的文件类型 if (nblocks < 0) 出错 goto done; if (nblocks == 0) { 表示输入盘是gzip文件#ifdef BUILD_CRAMDISK if (crd_load(&infile, &outfile) == 0) 将输入盘文件解压到输出盘文件中去 goto successful_load;#else printk(KERN_NOTICE "RAMDISK: Kernel does not support compressed " "RAM disk images/n");#endif goto done; } /* * NOTE NOTE: nblocks suppose that the blocksize is BLOCK_SIZE, so * rd_load_image will work only with filesystem BLOCK_SIZE wide! * So make sure to use 1k blocksize while generating ext2fs * ramdisk-images. */ if (nblocks > (rd_length[unit] >> BLOCK_SIZE_BITS)) { ; 如果输入盘的尺寸超过了输出内存盘的允许尺寸 printk("RAMDISK: image too big! (%d/%ld blocks)/n", nblocks, rd_length[unit] >> BLOCK_SIZE_BITS); goto done; } /* * OK, time to in the data */ buf = kmalloc(BLOCK_SIZE, GFP_KERNEL); if (buf == 0) { printk(KERN_ERR "RAMDISK: could not allocate buffer/n"); goto done; } if (blk_size[MAJOR(device)]) devblocks = blk_size[MAJOR(device)][MINOR(device)]; 取输入盘的容量#ifdef CONFIG_BLK_DEV_INITRD if (MAJOR(device) == MAJOR_NR && MINOR(device) == INITRD_MINOR) devblocks = nblocks; 如果输入是初始化内存盘,则盘的容量为它的实际尺寸#endif if (devblocks == 0) { printk(KERN_ERR "RAMDISK: could not determine device size/n"); goto done; } printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%d disk%s] into ram disk... ", nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : ""); for (i=0; i < nblocks; i++) { if (i && (i % devblocks == 0)) { printk("done disk #%d./n", i/devblocks); rotate = 0; invalidate_buffers(device); 使输入盘设备缓冲区无效 if (infile.f_op->release) infile.f_op->release(inode, &infile); printk("Please insert disk #%d and press ENTER/n", i/devblocks+1); wait_for_keypress(); if (blkdev_open(inode, &infile) != 0) { printk("Error opening disk./n"); goto done; } infile.f_pos = 0; printk("Loading disk #%d... ", i/devblocks+1); } infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos); outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);#if !defined(CONFIG_ARCH_S390) if (!(i % 16)) { printk("%c/b", rotator[rotate & 0x3]); rotate++; }#endif } printk("done./n"); kfree(buf);successful_load: invalidate_buffers(device); ROOT_DEV = MKDEV(MAJOR_NR, unit); 将根盘设备设置为当前加载的内存盘 if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0");done: if (infile.f_op->release) infile.f_op->release(inode, &infile); set_fs(fs); return;free_inodes: /* free inodes on error */ iput(out_inode); blkdev_put(inode->i_bdev, BDEV_FILE);free_inode: iput(inode);}int __init identify_ramdisk_image(kdev_t device, struct file *fp, int start_block){ const int size = 512; struct minix_super_block *minixsb; struct ext2_super_block *ext2sb; struct romfs_super_block *romfsb; int nblocks = -1; unsigned char *buf; buf = kmalloc(size, GFP_KERNEL); if (buf == 0) return -1; minixsb = (struct minix_super_block *) buf; ext2sb = (struct ext2_super_block *) buf; romfsb = (struct romfs_super_block *) buf; memset(buf, 0xe5, size); /* * Read block 0 to test for gzipped kernel */ if (fp->f_op->llseek) fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; fp->f_op->read(fp, buf, size, &fp->f_pos); ; 读取offset开始的512字节 /* * If it matches the gzip magic numbers, return -1 */ if (buf[0] == 037 && ((buf[1] == 0213) || (buf[1] == 0236))) { printk(KERN_NOTICE "RAMDISK: Compressed image found at block %d/n", start_block); nblocks = 0; goto done; } /* romfs is at block zero too */ if (romfsb->word0 == ROMSB_WORD0 && romfsb->word1 == ROMSB_WORD1) { printk(KERN_NOTICE "RAMDISK: romfs filesystem found at block %d/n", start_block); nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; goto done; } /* * Read block 1 to test for minix and ext2 superblock */ if (fp->f_op->llseek) fp->f_op->llseek(fp, (start_block+1) * BLOCK_SIZE, 0); fp->f_pos = (start_block+1) * BLOCK_SIZE; fp->f_op->read(fp, buf, size, &fp->f_pos); /* Try minix */ if (minixsb->s_magic == MINIX_SUPER_MAGIC || minixsb->s_magic == MINIX_SUPER_MAGIC2) { printk(KERN_NOTICE "RAMDISK: Minix filesystem found at block %d/n", start_block); nblocks = minixsb->s_nzones << minixsb->s_log_zone_size; goto done; } /* Try ext2 */ if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) { printk(KERN_NOTICE "RAMDISK: ext2 filesystem found at block %d/n", start_block); nblocks = le32_to_cpu(ext2sb->s_blocks_count); goto done; } printk(KERN_NOTICE "RAMDISK: Couldn't find valid RAM disk image starting at %d./n", start_block);done: if (fp->f_op->llseek) fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; kfree(buf); return nblocks;}; fs/super.cvoid __init mount_root(void){ struct file_system_type * fs_type; struct super_block * sb; struct vfsmount *vfsmnt; struct block_device *bdev = NULL; mode_t mode; int retval; void *handle; char path[64]; int path_start = -1;#ifdef CONFIG_BLK_DEV_FD if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { 当根盘还是软盘,表示没有加载过内存盘#ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; extern void rd_load_secondary(void);#endif floppy_eject();#ifndef CONFIG_BLK_DEV_RAM printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)/n");#else /* rd_doload is 2 for a al initrd/ramload setup */ ; 只有当加载了initrd但没有释放到内存盘中(mount_inird=0)才有可能到这一步 if(rd_doload==2) rd_load_secondary(); 加载另一张软盘到1号内存盘作为根盘 else#endif { printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER/n"); wait_for_keypress(); } }#endif devfs_make_root (root_device_name); handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME, MAJOR (ROOT_DEV), MINOR (ROOT_DEV), DEVFS_SPECIAL_BLK, 1); if (handle) /* Sigh: bd*() functions only paper over the cracks */ { unsigned major, minor; devfs_get_maj_min (handle, &major, &minor); ROOT_DEV = MKDEV (major, minor); } /* * Probably pure paranoia, but I'm less than happy about delving into * devfs crap and checking it right now. Later. */ if (!ROOT_DEV) panic("I have no root and I want to scream"); bdev = bdget(kdev_t_to_nr(ROOT_DEV)); if (!bdev) panic(__FUNCTION__ ": unable to allocate root device"); bdev->bd_op = devfs_get_ops (handle); path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5); mode = FMODE_READ; if (!(root_mountflags & MS_RDONLY)) mode |= FMODE_WRITE; retval = blkdev_get(bdev, mode, 0, BDEV_FS); if (retval == -EROFS) { root_mountflags |= MS_RDONLY; retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS); } if (retval) { /* * Allow the user to distinguish between failed open * and bad superblock on root device. */ printk ("VFS: Cannot open root device /"%s/" or %s/n", root_device_name, kdevname (ROOT_DEV)); printk ("Please append a correct /"root=/" boot option/n"); panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV)); } check_disk_change(ROOT_DEV); sb = get_super(ROOT_DEV); 取根盘的超级块 if (sb) { fs_type = sb->s_type; goto mount_it; } read_lock(&file_systems_lock); for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) { if (!(fs_type->fs_flags & FS_REQUIRES_DEV)) continue; 根文件系统必须依赖于块设备 if (!try_inc_mod_count(fs_type->owner)) continue; 当文件系统模块正在删除过程中 read_unlock(&file_systems_lock); sb = read_super(ROOT_DEV,bdev,fs_type,root_mountflags,NULL,1);建立根盘的超级块结构 if (sb) goto mount_it; read_lock(&file_systems_lock); put_filesystem(fs_type); 释放对文件系统模块的引用 } read_unlock(&file_systems_lock); panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));mount_it: printk ("VFS: Mounted root (%s filesystem)%s./n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); if (path_start >= 0) { devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT, path + 5 + path_start, NULL, NULL); memcpy (path + path_start, "/dev/", 5); vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start); } else vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root"); 建立根盘的安装结构 /* FIXME: if something will try to umount us right now... */ if (vfsmnt) { set_fs_root(current->fs, vfsmnt, sb->s_root); 设置当前进程的根盘和根目录 set_fs_pwd(current->fs, vfsmnt, sb->s_root); 设置当前进程的当前盘和当前目录 if (bdev) bdput(bdev); /* sb holds a reference */ return; } panic("VFS: add_vfsmnt failed for root fs");}#ifdef CONFIG_BLK_DEV_INITRDint __init change_root(kdev_t new_root_dev,const char *put_old){ 以new_root_dev作为根盘重新安装根文件系统,原来的根转移到put_old目录下 struct vfsmount *old_rootmnt; struct nameidata devfs_nd, nd; int error = 0; read_lock(¤t->fs->lock); old_rootmnt = mntget(current->fs->rootmnt); 取当前进程的根盘安装结构 read_unlock(¤t->fs->lock); /* First unmount devfs if mounted */ if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd)) error = path_walk("/dev", &devfs_nd); if (!error) { if (devfs_nd.mnt->mnt_sb->s_magic == DEVFS_SUPER_MAGIC && devfs_nd.dentry == devfs_nd.mnt->mnt_root) { dput(devfs_nd.dentry); down(&mount_sem); /* puts devfs_nd.mnt */ do_umount(devfs_nd.mnt, 0, 0); up(&mount_sem); } else path_release(&devfs_nd); } ROOT_DEV = new_root_dev; mount_root(); 改变根盘设备重新安装根文件系统#if 1 shrink_dcache(); 清除目录项缓冲中所有自由的目录项 printk("change_root: old root has d_count=%d/n", atomic_read(&old_rootmnt->mnt_root->d_count));#endif mount_devfs_fs (); /* * Get the new mount directory */ error = 0; if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd)) error = path_walk(put_old, &nd); 在新的根盘中寻找put_old目录 if (error) { int blivet; printk(KERN_NOTICE "Trying to unmount old root ... "); blivet = do_umount(old_rootmnt, 1, 0); 卸载原始的根盘 if (!blivet) { printk("okay/n"); return 0; } printk(KERN_ERR "error %d/n", blivet); return error; } /* FIXME: we should hold i_zombie on nd.dentry */ move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old"); mntput(old_rootmnt); path_release(&nd); return 0;}#endifstatic struct vfsmount *add_vfsmnt(struct nameidata *nd, 在虚拟文件系统中的安装点 struct dentry *root, 安装盘的根目录项 const char *dev_name) 安装盘名称{ struct vfsmount *mnt;
————————————————
版权声明:本文为CSDN博主“huanghaibin”的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/huanghaibin/java/article/details/478215

F. gcc 编译优化做了哪些事求解答

用过gcc的都应该知道编译时候的-O选项吧。它就是负责编译优化。下面列出它的说明: -O -O1 Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function. With -O, the compiler tries to rece code size and execution time, without performing any optimizations that take a great deal of compilation time. -O turns on the following optimization flags: -fdefer-pop -fdelayed-branch -fguess-branch-probability -fcprop-registers -floop-optimize -fif-conversion -fif-conver- sion2 -ftree-ccp -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-ter -ftree-lrs -ftree-sra -ftree-rename -ftree-fre -ftree-ch -funit-at-a-time -fmerge-constants -O also turns on -fomit-frame-pointer on machines where doing so does not interfere with debugging. -O doesn’t turn on -ftree-sra for the Ada compiler. This option must be explicitly speci- fied on the command line to be enabled for the Ada compiler. -O2 Optimize even more. GCC performs nearly all supported optimizations that do not involve a space-speed tradeoff. The compiler does not perform loop unrolling or function inlining when you specify -O2. As compared to -O, this option increases both compilation time and the performance of the generated code. -O2 turns on all optimization flags specified by -O. It also turns on the following opti- mization flags: -fthread-jumps -fcrossjumping -foptimize-sibling-calls -fcse-follow-jumps -fcse-skip-blocks -fgcse -fgcse-lm -fexpensive-optimizations -fstrength-rece -fre- run-cse-after-loop -frerun-loop-opt -fcaller-saves -fpeephole2 -fschele-insns -fsched- ule-insns2 -fsched-interblock -fsched-spec -fregmove -fstrict-aliasing -fdelete-null-pointer-checks -freorder-blocks -freorder-functions -falign-functions -falign-jumps -falign-loops -falign-labels -ftree-vrp -ftree-pre Please note the warning under -fgcse about invoking -O2 on programs that use computed gotos. -O3 Optimize yet more. -O3 turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops and -fgcse-after-reload options. -O0 Do not optimize. This is the default. -Os Optimize for size. -Os enables all -O2 optimizations that do not typically increase code size. It also performs further optimizations designed to rece code size. -Os disables the following optimization flags: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version If you use multiple -O options, with or without level numbers, the last such option is the one that is effective. Options of the form -fflag specify machine-independent flags. Most flags have both positive and negative forms; the negative form of -ffoo would be -fno-foo. In the table below, only one of the forms is listed---the one you typically will use. You can figure out the other form by either removing no- or adding it. The following options control specific optimizations. They are either activated by -O options or are related to ones that are. You can use the following flags in the rare cases when "fine-tuning" of optimizations to be performed is desired. -fno-default-inline Do not make member functions inline by default merely because they are defined inside the class scope (C++ only). Otherwise, when you specify -O, member functions defined inside class scope are compiled inline by default; i.e., you don’t need to add inline in front of the member function name. -fno-defer-pop Always pop the arguments to each function call as soon as that function returns. For machines which must pop arguments after a function call, the compiler normally lets argu- ments accumulate on the stack for several function calls and pops them all at once. Disabled at levels -O, -O2, -O3, -Os. -fforce-mem Force memory operands to be copied into registers before doing arithmetic on them. This proces better code by making all memory references potential common subexpressions. When they are not common subexpressions, instruction combination should eliminate the separate register-load. This option is now a nop and will be removed in 4.2. -fforce-addr Force memory address constants to be copied into registers before doing arithmetic on them. -fomit-frame-pointer Don’t keep the frame pointer in a register for functions that don’t need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra regis- ter available in many functions. It also makes debugging impossible on some machines. On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn’t exist. The machine-description macro "FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag. Enabled at levels -O, -O2, -O3, -Os. -foptimize-sibling-calls Optimize sibling and tail recursive calls. Enabled at levels -O2, -O3, -Os. -fno-inline Don’t pay attention to the "inline" keyword. Normally this option is used to keep the com- piler from expanding any functions inline. Note that if you are not optimizing, no func- tions can be expanded inline. -finline-functions Integrate all simple functions into their callers. The compiler heuristically decides which functions are simple enough to be worth integrating in this way. If all calls to a given function are integrated, and the function is declared "static", then the function is normally not output as assembler code in its own right. Enabled at level -O3. -finline-functions-called-once Consider all "static" functions called once for inlining into their caller even if they are not marked "inline". If a call to a given function is integrated, then the function is not output as assembler code in its own right. Enabled if -funit-at-a-time is enabled. -fearly-inlining Inline functions marked by "always_inline" and functions whose body seems smaller than the function call overhead early before doing -fprofile-generate instrumentation and real inlining pass. Doing so makes profiling significantly cheaper and usually inlining faster on programs having large chains of nested wrapper functions. Enabled by default. -finline-limit=n By default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). n is the size of func- tions that can be inlined in number of pseudo instructions (not counting parameter han- dling). The default value of n is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++. Inlining is actually controlled by a number of parameters, which may be specified indivi- ally by using --param name=value. The -finline-limit=n option sets some of these parame- ters as follows: max-inline-insns-single is set to I<n>/2. max-inline-insns-auto is set to I<n>/2. min-inline-insns is set to 130 or I<n>/4, whichever is smaller. max-inline-insns-rtl is set to I<n>. See below for a documentation of the indivial parameters controlling inlining. Note: pseudo instruction represents, in this particular context, an abstract measurement of function’s size. In no way does it represent a count of assembly instructions and as such its exact meaning might change from one release to an another. -fkeep-inline-functions In C, emit "static" functions that are declared "inline" into the object file, even if the function has been inlined into all of its callers. This switch does not affect functions using the "extern inline" extension in GNU C. In C++, emit any and all inline functions into the object file. -fkeep-static-consts Emit variables declared "static const" when optimization isn’t turned on, even if the vari- ables aren’t referenced. GCC enables this option by default. If you want to force the compiler to check if the variable was referenced, regardless of whether or not optimization is turned on, use the -fno-keep-static-consts option. -fmerge-constants Attempt to merge identical constants (string constants and floating point constants) across compilation units. This option is the default for optimized compilation if the assembler and linker support it. Use -fno-merge-constants to inhibit this behavior. Enabled at levels -O, -O2, -O3, -Os. -fmerge-all-constants Attempt to merge identical constants and identical variables. This option implies -fmerge-constants. In addition to -fmerge-constants this considers e.g. even constant initialized arrays or initialized constant variables with integral or floating point types. Languages like C or C++ require each non-automatic variable to have distinct location, so using this option will result in non-conforming behavior. -fmolo-sched Perform swing molo scheling immediately before the first scheling pass. This pass looks at innermost loops and reorders their instructions by overlapping different itera- tions. -fno-branch-count-reg Do not use "decrement and branch" instructions on a count register, but instead generate a sequence of instructions that decrement a register, compare it against zero, then branch based upon the result. This option is only meaningful on architectures that support such instructions, which include x86, PowerPC, IA-64 and S/390. The default is -fbranch-count-reg, enabled when -fstrength-rece is enabled. -fno-function-cse Do not put function addresses in registers; make each instruction that calls a constant function contain the function’s address explicitly. This option results in less efficient code, but some strange hacks that alter the assembler output may be confused by the optimizations performed when this option is not used. The default is -ffunction-cse -fno-zero-initialized-in-bss If the target supports a BSS section, GCC by default puts variables that are initialized to zero into BSS. This can save space in the resulting code. This option turns off this behavior because some programs explicitly rely on variables going to the data section. E.g., so that the resulting executable can find the beginning of that section and/or make assumptions based on that. The default is -fzero-initialized-in-bss. -fmudflap -fmudflapth -fmudflapir For front-ends that support it (C and C++), instrument all risky pointer/array dereferenc- ing operations, some standard library string/heap functions, and some other associated con- structs with range/validity tests. Moles so instrumented should be immune to buffer overflows, invalid heap use, and some other classes of C/C++ programming errors. The instrumentation relies on a separate runtime library (libmudflap), which will be linked into a program if -fmudflap is given at link time. Run-time behavior of the instrumented program is controlled by the MUDFLAP_OPTIONS environment variable. See "env MUD- FLAP_OPTIONS=-help a.out" for its options. Use -fmudflapth instead of -fmudflap to compile and to link if your program is multi-threaded. Use -fmudflapir, in addition to -fmudflap or -fmudflapth, if instrumenta- tion should ignore pointer reads. This proces less instrumentation (and therefore faster execution) and still provides some protection against outright memory corrupting writes, but allows erroneously read data to propagate within a program. -fstrength-rece Perform the optimizations of loop strength rection and elimination of iteration vari- ables. Enabled at levels -O2, -O3, -Os. -fthread-jumps Perform optimizations where we check to see if a jump branches to a location where another comparison subsumed by the first is found. If so, the first branch is redirected to either the destination of the second branch or a point immediately following it, depending on whether the condition is known to be true or false. Enabled at levels -O2, -O3, -Os. -fcse-follow-jumps In common subexpression elimination, scan through jump instructions when the target of the jump is not reached by any other path. For example, when CSE encounters an "if" statement with an "else" clause, CSE will follow the jump when the condition tested is false. Enabled at levels -O2, -O3, -Os. -fcse-skip-blocks This is similar to -fcse-follow-jumps, but causes CSE to follow jumps which conditionally skip over blocks. When CSE encounters a simple "if" statement with no else clause, -fcse-skip-blocks causes CSE to follow the jump around the body of the "if". Enabled at levels -O2, -O3, -Os. -frerun-cse-after-loop Re-run common subexpression elimination after loop optimizations has been performed. Enabled at levels -O2, -O3, -Os. -frerun-loop-opt Run the loop optimizer twice. Enabled at levels -O2, -O3, -Os. -fgcse Perform a global common subexpression elimination pass. This pass also performs global constant and propagation. Note: When compiling a program using computed gotos, a GCC extension, you may get better runtime performance if you disable the global common subexpression elimination pass by adding -fno-gcse to the command line. Enabled at levels -O2, -O3, -Os. -fgcse-lm When -fgcse-lm is enabled, global common subexpression elimination will attempt to move loads which are only killed by stores into themselves. This allows a loop containing a load/store sequence to be changed to a load outside the loop, and a /store within the loop. Enabled by default when gcse is enabled. -fgcse-sm When -fgcse-sm is enabled, a store motion pass is run after global common subexpression elimination. This pass will attempt to move stores out of loops. When used in conjunction with -fgcse-lm, loops containing a load/store sequence can be changed to a load before the loop and a store after the loop. Not enabled at any optimization level. -fgcse-las When -fgcse-las is enabled, the global common subexpression elimination pass eliminates rendant loads that come after stores to the same memory location (both partial and full rendancies). Not enabled at any optimization level. -fgcse-after-reload When -fgcse-after-reload is enabled, a rendant load elimination pass is performed after reload. The purpose of this pass is to cleanup rendant spilling. -floop-optimize Perform loop optimizations: move constant expressions out of loops, simplify exit test con- ditions and optionally do strength-rection as well. Enabled at levels -O, -O2, -O3, -Os. -floop-optimize2 Perform loop optimizations using the new loop optimizer. The optimizations (loop unrolling, peeling and unswitching, loop invariant motion) are enabled by separate flags. -funsafe-loop-optimizations If given, the loop optimizer will assume that loop indices do not overflow, and that the loops with nontrivial exit condition are not infinite. This enables a wider range of loop optimizations even if the loop optimizer itself cannot prove that these assumptions are valid. Using -Wunsafe-loop-optimizations, the compiler will warn you if it finds this kind of loop. -fcrossjumping Perform cross-jumping transformation. This transformation unifies equivalent code and save code size. The resulting code may or may not perform better than without cross-jumping. Enabled at levels -O2, -O3, -Os. -fif-conversion Attempt to transform conditional jumps into branch-less equivalents. This include use of conditional moves, min, max, set flags and abs instructions, and some tricks doable by standard arithmetics. The use of conditional execution on chips where it is available is controlled by "if-conversion2". Enabled at levels -O, -O2, -O3, -Os. -fif-conversion2 Use conditional execution (where available) to transform conditional jumps into branch-less equivalents. Enabled at levels -O, -O2, -O3, -Os. -fdelete-null-pointer-checks Use global dataflow analysis to identify and eliminate useless checks for null pointers. The compiler assumes that dereferencing a null pointer would have halted the program. If a pointer is checked after it has already been dereferenced, it cannot be null. In some environments, this assumption is not true, and programs can safely dereference null pointers. Use -fno-delete-null-pointer-checks to disable this optimization for programs which depend on that behavior. Enabled at levels -O2, -O3, -Os. -fexpensive-optimizations Perform a number of minor optimizations that are relatively expensive. Enabled at levels -O2, -O3, -Os. -foptimize-register-move -fregmove Attempt to reassign register numbers in move instructions and as operands of other simple instructions in order to maximize the amount of register tying. This is especially helpful on machines with two-operand instructions. Note -fregmove and -foptimize-register-move are the same optimization. Enabled at levels -O2, -O3, -Os. -fdelayed-branch If supported for the target machine, attempt to reorder instructions to exploit instruction slots available after delayed branch instructions. Enabled at levels -O, -O2, -O3, -Os. -fschele-insns If supported for the target machine, attempt to reorder instructions to eliminate execution stalls e to required data being unavailable. This helps machines that have slow floating point or memory load instructions by allowing other instructions to be issued until the result of the load or floating point instruction is required. Enabled at levels -O2, -O3, -Os. -fschele-insns2 Similar to -fschele-insns, but requests an additional pass of instruction scheling after register allocation has been done. This is especially useful on machines with a rel- atively small number of registers and where memory load instructions take more than one cycle. Enabled at levels -O2, -O3, -Os. -fno-sched-interblock Don’t schele instructions across basic blocks. This is normally enabled by default when scheling before register allocation, i.e. with -fschele-insns or at -O2 or higher. -fno-sched-spec Don’t allow speculative motion of non-load instructions. This is normally enabled by default when scheling before register allocation, i.e. with -fschele-insns or at -O2 or higher. -fsched-spec-load Allow speculative motion of some load instructions. This only makes sense when scheling before register allocation, i.e. with -fschele-insns or at -O2 or higher. -fsched-spec-load-dangerous Allow speculative motion of more load instructions. This only makes sense when scheling before register allocation, i.e. with -fschele-insns or at -O2 or higher. -fsched-stalled-insns -fsched-stalled-insns=n Define how many insns (if any) can be moved prematurely from the queue of stalled insns into the ready list, ring the second scheling pass. -fno-fsched-stalled-insns and -fsched-stalled-insns=0 are equivalent and mean that no insns will be moved prematurely. If n is unspecified then there is no limit on how many queued insns can be moved prema- turely. -fsched-stalled-insns-dep -fsched-stalled-insns-dep=n Define how many insn groups (cycles) will be examined for a dependency on a stalled insn that is candidate for premature removal from the queue of stalled insns. This has an effect only ring the second scheling pass, and only if -fsched-stalled-insns is used and its value is not zero. +-fno-sched-stalled-insns-dep is equivalent to +-fsched-stalled-insns-dep=0. +-fsched-stalled-insns-dep without a value is equivalent to +-fsched-stalled-insns-dep=1. -fsched2-use-superblocks When scheling after register allocation, do use superblock scheling algorithm. Superblock scheling allows motion across basic block boundaries resulting on faster scheles. This option is experimental, as not all machine descriptions used by GCC model the CPU closely enough to avoid unreliable results from the algorithm. This only makes sense when scheling after register

G. 介绍几种主流嵌入式操作系统的特点,并分析比较 哥们,我现在纠结这个问题,可以给点指点吗

如果你是学习阶段的话,那LINUX和UCOS-II是比较合适的
uc/os和uclinux操作系统是两种性能优良源码公开且被广泛应用的的免费嵌入式操作系统,可以作为研究实时操作系统和非实时操作系统的典范。本文通过对 uc/os和uclinux的对比,分析和总结了嵌入式操作系统应用中的若干重要问题,归纳了嵌入式系统开发中操作系统的选型依据。
两种开源嵌入式操作系统介绍
uc/os和uclinux操作系统,是当前得到广泛应用的两种免费且公开源码的嵌入式操作系统。uc/os适合小型控制系统,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点,最小内核可编译至2k。uclinux则是继承标准linux 的优良特性,针对嵌入式处理器的特点设计的一种操作系统,具有内嵌网络协议、支持多种文件系统,开发者可利用标准linux先验知识等优势。其编译后目标文件可控制在几百k量级。
uc/os是一种免费公开源代码、结构小巧、具有可剥夺实时内核的实时操作系统。其内核提供任务调度与管理、时间管理、任务间同步与通信、内存管理和中断服务等功能。
uclinux是一种优秀的嵌入式linux版本。uclinux是micro-conrol-linux的缩写。同标准linux相比,它集成了标准linux操作系统的稳定性、强大网络功能和出色的文件系统等主要优点。但是由于没有mmu(内存管理单元),其多任务的实现需要一定技巧。
两种嵌入式操作系统主要性能比较
嵌入式操作系统是嵌入式系统软硬件资源的控制中心,它以尽量合理的有效方法组织多个用户共享嵌入式系统的各种资源。其中用户指的是系统程序之上的所有软件。所谓合理有效的方法,指的就是操作系统如何协调并充分利用硬件资源来实现多任务。复杂的操作系统都支持文件系统,方便组织文件并易于对其规范化操作。
嵌入式操作系统还有一个特点就是针对不同的平台,系统不是直接可用的,一般需要经过针对专门平台的移植操作系统才能正常工作。进程调度、文件系统支持和系统移植是在嵌入式操作系统实际应用中最常见的问题,下文就从这几个角度入手对uc/os和uclinux进行分析比较。
进程调度
任务调度主要是协调任务对计算机系统内资源(如内存、i/o设备、cpu)的争夺使用。进程调度又称为cpu调度,其根本任务是按照某种原则为处于就绪状态的进程分配cpu。由于嵌入式系统中内存和i/o设备一般都和cpu同时归属于某进程,所以任务调度和进程调度概念相近,很多场合不加区分,下文中提到的任务其实就是进程的概念。
进程调度可分为"剥夺型调度"和"非剥夺型调度"两种基本方式。所谓"非剥夺型调度"是指:一旦某个进程被调度执行,则该进程一直执行下去直至该进程结束,或由于某种原因自行放弃cpu进入等待状态,才将cpu重新分配给其他进程。所谓"剥夺型调度"是指:一旦就绪状态中出现优先权更高的进程,或者运行的进程已用满了规定的时间片时,便立即剥夺当前进程的运行(将其放回就绪状态),把cpu分配给其他进程
作为实时操作系统,uc/os是采用的可剥夺型实时多任务内核。可剥夺型的实时内核在任何时候都运行就绪了的最高优先级的任务。uc/os中最多可以支持64 个任务,分别对应优先级0~63,
其中0为最高优先级。调度工作的内容可以分为两部分:最高优先级任务的寻找和任务切换。
其最高优先级任务的寻找是通过建立就绪任务表来实现的。uc/os中的每一个任务都有独立的堆栈空间,并有一个称为任务控制块tcb(task control block)数据结构,其中第一个成员变量就是保存的任务堆栈指针。任务调度模块首先用变量 ostcbhighrdy记录当前最高级就绪任务的tcb地址,然后调用os_task_sw() 函数来进行任务切换。
uclinux的进程调度沿用了linux的传统,系统每隔一定时间挂起进程,同时系统产生快速和周期性的时钟计时中断,并通过调度函数(定时器处理函数)决定进程什么时候拥有它的时间片。然后进行相关进程切换,这是通过父进程调用fork 函数生成子进程来实现的。
uclinux系统fork调用完成后,要么子进程代替父进程执行(此时父进程已经 sleep),直到子进程调用exit退出;要么调用exec执行一个新的进程,这个时候产生可执行文件的加载,即使这个进程只是父进程的拷贝,这个过程也不可避免。当子进程执行exit或exec后,子进程使用wakeup把父进程唤醒,使父进程继续往下执行。
uclinux由于没有mmu管理存储器,其对内存的访问是直接的,所有程序中访问的地址都是实际的物理地址。操作系统队内存空间没有保护,各个进程实际上共享一个运行空间。这就需要实现多进程时进行数据保护,也导致了用户程序使用的空间可能占用到系统内核空间,这些问题在编程时都需要多加注意,否则容易导致系统崩溃。
由上述分析可以得知,uc/os内核是针对实时系统的要求设计实现的,相对简单,可以满足较高的实时性要求。而uclinux则在结构上继承了标准linux的多任务实现方式,仅针对嵌入式处理器特点进行改良。其要实现实时性效果则需要使系统在实时内核的控制下运行,rt-linux就是可以实现这一个功能的一种实时内核。
文件系统
所谓文件系统是指负责存取和管理文件信息的机构,也可以说是负责文件的建立、撤销、组织、读写、修改、复制及对文件管理所需要的资源(如目录表、存储介质等)实施管理的软件部分。
uc/os是面向中小型嵌入式系统的,如果包含全部功能(信号量、消息邮箱、消息队列及相关函数),编译后的uc/os内核仅有6~10kb,所以系统本身并没有对文件系统的支持。但是uc/os具有良好的扩展性能,如果需要的话也可自行加入文件系统的内容。
uclinux则是继承了linux完善的文件系统性能。其采用的是romfs文件系统,这种文件系统相对于一般的ext2文件系统要求更少的空间。空间的节约来自于两个方面,首先内核支持romfs文件系统比支持ext2文件系统需要更少的代码,其次romfs文件系统相对简单,在建立文件系统超级块(superblock)需要更少的存储空间。romfs文件系统不支持动态擦写保存,对于系统需要动态保存的数据采用虚拟ram盘的方法进行处理(ram盘将采用ext2文件系统)。
uclinux还继承了linux网络操作系统的优势,可以很方便的支持网络文件系统且内嵌tcp/ip协议,这为uclinux开发网络接入设备提供了便利。
由两种操作系统对文件系统的支持可知,在复杂的需要较多文件处理的嵌入式系统中uclinux是一个不错的选择。而uc/os则主要适合一些控制系统。
操作系统的移植
嵌入式操作系统移植的目的是指使操作系统能在某个微处理器或微控制器上运行。uc/os和uclinux都是源码公开的操作系统,且其结构化设计便于把与处理器相关的部分分离出来,所以被移植到新的处理器上是可能的。
以下对两种系统的移植分别予以说明。
(1)uc/os的移植
要移植uc/os,目标处理器必须满足以下要求;
·处理器的c编译器能产生可重入代码,且用c语言就可以打开和关闭中断;
·处理器支持中断,并能产生定时中断;
·处理器支持足够的ram(几k字节),作为多任务环境下的任务堆栈;
·处理器有将堆栈指针和其他cpu寄存器读出和存储到堆栈或内存中的指令。
在理解了处理器和c编译器的技术细节后,uc/os的移植只需要修改与处理器相关的代码就可以了。
具体有如下内容:
·os_cpu.h中需要设置一个常量来标识堆栈增长方向;
·os_cpu.h中需要声明几个用于开关中断和任务切换的宏;
·os_cpu.h中需要针对具体处理器的字长重新定义一系列数据类型;
·os_cpu_a.asm需要改写4个汇编语言的函数;
·os_cpu_c.c需要用c语言编写6个简单函数;
·修改主头文件include.h,将上面的三个文件和其他自己的头文件加入。
(2)uclinux的移植
由于uclinux其实是linux针对嵌入式系统的一种改良,其结构比较复杂,相对 uc/os,uclinux的移植也复杂得多。一般而言要移植uclinux,目标处理器除了应满足上述uc/os应满足的条件外,还需要具有足够容量(几百k字节以上)外部rom和ram。
uclinux的移植大致可以分为3个层次:
·结构层次的移植,如果待移植处理器的结构不同于任何已经支持的处理器结构,则需要修改linux/arch目录下相关处理器结构的文件。虽然uclinux内核代码的大部分是独立于处理器和其体系结构的,但是其最低级的代码也是特定于各个系统的。这主要表现在它们的中断处理上下文、内存映射的维护、任务上下文和初始化过程都是独特的。这些例行程序位于linux/arch/目录下。由于linux所支持体系结构的种类繁多,所以对一个新型的体系,其低级例程可以模仿与其相似的体系例程编写。
·平台层次的移植,如果待移植处理器是某种uclinux已支持体系的分支处理器,则需要在相关体系结构目录下建立相应目录并编写相应代码。如mc68ez328就是基于无mmu的m68k内核的。此时的移植需要创建 linux/arch/m68knommu/platform/ mc68ez328目录并在其下编写跟踪程序(实现用户程序到内核函数的接口等功能)、中断控制调度程序和向量初始化程序等。
·板级移植,如果你所用处理器已被uclinux支持的话,就只需要板级移植了。板级移植需要在linux/arch/?platform/中建立一个相应板的目录,再在其中建立相应的启动代码crt0_rom.s或crt0_ram.s和链接描述文档rom.ld或ram.ld就可以了。板级移植还包括驱动程序的编写和环境变量设置等内容。
结语
通过对uc/os和uclinux的比较,可以看出这两种操作系统在应用方面各有优劣。 uc/os占用空间少,执行效率高,实时性能优良,且针对新处理器的移植相对简单。uclinux则占用空间相对较大,实时性能一般,针对新处理器的移植相对复杂。但是,uclinux具有对多种文件系统的支持能力、内嵌了tcp/ip协议,可以借鉴linux丰富的资源,对一些复杂的应用,uclinux具有相当优势。例如cisco 公司的 2500/3000/4000 路由器就是基于uclinux操作系统开发的。总之,操作系统的选择是由嵌入式系统的需求决定的。简单的说就是,小型控制系统可充分利用uc/os小巧且实时性强的优势,如果开发pda和互联网连接终端等较为复杂的系统则uclinux是不错的选择。

还有就是如果从开发的工具方便好用,易用的角度来看,那些收费的系统用起来更爽一些

H. UC/OS与Linux操作系统的区别

uc/os比较简单一点,开始学的uc/os,感觉没意思了就开始学linux,感觉ucos只是在单片机上跑跑,像arm9的一般是跑linux。其实先学哪个都差不多,因为学习方法大不相同,差别太大了,ucos太简单,就一些信号量,邮箱什么的,懂了也就会了,linux有点难,涉及知识太多,光是涉及内核以外的编程就需要大把大把的经典书籍去看。兴趣很重要,都靠兴趣过来的。

I. C++ int i[233];我直接这样写代表了什么意思

C++ int i[233];直接这样写代表了,定义了一个整形的数组,共有233个整形元素,数组的名字叫做i。

阅读全文

与编译器superblock相关的资料

热点内容
qt搭建msvc编译器环境 浏览:337
单片机晶振坏了会不会工作不稳定 浏览:767
天天影迷APP显示连接服务器失败怎么回事 浏览:958
钢铁命令同盟第七关怎么过 浏览:4
android底部控件弹出 浏览:42
为程序员而自豪 浏览:580
可以进行c语言编译的文件名 浏览:381
如何使用网络服务器运行程序 浏览:368
江西人社app什么时候开始年审 浏览:498
程序员浪漫求婚 浏览:955
什么是通知app 浏览:582
小米4文件夹怎么清理 浏览:150
linuxsudo安装 浏览:129
程序员培训班几号开班 浏览:264
教育官网源码 浏览:781
遮挡对tof算法的影响 浏览:508
人没了车怎么解压 浏览:895
国外app怎么支付 浏览:88
转转app鬼市怎么进 浏览:439
新用户免费云服务器 浏览:331