就业模拟测试题-LINUX 驱动、系统底层工程师职位
本试卷从考试酷 examcoo 网站导出,文件格式为 mht,请用 WORD/WPS 打开,并另存为
doc/docx 格式后再使用
试卷编号:143921
试卷录入者:yisonghua(华清远见)
试卷总分:80
出卷时间:2012-09-13 14:53
答题时间:150 分钟
姓名:
学号:
班级:
说明: 以下个体中的分数是说明题目的重要性而言的,并不是具体题目的考试分数,此试
卷中的题目主要是出现在笔试之后的面试中,大部分题目要直接能说的出来,多去整理,
完善自己的表达
1.你平常是怎么用 C 写嵌入式系统的死循环的? [3 分]
参考答案:
while(1)
{
//...
}
或者
for(;;)
{
//...
{
2.写一条命令,实现在 dir 以及其子目录下找出所有包含“hello world”字符串的文件[2
分]
参考答案:
grep -r "hello world" ./dir
或者 grep -rHn "hello world" ./
3.下面的两段程序中,循环能否执行?为什么?
A:
unsigned short i;
unsigned short index = 0;
for(i = 0; i
printf(“b\n”);
}
能,index 是 unsigned long 型,当执行到语句 i unsigned int -> long -> unsigned long -> long long -> unsigned long long
-> float -> double -> long double
注意,上面的顺序并不一定适用于你的机器,比如当 int 和 long 具有相同字长时,
unsigned int 的精度就会比 long 的精度高(事实上大多数针对 32 机的编译器都是如此)。
另外需要注意的一点是并没有将 char 和 short 型写入上式,原因是他们可以被提升到 int
也可能被提升到 unsigned int。
提升数据的精度通常是一个平滑无损害的过程,但是降低数据的精度可能导致真正的问
题。原因很简单:一个较低精度的类型可能不够大,不能存放一个具有更高精度的完整的
数据。一个 1 字节的 char 变量可以存放整数 101 但不能存放整数 12345。当把浮点类型数
据转换为整数类型时,他们被趋零截尾或舍入。
强制类型转换:
通常我们应该避免自动类型转换,当我们需要手动指定一个准确的数据类型时,我们可以
用强制类型转换机制来达到我们的目的,使用方法很简单,在需要强制转换类型的变量或
常量前面加上(type),例如(double)i; 即把变量 i 强制转换成 double 型。
4.一个计划跑 LINUX 系统的 ARM 系统把 bootloader 烧录进去后,上电后串口上没有任何输
出,硬件和软件各应该去检查什么?
提示: 1.跑 LINUX 的系统一般都需要外扩 DRAM,一般的系统也经常有 NOR 或 NAND
FLASH
2.bootloader 一般是由汇编和 C 编写的裸奔程序[5 分]
参考答案:
单片机系统:
硬件上:
1.确认电源电压是否正常。用电压表测量接地引脚跟电源引脚之间的电压,看是否是电源
电压,例如常用的 5V。
2.检查复位引脚电压是否正常。分别测量按下复位按钮和放开复位按钮的电压值,看是否
正确。
3.检查晶振是否起振了,一般用示波器来看晶振引脚的波形,另一个办法是测量复位状态
下的 IO 口电平,按住复位键不放,然后测量 IO 口(没接外部上拉的 IO 口除外)的电压,
看是否是高电平,如果不是高电平,则多半是因为晶振没有起振。
4.检查基本的外扩设备(这里主要是 DRAM,特别是 DDR/DDR2/DDR3)的 pcb layout 的走线
是否符合要求
软件上:
如果软件代码中:
1.检查 CPU 和 DRAM 是否正确初始化(CPU 的初始化包括一些典型步骤如: 关闭看门
狗,关键 FIQ,IRQ 中断,关闭 MMU 和 CACHE,调整 CPU 的频率)
2.检查堆栈指针是否正确设置了
2. 若如 NAND FLASH 做系统启动部分,则需注意一般需要的从 NAND FLASH 中拷贝代
码到 DRAM 中的步骤是否能正常完成
5.列举最少 3 种你所知道的嵌入式的体系结构,并请说明什么是 ARM 体系结构。[7 分]
参考答案:
嵌入式的体系结构包括 ARM,MIPS,POWERPC,X86,AVR32,SH 等
这个没有非常标准的答案,但由经常面试的时候会问到,关于什么是 ARM 体系结构主
要请参考讲义的 ARM 相关章节去总结,下面是我的总结,仅供参考:
什么是 ARM 体系结构?
答: 首先,ARM 体系结构是 ARM 公司设计,并授权其合作伙伴生产的占嵌入式市场
份额最大的一种 RISC(精简指令集)的 CPU,它具有高性能、低功耗、低成本的特点。
ARM 体系结构从工作模式、工作状态,指令集几个方面简述以下 ARM:
ARM 体系支持 7 种工作模式,包括系统(Sys)、未定义指令(und)、数据存取
异常(abt)、 管理(SVC)、中断(IRQ)、快速中断(FIQ)、用户模式(usr).其中,除了用户
模式以外的其它模式,我们称之为特权模式.它们之间的区别在于有些操作只能在特权模式
下才被允许,如直接改变模式和中断使能等. 除了用户模式和系统模式以外的其它 5 种模
式,我们又称之为异常模式。当特定的异常出现的时候,程序就会进入到相应的异常模式
中。
备注: 在 LINUX 系统中, Linux 的应用程序工作在 usr 模式,而内核在正常情况
下工作在 svc 模式,当中断或异常时工作在异常模式
ARM 体系结构中 CPU 有 2 种工作状态,thumb(指令为 16 位)和 ARM 状态(指令
为 32 位),相对寄存器不多,总共 37 个,它包括通用寄存器 r0~r12(FIQ 有自己的
r8
~ r12),栈指针寄存器 SP(r13),链接寄存器 lr(r14),PC 指针寄存器 PC(r15),程序
状态寄存器 CPSR 和保存程序状态寄存器 SPSR,在上面提到几种异常中,用户(usr)和系统
模式(sys)使用相同寄存器, 而其他异常模式有自己独立的 SP,LR,SPSR 寄存器。
当异常产生时, 硬件上(ARM core)会完成以下动作:
拷贝 CPSR 到 SPSR_
设置适当的 CPSR 位:
改变处理器状态进入 ARM 态
改变处理器模式进入相应的异常模式
设置中断禁止位禁止相应中断 (如果需要)}保存返回地址到 LR_
设置 PC 为相应的异常向量
返回时, 软件的异常处理程序需要:
从 SPSR_恢复 CPSR
从 LR_恢复 PC
Note:这些操作只能在 ARM 态执行.
ARM 处理器是基于精简指令集计算机(RISC)原理设计的,发展过程中商用的指令集经
过了 v4,v5,v6,v7(cortex 系列)
arm7tdmi(v4), arm920/arm920t/arm926ejs,arm10,arm11,cortex-a8。
4 个系列,ARM 内核的通用处理器型号比较常见的有
为了提高指令执行效率,大部分的 ARM 指令为单周期指令,并从软件设计角度看,ARM
处理器的指令流水线采用 3 级流水线模型,并提供了 LDM/STM 类似的批量数据操作指令。
为了提高 CPU 访问外部设备数据效率,ARM 处理器除部分 ARM7 采用冯.洛伊曼结构外,
其他得都采用
哈佛架构,从而实现了对指令和数据存储器的同时访问。并且,ARM CPU 提供了现代操
作系统所需的虚拟内存管理机制(MMU)和指令、数据 cache,并提供了协议处理器(cp15)来
协助管理 CPU 的 MMU 和 CACHE。
扩展概念:以上叙述里面提及的概念也要稍微去总结一下,比如:
1.什么是 RISC?
2.ARM 中断在 ARM9,CORTEX-A8 是怎么处理的?LINUX 中为什么需要把中断分为上半
部分,下半部分
3.MMU 和 CACHE 的一些基本原理和知识
6.请简述下面这段代码的功能
mov r12, #0x0
ldr r13, =0x30100000
mov r14, #4096
loop:
ldmia
stmia
cmp
blo
r12!, {r0-r11}
r13!, {r0-r11}
r12, r14
loop
[2 分]
参考答案:
借助 r0~r11,将内存地址 0x0 开始的 4KB 数据拷贝到 0x30100000
7.嵌入式中常用的文件系统有哪些?说出它们的主要特点和应用场合?[5 分]
参考答案:
嵌入式相关的文件系统:
读文件系统启动速度快于可读写的文件系统
嵌入式文件系统包括只读和可读写文件系统,一般情况下,只
嵌入式相关的文件系统包括以下几种:
只读文件系统
cramfs: 压缩的只读文件系统
特点:
启动快,文件最大支持 256MB,单个文件最大 16MB
squashfs: 只读文件系统
特点: 压缩比最大,启动比 cramfs 慢
案例:路由器,ubuntu 的发行光盘
可结合 LZMA 压
缩算法
可读写的文件系统:
JFFS2: 支持 NOR 和 NAND FLASH (对 NAND 的支持天生不足)
特点:
更慢)
会急剧下降
1.可读写
2. 挂载慢(特别是在小文件很多的文件系统中,就
3. 当数据占到 JFFS2 分区的 75~80%左右时,性能
YAFFS2: 只支持 NAND FLASH
特点: 1.可读写
中,优势更明显)
加
ubifs:
2. 挂载快(特别是在小文件很多的文件系统
3.它不是标准内核中的,需通过补丁添
起码支持 NAND FLASH
特点: 1.可读写
2. 挂载快
3.它的实现和其他的文件系统不一样,引进
了一个"卷"的概念
在内存中的文件系统:
ramdisk: 描述的是功能,不是格式
启动快,防止用户修改
ramfs: 在内存中的文件系统
tmpfs:
临时文件系统
实时反映系统状态: procfs, sysfs
另外,一些支持 SD 卡,U 盘功能的系统还需要支持
windows 文件系统:
fat:
FAT32
另外,一些带硬盘的嵌入式系统(比如 DVR)还需要支持
硬盘的文件系统:
EXT3/EXT4
另外,很重要很重要的一点,需要去总结文件过程中遇到的问题,总结比如文件体系挂
不上的可能原因
(给个提示,可能有比如网卡或 FLASH 驱动没加载,内核启动参数传的不对,文件系统制
作的步骤不对等好像原因)
8.某外设寄存器 rGpioBase 的地址是 0x56000000,寄存器的 0~15 位有效,请写出给外设寄
存器高八位(8~`15 位)设置成 0xc3 的代码[7 分]
参考答案:
#define rGpioBase (*((volatile unsigned int *)0x56000000))
rGpioBase &= ~0xff00;
rGpioBase |= 0xc300;
9.根据时序图和说明编写程序:
GPIO 已经设置好,只需要调用函数 gpio_seet_level(int gpio, int level)即课使某个
GPIO 输出高电平或者低电平。图中用于产生时序的 gpio 已经分别定义为
SSP_XCS,SSP_SCLK,SSP_DIN,level 的定义分别为 GPIO_LO 和 GPIO_HI,需要编写函数的原
型为:void ssp_io_write_word(u32 command),该函数用来输出一个字(如上图中的 A0 到
C0 一组 9 位),这 9 个位是在参数 command 中的低 9 位.
[5 分]
参考答案:
这道题立意非常好,做为一个底层工程师,看时序是必须的,相关的代码写法:
void ssp_io_wirte_word(u32 command)
{
int i;
//片选
gpio_set_level(SSP_XCS, GPIO_LO);
//送 COMMAND
for (i=0; i++; i<9) { //依次送 A0,C7~C0
gpio_set_level(SSP_SCLK,GPIO_LO);
gpio_set_level(SSP_DIN, (command >>(8-i))&0x1);
gpio_set_level(SSP_SCLK,GPIO_HI);
}
//结束片选
gpio_set_level(SSP_SCLK,GPIO_LO);
gpio_set_level(SSP_XCS, GPIO_HI);
return;
如果实际结果并没有把数据正确的送出,那么就需用示波器或者逻辑分析仪看一下波形
是否正确,再根据计算得到的 CLK 周期看一下 CLK 的延时是否合适,否则就加一定延迟处
理
================================
另外,这道题还提醒我们,I2C 的时序是要能记得的,如果不记得,再去复习 I2C 协议
10.简述 LINUX 系统从上电开始到系统起来的主要流程?
提示: 1.可以 uboot、内核和文件系统的主要功能去总结
2.这个题主要是在笔试之后的面试,需要在 3~5 分钟之内表述清楚[8 分]
参考答案:
系统启动流程应该从 4 个方面去总结,bootloader,内核,文件系统挂载,应用程序运行
4 个方面去总结,先总结大功能,再总结小功能:下面的手绘稿中,先说第一层,再说分
开说第二层,在说第二层的时候,可以三星的 ARMCPU,以从 NAND FLASH 启动为例,并在
我们的图上加上硬件的相应部分:CPU 上电时,CPU 里面的 ROMCODE 负责把 booloader 的前
面部分代码搬移到 SRAM,并把 SRAM 映射成 0x0 地址,然后跳到 0x0 地址,另外,
bootloader 第二层里面,说完初始化 CPU(可补充一下 CPU 的初始化包括进入到管理模
式,关闭看门狗,中断,MMU 和 CACHE)和 DRAM 后,省略号(...)的位置是在补充一行文
字: 把 bootloader 完整代码拷贝到 DRAM 中
另外,很重要很重要的一点,需要去总结移植过程中遇到的典型问题和以及自己当时是
怎么思考这个问题,并找到解决方法的过程(至少应该总结 2~3 个问题),也到网上去以
比如(uboot, ARM 移植,问题)或(内核 移植 问题)和(文件 移植 问题)这样的关键
词去搜看看别人经常遇到什么问题,总结一下!!
11.如何编写一个 LINUX 驱动?
提示:主要说字符设备的编写过程 [7 分]
参考答案:
这个得对着自己相应模块的驱动的找出其初始化部分并总结,下面是我总结的,仅仅供参
考,不要照搬这些东西:切忌照搬,得自己去总结一下主要流程,
以字符设备为例,现在平台设备的驱动一般包括(注意,以下部分要结合一个具体的驱动
去说):
一.在系统的资源文件代码中定义 platform_device,里面填写对应设备的外设 IO 起始地
址,地址长度,中断,DMA 资源等信息资源信息,并把资源信息添加到系统启动初始化流
程里面,比如:
/* LCD Controller */
static struct resource s3c_lcd_resource[] =
{
[0] = {
.start = S3C24XX_PA_LCD,
.end
.flags = IORESOURCE_MEM,
= S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
},
[1] = {
.start = IRQ_LCD,
.end
= IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
= "s3c2410-lcd",
.name
.id
= -1,
.num_resources
= ARRAY_SIZE(s3c_lcd_resource),
.resource
.dev
= s3c_lcd_resource,
= {
.dma_mask
= &s3c_device_lcd_dmamask,
.coherent_dma_mask
= 0xffffffffUL
}};
EXPORT_SYMBOL(s3c_device_lcd);
二. 通过 module_init(xxx_init)和 moule_exit(xxx_init)定义驱动入口和出口函数;
三.写出模块加载 xxx_init()和退出的实际处理函数 xxx_exit(),这里以 xxx_init()为
例:
在里面调用 platform_driver_resigter()注册一个 platform_driver 结构体,实现其
中的 probe()和 remove()函数以及 driver 成员结构体中 name 和 owner 成员,比如:
static struct platform_driver s3c2410fb_driver = {
.probe
.remove
= s3c2410fb_remove,
= s3c2410fb_probe,
.driver
= {
.name
.owner
= "s3c2410-lcd",
= THIS_MODULE,
},
};
五、在 xxx_probe()函数里面主要做一下事情:
1.获取平台设备资源的外设 IO 地址,中断,DMA 资源等信息
2.映射外设控制寄存器的外设 IO 地址到内核的虚拟地址空间
3.使能外设时钟,注册外设中断的处理函数(如果有中断)
4.扫描和初始化硬件
5.最后向 LINUX 内核注册相应设备并通知应用层的 udev/mdev 守护进程创建相应的设备
节点,或者通过子系统(比如输入子系统,I2C 子系统等)注册相应设备并创建设备节点
6.然后,根据字符设备相应的数据结构 file_operations 的实现里面的比如
open,release,read,write,mmap 等关键函数,或者通过子系统去注册的话,按子系统要求
去实现相应的代码就行了
12.简述 LINUX 驱动中字符设备和块设备的区别?[5 分]
参考答案:
字符设备的特点是数据以字符流的方式进行访问,数据的顺序不能错序,乱序和随机读
写,字符设备内核中不需要读写的缓冲,其驱动不支持 lseek()函数
块设备的特点是数据是固定块大小(典型值有 512 字节,2KB,4KB)进行读写,块设备可以
随机读写,读写的时候内核中需要缓冲,驱动支持 lseek()函数,块设备中数据的访问需
要先 mount 到 LINUX 的目录文件后才能访问里面的数据
LINUX 中字符设备架构相对简单,应用编程的系统调用 open,close,read,write 和
ioctl 等函数驱动里面有相应的 file_operations 结构体里面的函数与之对应
LINUX 中块设备架构相对复杂,应用程序的读写会通过块设备里面的文件系统转化
为读写的 IO 请求,块设备驱动里面通过 gendisk 结构体抽象块设备,并通过对请求队列的