简答题6-7个
编程题{GNU汇编,几个代码+结构体,makefile}
P4 嵌入式系统体系结构
嵌入式系统是“控制、监视或者辅助设备、机器和车间运行的装置
P8 嵌入式操作系统定义
硬件软件操作系统
(1)操作系统可以有效管理越来越复杂的系统资源。
(2)操作系统可以把硬件虚拟化,使得开发人员从繁忙的驱动程序移植和维护中解脱出来。
(3)操作系统可以提供库函数、驱动程序、工具集以及应用程序
P12 嵌入式系统设计过程
按照常规的工程设计方法,嵌入式系统的设计可以分成三个阶段:分析、设计和实现。
分析阶段是确定要解决的问题及需要完成的目标,也常常被称为需求阶段;
设计阶段主要是解决如何在给定的约束条件下完成用户的要求:
实现阶段主要是解决如何在所选择的硬件和软件的基础上进行整个软、硬件系统的协调实现。
P23-24 ARM指令类型
定义:
ARM指令集属于加载/存储型指令,指令的操作数都储存在寄存器中,处理结果直接放回到目的寄存器中,而想要访问存储器需要使用专门的存储器访问指令。
ARM 指令集可以分为6类,分别是跳转指令、数据处理指令、存储器访问指令、协处理器指令、杂项指令和饱和算术指令。
P25 指令格式
ARM 指令一般由操作码、目的寄存器、操作数几部分组成,并可以配合条件码,S后缀等可选项目,以完成更复杂操作,它的格式一般为:<opcode> {<cond>} {S} <Rd> , <Rn> {, <shift_op2 >}
指令中心<>内的项目是必需的,例如 opcode,Rd,Rn等,{}内的项目是可选的,可以根据功能需求选择,各个项目的具体含义如表2-7所示。
P27 ARM寻址
立即寻址
立即寻址也可被称为立即数寻址,这种方式比较特别,其实并不需要真正的“寻址”,因为操作数本身已经包含在指令中了,读取指令后可以立即得到操作数,而不需要去物理内存得到相应内容。这个给出的操作数叫立即数,它一般以“#”为前缀,“#Ox”“# Od”“#Ob"开头的计数用来表示十六进制,十进制和二进制。举例:ADD R1,R1,#0x1; #R1<- R1+1
寄存器寻址
寄存器寻址也是一种不需要访问存储器内容的寻址方式,指令中直接指明操作数所在的寄存器,执行时处理器直接访问寄存器获取操作数,如下面的指令。ADDR1,R1,R2; #R1 <- R+R2
MOV R1,RO; #R1 <- RO
寄存器间接寻址
寄存器间接寻址的指令中虽然也是指定寄存器,但并不是直接拿寄存器中的值来进行运算操作,此时寄存器中储存的是地址,处理器需要根据这个地址从存储器中获取操作数。所以寄存器间接寻址是需要进行存储器访问的,所以执行效率比寄存器寻址要慢。相应的指令举例如下。STR R1,[R2]; 将R1的值存人以照内容为地址的存储器中
SWP R1, R1,[R2]; 交换以R2为地址的存储器内容和R1内容
以下做了解:
多寄存器寻址:
多寄存器寻址方式可以在同一条指令中完成多个寄存器数据的传送,最多可以传送16个通用寄存器。下面举两个例子。
LDMIARO,{R1,R2,R3,R4,R5}; #R1<-RO,R2<- RO+4,…,R5<- RO + 16
STMIA RO,{R2-R5,R7}; #RO<-R2,RO+4<-R3,…,RO+12<-R5,RO+ 16<-R7
LDM 和 STM 指令后缀 IA 的作用是每次加载/存储操作后,RO的值按字长度增加,从而完成连续存储单元和多个寄存器之间内容的传递。寄存器的顺序一般都是由大到小排列,连续的寄存器可用“”连接,不连续的寄存器之间用“,”分隔。
堆栈寻址:
堆栈是一个后进先出的数据结构,堆栈寻址方式会有一个指针,始终指向存储单元的栈顶,这个指针需要用一个专门的寄存器来存放,这个寄存器一般是R13,当然用户也可以自己指定。如果堆栈指针总是指向最后压入堆栈的数据,称为满堆栈(Full Stack),当堆栈指针指向下一个空位置时,称为空堆栈(Empty Stack)。按照地址增长方式,堆栈又可以分成递增堆栈(Ascending Stack)和递减堆栈(Descending Stack)。递增堆栈从低地址向高地址生长,递减堆栈则相反。通过组合,共有4种堆栈类型:满递增堆栈(Full Ascending,指令如 LDMFA,STMFA),空递增堆栈(Empty Ascending,指令如 LDMEA,STMEA),满递减堆栈(FulDescending,指令如 LDMFD,STMFD),空递减堆栈(Empty Descending,指令如 LDMED.STMED),如图 2-2所示。
堆栈寻址的例子如下STMED SP!,{R1-R7,LR}; #将R-R7,LR存放到堆栈中,这条指令一般用来保护现场
P70 汇编语言优点
尽管如此,在嵌入式软件开发中还是有很多地方需要用到汇编语言,比如对硬件相关的操作、中断处理等。另外,一些对性能要求比较高的模块,也需要用汇编来编写才能达到目的。
P73-74 makefile
什么是make和makefile
make 执行编译工作之前,需要一个命名为 Makefile 的特殊文件来告诉它做什么以及怎么做。Makefie由一系列规则组成,每条规则说明要生成哪些目标文件、生成目标文件所依赖的其他文件以及生成目标文件所需要的命令。它的基本规则格式如下
TARGET… : PREREOUISITES…
CОMMAND
(1)TARGET:规则的目标。通常是最后所要生成的可执行文件名或者为了生成这个目标而必需的中间过程的目标文件名。另外,目标也可以是一个make 执行动作的名称,如目标“clean”,这类目标被称为“伪目标”,具体看后面的例子。
(2)PREREQUISITES:规则的依赖。生成规则目标所需要的文件列表,通常一个目标依赖于一个或多个文件。当然,像“clean”这种伪目标并不存在任何可依赖的文件。
(3)COMMAND:规则执行的命令。生成规则目标所需要执行的命令,可以是 shell 下面的任何命令组合。注意,命令前必须用 Tab 缩进,Tab 告诉 make 此行是一个命令行。make通过查看时间戳来确认依赖文件是否比目标文件更新,如果是则重新执行这条规则的命令,并执行依赖这些中间目标文件的规则,层层推进,最后生成新的结果文件。
Makefile示例(需要会解释):
Makefile 涉及的内容很多,在这里通过一个小的例子来说明 Makefile 的基本写法。此例子由两个头文件(.h)和7个源文件(.c)组成。最终生成的可执行文件依赖于上面所述的源文件和头文件,并由 Makefile 来描述如何创建。Makefile 文件的内容如下。
# Makefile Example for Math
math : main.o display.o plus.o minus.o multi.o divide. o mod. O
gcc -o math main.o display.o plus.o minus.o multi.o divide.o mod.o
main.o :main,c defs. h display.h
gcc -c main.c
display.o :display.c defs. h display. h
gcc -c display.c
plus.o :plus.c defs.h
gcc -c plus.c
minus.o:minus.c defs.h
gcc -c minus.c
multi.o: multi.c defs.h
gcc -c multi.c
divide.o: divide.c defs.h
gcc -c divide.c
mod.o:mod.c defs.h
gcc -c mod.c
.PHONY:clean
clean :
- rm main.o display.o plus.o minus.o multi.o divide.o mod.o
1.目标math
math : main.o display.o plus.o minus.o multi.o divide.o mod.o
gcc -o math main.o display.o plus.o minus.o multi.o divide.o mod.o
•目标:math 是最终生成的可执行文件。
•依赖:math 依赖于 main.o、display.o、plus.o、minus.o、multi.o、divide.o 和 mod.o 这些目标文件(.o 文件)。
•规则:当这些目标文件中的任何一个被更新时,make 会执行以下命令:gcc -o math main.o display.o plus.o minus.o multi.o divide.o mod.o
这条命令的作用是将所有的目标文件链接成一个可执行文件 math。
2.目标文件生成规则main.o : main.c defs.h display.h
gcc -c main.c
•目标:main.o 是一个目标文件。
•依赖:main.o 依赖于 main.c、defs.h 和 display.h。
•规则:当 main.c、defs.h 或 display.h 中的任何一个被更新时,make 会执行以下命令:gcc -c main.c
这条命令的作用是将 main.c 编译成目标文件 main.o。
类似的规则适用于其他目标文件.
3.清理规则
.PHONY: clean
clean :
-rm main.o display.o plus.o minus.o multi.o divide.o mod.o
•.PHONY:这是一个特殊的声明,表示 clean 是一个伪目标,它不会生成一个名为 clean 的文件。
•目标:clean 用于清理生成的文件。
•规则:当运行 make clean 时,make 会执行以下命令:-rm main.o display.o plus.o minus.o multi.o divide.o mod.o
这条命令的作用是删除所有目标文件(.o 文件)。注意,命令前的 - 表示即使删除文件失败(例如文件不存在),make 也不会报错。
Makefile的简化:
# 目标文件列表
OBJS = main.o display.o plus.o minus.o multi.o divide.o mod.o
# 最终目标
math: $(OBJS)
gcc -o math $(OBJS)
# 通用规则:编译每个 .c 文件为 .o 文件
%.o: %.c defs.h
gcc -c $<
# 清理生成的文件
.PHONY: clean
clean:
rm -f $(OBJS) math
makeflie内置变量:
在上面的例子中,生成 math 的规则如下所示,
math : main.o display.o plus.o minus.o multi.o divide. o mod, ogee -o math main.o display.o plus.o minus.o multi.o divide. o mod.o
在这里,*.0文件列表被重复了两次,假如要添加一个新的.0文件,那就需要在两处分别添加。随着工程代码越来越复杂,Makefie 文件也会变得更加庞大,有时候需要添加的地方不止一两处,很容易造成遗漏,从而导致编译错误。解决这个问题的方法就是定义变量,如定义一个 OBJS 的变量。
OBJS = main.o display.o plus.o minus.o multi.o divide.o mod.o
math: $(OBJS)
gcc -o math $(OBJS)
使用内置变量可以进一步简化 Makefile的书写,比如上面的例子中使用内置变量代替用户自定义的变量,修改如下。
math : $(OBJS)
gcc $^-o $@
Makefile规则:
上面介绍的所有规则都属于显式规则,它们的共同点是,在规则描述语句中都包含目标文件、依赖文件和所要执行的命令。但有时候为了简化 Makefile的编写,往往除了显式规则以外,还会使用大量的隐含规则和模式规则。
隐含规则为 make 提供了编译一类目标文件的通用方法,并且不需要在: Makefile 中明确地给出编译特定目标文件所需要的详细描述。例如,make对C文件的编译过程由.c源文件编译生成.0目标文件。当Makefile 中出现一个,0文件目标时,make 会使用这个通用方式将后缀为.c的文件编译称为目标的.0文件。编译,c文件为,0文件的隐含规则所执行的命令如下。$(CC) -c.$ (CPPELAGS) $(CFLAGS)
模式规则的结构类似于显式规则,但是在模式规则中,目标包含模式符号“%”。包含模式符号的目标用来匹配一个文件名。相应地,规则的依赖文件中同样可以包含“%”,它的取值同目标中的“%"保持一致。与隐含规则不同的是,模式规则中允许使用变量。下面这个例子使用了模式规则。
%.o: %.c
$(CC) $(CELAGS) $< -o $ @
OBJS = k1.o k2.o #定义目标文件列表
CC=gcc
CFLAGS=-Wall-O-g #所有警告 优化 调试信息
David: $(OBJS) #目标david依赖OBJS变量定义的目标文件列表
$(CC) $^ -o $@
K1.o:k1.c k1.h
$(CC) $( CFLAGS) -c $ < -o $@
P107 Boot Loader技术
概念:
概括地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备和建立内存空间的映射图,从面将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
P109 Boot Loader典型结构
Boot Loader 阶段1介绍:
1.基本的硬件初始化
2.为加载阶段 2 准备 RAM 空间
3.复制阶段2 到RAM 中
4.设置堆栈指针
5.跳转到阶段2人口
Boot Loader 阶段2介绍:
1.初始化阶段要使用到的硬件设备
2.检测系统的内存映射
3.加戟内核映像和根文件系统映像
4.设置内核启动参数
5.调用内核
P141 ARM-Linux存储机制
内存映射模型
物理地址与逻辑地址
物理地址是实际存在于内存中的地址,用于访问物理内存中的数据。
逻辑地址是程序中使用的地址,也称为虚拟地址。它是程序在运行时通过代码生成的地址,用于访问内存中的数据。
P142 linux映射机制建立过程
Linux在启动初始化的时候依次调用 start_kernel()→setup_arch()→pageing_init()→memtable_init()→create_mapping(),下面是 create_mapping()函数。
static void __init create-mapping(struct map_desc * md)
{
unsigned long virt, length;
int prot_sect, prot_pte;
long off;
if(md->prot_read && md->prot_write&&!md->cacheable && !md->bufferable)
{
printk(KERN WARNING "Security risk: creating user”
“accessible mapping for 0x$ 08lx at 0x $ 08lx\n",
md->physical,md->virtual);
}
if(md->virtual != vectors base()&& md-> virtual < PAGE OFESET)
{
printk(KERN WARNING "MM: not creating mapping for"
0x % 08lx at 0x % 08lx in user region\n"
md->physical,md->virtual);
}
prot pte =LPTE PRESENT | L_PTE _YOUNG I L_PTE_DIRTY |
(md->prot read ?L_PTE_USER :0) |
(md-> prot write ? L_PTE_WRITE :0) |
(md->cacheable ?L_PTE_CACHEABLE :0) |
(md-> bufferable ?L_PTE_BUFFERABLE :0);
prot_Sect = PMD_TYPE_SECTIPMD_DOMAIN(md->domain) |
(md-> prot read ? PMD_SECT_AP_READ :0) |
(md-> prot write ? PMD_SECT_AP_WRITE :0) |
(md->cacheable ?PMD_SECT_CACHEABLE :0) |
(md->bufferable ?PMD_SECT_BUFFERABLE :0);
virt= md->virtual;
off=md->physical-virt;
length = md->length;
while((virt & 0xfffff||(virt + off)&0xfffff)s& length>= PAGE SIZE)
{
alloc_init_page(virt,virt + off,md->domain, prot_pte);
virt +=PAGE SIZE;
length-= PAGE SIZE;
}
while(length>=PDIR SIE)
{
alloc init section(virt,virt + off,prot sect);
virt + PGDIR SIZE;length-= PGDIRSIZE;
}
while(length>=PAGE SIE)
{
alloc init page(virt,virt + off,md->domain, prot_pte);
virt +=PAGE SIZE;
length-= PAGE SIZE;
}
}
P152-153 Linux进程调度
进程调度时机
进程调度的时机按大的来分有主动调度和被动调度两种方式:
主动的调度随时可以进行,在内核里通过 schedule()启动调度,或者将进程状态设置为 TASK_INTERRUPTIBLETASK_JNINTERRUPTIBLE,或者在用户空间通过 pause();
被动调度发生在系统调用返回的前夕、中断异常处理返回前、用户态处理软中断返回前。
进程调度依据
在 task_struct结构中,可以看到以下4项:policy、priority、counter、rt_priority。这4项就是选择进程的依据
policy 是进程的调度策略,用来区分实时进程和普通进程,实时进程会优先于普通进程运行;
priority 是进程(包括实时和普通)的静态优先级;
counter 是进程剩余的时间片,它的起始值就是 priority 的值,由于在后面 counter 计算一个处于可运行状态的进程值的运行程度goodness 时起重要作用,因此,counter 也可以看作是进程的动态优先级。
rt_priority 是实时进程特有的,用于实时进程间的选择。
Schedule()函数
主动或被动调用schedule函数对应着进程是主动调度还是被动调度。
在内核应用中直接调用schedule(),通常发生在因为等待内核事件而需要将进程置于挂起(休眠)状态的时候。这时应该主动请求调度以方便其他进程使用CPU。其过程可分为以下4步。
(1)将进程添加到事件等待队列中;
(2)置进程状态为 TASK_INTERRUPTIBLE(或 TASK UNINTERRUPTIBLE);
(3)在循环中检查等待条件是否满足,不满足则调用 schedule(),满足就退出循环。
(4)将进程从事件等待队列中删除。
P157 ARM-Linux中断管理
什么是中断:
从系统的角度来看,中断是一个流程,一般来说,它要经过三个环节:中断响应,中断处理,中断返回。
在外部提供一个“集线器”,称为“中断控制器”。它为外设提供多条中断请求线,但是将这些中断请求线(相或)合并成一条。与此同时,在中断控制器中还要提供一个寄存器,记录下当前的(综合)中断请求来自哪几条外部中断请求线。而CPU则可以像访问外设一样地读出这个寄存器的内容,以确定中断请求的来源。
P158 中断控制器数据结构
每一个中断控制器都由 strcut hw_interrupt_type 数据结构表示。
struct hw_interrupt_type {
const char * typename;
unsigned int(*startup)(unsigned_int_irg);
void(*shutdown)(unsigned_int_irg);
void(* enable)(unsigned_int_irg);
void(* ack)(unsigned_int_irg);void(* end)(unsigned_int_irg);
void(* set_affinity)(unsiged int_irg,unsigned long mask);
};
每一个中断请求线都由一个 struct irqdesc 数据结构表示。
typedef struct {
unsigned int status; /*IRQ 状态*/
hw irg controller * handler;
struet irgaction *action;
unsigned int depth;
spinlock t lock;
}cacheline aligned irg dese t;
此外还有一个中断请求队列数组 irq_desc_irq_desc[NR_IRQS];,具体中断处理程序则在数据结构 struct irgaction中。
struct irgaction{
void(*handler)(int,void *,struct pt regs *);
//指向具体中断服务程序
unsigned long flags;
unsigned long mask;
constcharevoid * dev id;
struct irgaction *next;
};
P178 根文件系统
什么是根文件系统
简单来说,就是系统挂载的第一个文件系统。本质来说,根文件系统就是一种目录结构根文件系统和普通的文件系统的区别在于,根文件系统要包括Linux启动时所必需的目录和关键性的文件
P184 设备分类
Linux支持三类硬件设备:字符设备、块设备和网络设备,
P186 设备驱动程序结构
(1)驱动程序与操作系统内核的接口。这是通过 include/linux/fs.h中的 file_operations数据结构来完成的,后面将会介绍这个结构。
(2)驱动程序与系统引导的接口。这部分利用驱动程序对设备进行初始化。
(3)驱动程序与设备的接口。这部分描述了驱动程序如何与设备进行交互,这与具体设备密切相关。
P191 linux统一设备模型的基本结构
Linux2.6设备驱动模型的基本元素是设备类结构classes、总线结构 bus、设备结构devices、驱动结构 drivers,它们的对应关系见表 9-1。
shell命令pwd;vi;mkdir;ls;cat;cd;tar jvrf; nasm(调用nasm编译器);
GNU汇编格式
.global _start @ 声明入口点为_start
@ 数据段定义
.section .data
message:
.ascii "hellow,world\n\0" @ 定义以空字符结尾的字符串
@ 代码段定义
.section .text
_start:
@ 系统调用:write(1, message, 13)
mov r7, #4 @ 系统调用号4 = write
mov r0, #1 @ 文件描述符1 = stdout
ldr r1, =message @ 加载字符串地址到r1
mov r2, #13 @ 字符串长度(12字符 + 1换行)
swi #0 @ 执行系统调用
stop:
B stop
.end
评论0
暂时没有评论