快报:这11道嵌入式笔试题,你会几道?

来源面包芯语   2023-04-25 13:20:15

2.2 步骤详解

当前程序的执行状态是保存在CPSR里面的,异常发生时,要保存当前的CPSR里的执行状态到异常模式里的SPSR里,将来异常返回时,恢复回CPSR,恢复执行状态。

硬件自动根据当前的异常类型,将异常码写入CPSR里的M[4:0]模式位,这样CPU就进入了对应异常模式下。不管是在ARM状态下还是在THUMB状态下发生异常,都会自动切换到ARM状态下进行异常的处理,这是由硬件自动完成的,将CPSR[5] 设置为 0。同时,CPU会关闭中断IRQ(设置CPSR 寄存器I位),防止中断进入,如果当前是快速中断FIQ异常,关闭快速中断(设置CPSR寄存器F位)。


【资料图】

当前程序被异常打断,切换到异常处理程序里,异常处理完之后,返回当前被打断模式继续执行,因此必须要保存当前执行指令的下一条指令的地址到LR_excep(异常模式下LR,并不存在LR_excep寄存器,为方便读者理解加上_excep,以下道理相同),由于异常模式不同以及ARM内核采用流水线技术,异常处理程序里要根据异常模式计算返回地址。

该操作是CPU硬件自动完成的,当异常发生时,CPU强制将PC的值修改为一个固定内存地址,这个固定地址叫做异常向量。

3. ARM状态与Thumb状态的切换指令是什么?举例说明

概念

切换指令

BX指令:R0[0]=1,则执行BXR0指令将进入thumb状态

BX指令:R0[0]=0,则执行BXR0指令将进入arm状态

举例

AREAArm_to_Thumb,CODE,READONLYENTRYCODE32startldrr0,=aaa+1movr3,#18bxr0CODE16aaamovr1,#12movr2,#10END

4. 请问ARM支持哪几种异常类型,请列出其中断向量地址。

异常源分类

要进入异常模式,一定要有异常源,ARM规定有7种异常源:

异常向量表是一段特定内存地址空间,每种ARM异常对应一个字长空间(4Bytes),正好是一条32位指令长度,当异常发生时,CPU强制将PC的值设置为当前异常对应的固定内存地址。

异常向量表:

异常向量表

跳入异常向量表操作是异常发生时,硬件自动完成的,剩下的异常处理任务完全交给了程序员。由上表可知,异常向量是一个固定的内存地址,我们可以通过向该地址处写一条跳转指令,让它跳向我们自己定义的异常处理程序的入口,就可以完成异常处理了。

正是由于异常向量表的存在,才让硬件异常处理和程序员自定义处理程序有机联系起来。异常向量表里0x00000000地址处是reset复位异常,之所以它为0地址,是因为CPU在上电时自动从0地址处加载指令,由此可见将复位异常安装在此地址处也是前后结合起来设计的,不得不感叹CPU设计师的伟大,其后面分别是其余7种异常向量,每种异常向量都占有四个字节,正好是一条指令的大小,最后一个异常是快速中断异常,将其安装在此也有它的意义,在0x0000001C地址处可以直接存放快速中断的处理程序,不用设置跳转指令,这样可以节省一个时钟周期,加快快速中断处理时间。

存储器映射地址0x00000000是为向量表保留的。在有些处理器中,向量表可以选择定位在高地址0xFFFF0000处【可以通过协处理器指令配置】,当今操作系统为了控制内存访问权限,通常会开启虚拟内存,开启了虚拟内存之后,内存的开始空间通常为内核进程空间,和页表空间,异常向量表不能再安装在0地址处了。

比如Cortex-A8系统中支持通过设置CP15的C12寄存器将异常向量表的首地址放置在任意地址。

5. 在Linux中与外设的通信是层次化的,请画出外设寻址的分层模型。

本题有点没整明白在问什么,如果分析有误,欢迎给我留言。

Linux下一切皆文件,应用程序访问外设都需要通过驱动来操作外设。

Linux下设备类型分为:字符设备、块设备、网络设备。

架构图参考下图:

此外题目问画出外设寻址的分层模型,我想可能是想问如何访问SDRAM。

如下图所示,CPU要访问SDRAM或者flash需要先通过AHB总线,然后再通过外存接口控制器对外存寻址,然后进行数据的读写。

访问外存

6. 中断是嵌入式中重要的组成部分,中断服务程序通常需要满足哪些要求?

中断是嵌入式系统中重要的组成部分,很多外设和cpu的交互都是通过中断的方式进行。衡量一个OS的实时性很重要的一个特性就是中断的响应时间,长时间处于中断状态所以中断反应。一旦进入中断状态,cpu是在占用的,所以必须快速的出中断,否则会影响整体性能。

中断服务程序需要满足如下要求:

7. 使用共享资源时,为了使之满足互斥条件,通常有哪些方法?

共享资源也叫临界资源,在进程和线程、内核下都有自己的同步互斥机制,仅考虑互斥的话,分开来看:

进程:信号量(值设置为1)线程:互斥体、信号量(也叫信号灯)内核:原子操作,自旋锁,信号量,互斥锁

8. 嵌入式操作系统有哪些同步通信服务?

Linux进程间通信方式主要有

9. 请举例说明什么是可重入函数与不可重入函数。

1. 不可重入函数

在实时系统的设计中,经常会出现多个任务调用同一个函数的情况。如果有一个函数不幸被设计成为不可重入这样:那么不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。这样的函数是不安全的函数,也叫不可重入函数。

满足下列条件的函数多数是不可重入的

2. 可重入函数

可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。可重入函数或者只使用局部变量,即保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护

如何写出可重入函数在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用缺省态(auto)局部变量,写出的函数就将是可重入的。如果必须访问全局变量,记住利用互斥信号量来保护全局变量。或者调用该函数前关中断,调用后再开中断。Linux常见的可重入函数

3. 举例

A) 可重入函数

voidstrcpy(char*dest,char*src){while(*dest++=*src++);*dest=0;}

B) 不可重入函数1

chartemp;//全局变量voidswapchar(char*x,char*y){temp=*x;*x=*y;*y=temp;//访问了全局变量}

C) 不可重入函数2

voidswapchar(char*x,char*y){chartemp;//局部变量temp=*x;*x=*y;*y=temp;//使用了全局变量}

4. 不可重入函数的使用注意事项

intExam=0;unsignedintexample(intpara){unsignedinttemp;Exam=para;//(**)temp=Square_Exam();returntemp;}

Exam 是 int 型全局变量,函数 Squre_Exam() 返回 Exam 平方值,所以函数example()不具有可重入性。

此函数若被多个进程调用的话,其结果可能是未知的,因为当Exam=para语句刚执行完后,另外一个使用本函数的进程可能正好被激活,那么当新激活的进程执行到此函数时,将使 Exam 赋与另一个不同的 para 值,所以当控制重新回到 “temp = Square_Exam( )” 后,计算出的temp很可能不是预想中的结果。

此函数应如下改进:

intExam=0;unsignedintexample(intpara){unsignedinttemp;[申请信号量操作]//(1)加锁Exam=para;temp=Square_Exam();[释放信号量操作]//解锁returntemp;}

申请不到“信号量”,说明另外的进程正处于给 Exam 赋值并计算其平方过程中(即正在使用此信号),本进程必须等待其释放信号后,才可继续执行。

若申请到信号,则可继续执行,但其它进程必须等待本进程释放信号量后,才能再使用本信号。

5. 可重入函数

可重入函数

10. int *fun(void)与int (*fun)(void)区别?

int *fun(void)fun是一个函数名,该函数的参数是void型,返回值是 int *

int (*fun)(void)fun是一个函数指针,该指针用于指向一个函数,函数的参数是void型,返回值是 int即类型如下:

intfunction(void){return0;}

11. 32位系统上有如下C程序:

structperson{inta;unsignedshortintm;charb;char*q;charc;};charstr[]="yikoulinux";char*p=str;intn=10;

则sizeof(str)、sizeof(p)、sizeof(n)、sizeof(int)、sizeof(struct person)的值分别是:

编写测试程序

1#include23intmain(intargc,constchar*argv[])4{5structperson6{7inta;8unsignedshortintm;9charb;10char*q;11charc;12};13charstr[]="yikoulinux";14char*p=str;15intn=10;1617structpersonpe;1819printf("sizeof(str):%dsizeof(p):%dsizeof(n):%dsizeof(int):%dsizeof(structperson):%d\n",si2021printf("%p\n%p\n%p\n%p\n%p\n",22&pe.a,&pe.m,&pe.b,&pe.q,&pe.c);23return0;24}

编译执行:

sizeof(str):11sizeof(p):4sizeof(n):4sizeof(int):4sizeof(structperson):160xbf917d180xbf917d1c0xbf917d1e0xbf917d200xbf917d24

解释:

热门推荐

猜你喜欢