logo资料库

Linux驱动面试题.doc

第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
资料共10页,剩余部分请下载后查看
就业模拟测试题-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 结构体抽象块设备,并通过对请求队列的
分享到:
收藏