嵌入式技术面试

嵌入式技术面试

  • 1 C/C++
    • 1.1 结构体struct与union区别?struct与class区别?
    • 1.2 结构体使用需要注意什么?字节对齐?
    • 1.3 带参数宏与一个函数差别?
    • 1.4 有一个函数,这个函数是在中断和主程序都有调用,请问这个函数有什么要求?
    • 1.5 函数可重入和不可重入的区别?解释什么是可重入函数?
    • 1.6 volatile变量考察?
    • 1.7 volatile和const可以同时使用么?这两个组合在什么情况下使用?
    • 1.8 描述内存分配方式及它们的区别?
    • 1.9 用两个栈实现一个队列功能?给出算法和思路?
    • 1.10 解释下位域,为什么要用位域,位域的好处?
    • 1.11 const?
    • 1.12 const与define区别?typedef与define区别?
    • 1.13 简述数组与指针区别?
  • 2 数据结构
    • 2.1 如何判断一个环形链表?
    • 2.2 数组与链表差别?
    • 2.3 利用链表实现队列?
    • 2.4 二叉树的遍历?
    • 2.5 ((a+b)*c + (d+e))*(g+h)用二叉树表示?
  • 3 嵌入式系统
    • 3.1 uboot经过哪些过程?
    • 3.2 为什么uboot要关掉cache?
    • 3.3 uboot怎么传参给内核的?
    • 3.4 linux怎么进行内存管理?
    • 3.5 说说进程和线程的区别?
    • 3.6 进程有几种状态?进程间通信都有些什么?
    • 3.7 两个线程处理一个数据结构怎处理?
    • 3.8 在应用层调用一个read,是怎么传到内核中去的?
    • 3.9 对内核各种锁的了解?
    • 3.10 简述Linux输入子系统?
    • 3.11 简述分层分离机制?
    • 3.12 同步互斥阻塞?
    • 3.13 驱动中操作物理绝对地址为什么要先ioremap?
    • 3.14 中断的上半部分和下半部分的问题:请说明分成上半部分和下半部分的原因,为何要分?该如何实现?
    • 3.15 自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么?
    • 3.16 原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行?
    • 3.17 insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
    • 3.18 设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义?注册一个字符设备驱动有两种方法:
    • 3.19 Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备?
    • 3.20 linux中引入模块机制有什么好处?
    • 3.21 copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?
    • 3.22 请简述中断与DMA的区别。Linux设备驱动程序中,使用哪个函数注册和注销中断处理程序?
    • 3.23 写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?
    • 3.24 内核函数mmap的实现原理,机制?
    • 3.25 linux调度原理?
    • 3.26 驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?
    • 3.27 Linux内存泄漏检测工具
    • 3.28 设备树
  • 4 外设接口
    • 4.1 串口
      • 4.1.1 如何判断两个串口未知的波特率?
      • 4.1.2 串口如何发送浮点类型数据?
      • 4.1.3 UART如何保证数据传输的正确性?
      • 4.1.4 保证数据无串扰,参数设置正确。但是串口A传输float类型给传输B,但是之间的数据总是错误,请问可能是什么原因?
      • 4.1.5 A传输一个buffer给B串口,请问B接收后如何解析出来?
      • 4.1.6 串口A发送给串口B,A、B串口端的大小端不同, 会不会造成数据出错,怎么处理?
    • 4.2 IIC
    • 4.3 SPI
      • 4.3.1 uart、iic、spi比较?
    • 4.4 LCD
    • 4.5 Nor/Nand
      • 4.5.1 Nand Flash和Nor Flash的区别?
  • 6 项目
    • 6.1 介绍一个最满意软件的项目,用到了那些技术,实现了哪些功能?

1 C/C++

1.1 结构体struct与union区别?struct与class区别?

struct与union:
   最大的区别在于内存利用。 struct各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。union 各成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。因而,联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍。不允许对联合体变量名U2直接赋值或其他操作。
struct与class:
   C++中struct与class关键字一般可以通用,只有一个很小的差别。struct成员默认属性是public,而class成员为private。

1.2 结构体使用需要注意什么?字节对齐?

struct 结构名 
{ 
        类型  变量名; 
        类型  变量名; 
        ... 
} 结构变量;

   在使用结构变量时要先对其定义; 结构名是结构的标识符不是变量名;结构是按变量名字来访问成员的;结构体指针采用“->”访问成员,结构体变量采用“.”访问。
   需要字节对齐的根本原因在于CPU访问数据的效率问题。假设整型变量不是内存对齐,比如地址在0x00000002,则取值需要2次操作,先取0x00000002-0x00000003的short数据,再取0x00000003-0x00000004的short数据,再合并起来组成整型数据。

1.3 带参数宏与一个函数差别?

带参宏 函数
处理时间 编译时 程序运行时
参数类型 没有参数类型问题 定义实参、形参类型
处理过程 不分配内存 分配内存
程序长度 变长 不变
运行速度 不占运行时间 调用和返回占用时间

1.4 有一个函数,这个函数是在中断和主程序都有调用,请问这个函数有什么要求?

引发的问题:
①此函数不是一个可重入函数,而当此函数已经在执行时它可能被另一个ISR所调用。这样就会导致结果是可变的而且很可能会导致一些参数的错误。
②另一个问题就是本地变量和参数所使用的内存可能被其他函数的内存覆盖。如果函数是由中断所调用的,则此函数的内存就会被使用。这会引起其它函数的内存错误。
解决办法:
①如果你100%确认这个函数的两个副本都不会同时执行(如果此函数是被main调用并且中断是未被使能的)并且此函数没有使用内存(只使用的寄存器),那么你就可以忽略此警告。
②如果此函数使用了内存,你就要使用OVERLAY directive来将此函数从覆盖分析(overlay anaysis)中移除。
③无论何时当从main中调用此函数时,需要关闭中断。你可能需要对被调用的函数使用#pragma disable。你也必须使用OVERLAY directive将此函数从overlay analysis中移除。
④为此函数创建两个副本。
⑤使此函数可重入。

1.5 函数可重入和不可重入的区别?解释什么是可重入函数?

https://blog.csdn.net/u011123091/article/details/81748686

1.6 volatile变量考察?

   优化器在用到使用该关键字定义的变量时需要小心地去内存中取值,而不是取寄存器的备份。
例子:
   ① 并行设备的硬件寄存器
   ② 一个中断服务子程序会访问到的非自动变量
   ③ 多线程应用中被几个任务共享的变量

1.7 volatile和const可以同时使用么?这两个组合在什么情况下使用?

   可以同时使用,例如只读的状态寄存器。

1.8 描述内存分配方式及它们的区别?

   (1)静态存储区分配。内存在程序编译时已分配好,在整个运行器件都存在。
   (2)栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动释放。
   (3)堆上分配。动态内存分配生存期由程序员决定。

1.9 用两个栈实现一个队列功能?给出算法和思路?

设两个栈A、B,一开始为空。
入队:
   将新元素push入栈A
出队:
   (1)判断栈B是否为空
   (2)若不为空,则将栈A中所有元素一次pop出并push到栈B
   (3)将栈B的栈顶元素pop出

1.10 解释下位域,为什么要用位域,位域的好处?

   在计算机中,有些信息存储时并不需要占用一个完整的字节,而只需占用一个或几个二进制位。比如在存放一个只有0和1两种状态的开关量时,用一位二进制位即可表示。因此,为了节省存储空间,C语言提供了一种称为“位域”的数据结构来充分利用存储空间。

1.11 const?

① 定义只读常量
② 修饰函数参数表示在函数体内不希望改变参数的值
③ 修饰函数返回值表示返回值不可变,多用于返回指针
const int p; //p是一个常整数型
int const p; //p是一个常整数型
<左数右指>
const int* p; //p可变,p指向的内容不可变
int* const p; //p不可变,p指向的内容可变
int const* p; //p可变,p指向的内容不可变
const int* const p; // p和p指向的内容都不可变

1.12 const与define区别?typedef与define区别?

  • const有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
  • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

1.13 简述数组与指针区别?

   数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

2 数据结构

2.1 如何判断一个环形链表?

   创建两个指针指向链表头结点,然后让其中的一个指针每次移动一个结点,另一个每次移动两个结点,判断两个指针指向的结点是否相同

2.2 数组与链表差别?

https://blog.csdn.net/m0_37631322/article/details/81777855

2.3 利用链表实现队列?

https://www.cnblogs.com/electronic/p/10896510.html

2.4 二叉树的遍历?

https://blog.csdn.net/qq_33243189/article/details/80222629

2.5 ((a+b)c + (d+e))(g+h)用二叉树表示?

3 嵌入式系统

3.1 uboot经过哪些过程?

   系统启动RomBoot --> SPL --> u-boot --> Linux kernel --> file system --> start application
   SPL是单板上电后,硬件主动读取Nandflash等存储器读取前4k/8k数据到内存的0地址上,例如S3C2440在上电后就会读取前4k到内存上
第一阶段:
   ① 设置CPU工作模式svc管理者模式
   ② 关闭MMU和cache
   ③ 关看门狗
   ④ 屏蔽中断(防止程序跑飞)
   ⑤ 初始化时钟
   ⑥ 初始化SDRAM
   ⑦ 设置栈
   ⑧ 代码重定位
   ⑨ 清bss段
   ⑩ 调用start_armboot
第二阶段:
   主要是环境参数设置

3.2 为什么uboot要关掉cache?

   cache主要用来加快cpu读取内存中指令的速度,但在cpu初始化时,内存起始并未准备就绪,这样直接进行数据读取,势必导致指令取址异常。

3.3 uboot怎么传参给内核的?

简单的讲,uboot利用函数指针及传参规范,它将
R0: 0x0
R1: 机器号
R2: 参数地址
三个参数传递给内核。
其中,R2寄存器传递的是一个指针,这个指针指向一个TAG区域。

3.4 linux怎么进行内存管理?

  • 对内存的分配和管理,也就是平时应用层malloc和内核层vmalloc、kmalloc之类的内存申请的管理
  • 虚拟内存和物理内存之间的转换

3.5 说说进程和线程的区别?

参考:https://blog.csdn.net/MoLiYw/article/details/101104184

3.6 进程有几种状态?进程间通信都有些什么?

参考:https://blog.csdn.net/MoLiYw/article/details/101104184

3.7 两个线程处理一个数据结构怎处理?

3.8 在应用层调用一个read,是怎么传到内核中去的?

   应用程序通过系统调用进入内核,具体执行了swi val,引发一个异常,进入内核的异常处理函数中,根据传入的值调用sys_read函数,再根据打开文件的属性,去找到相应的驱动程序,从而调用驱动程序的read函数来实现功能。

3.9 对内核各种锁的了解?

  • 原子操作(atomic):
  • 自旋锁(spinlock/spinlock_irqsave):
  • 读写自旋锁(rwlock):
  • 顺序自旋锁(seqlock):
  • RCU(读-拷贝-更新):
  • 信号量(semaphore):

3.10 简述Linux输入子系统?

参考:https://www.cnblogs.com/lifexy/p/7542989.html

3.11 简述分层分离机制?

参考:https://www.cnblogs.com/lifexy/p/7569371.html

3.12 同步互斥阻塞?

参考:https://www.cnblogs.com/MrYuan/p/5245191.html

3.13 驱动中操作物理绝对地址为什么要先ioremap?

   因为在内核中操作的都是虚拟地址,内核访问不到物理地址,只能通过ioremap映射为虚拟地址,内核才能访问此内存空间。

3.14 中断的上半部分和下半部分的问题:请说明分成上半部分和下半部分的原因,为何要分?该如何实现?

   中断分成上半部分和下班部分的原因主要是因为内核要保证内核中进程的正常调度和运行,中断程序所以需要短小精悍,但是有些驱动在中断处理程序需要完成大量的工作,所以就很耗时。为了解决这个问题就Linux 将中断处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。顶半部完成尽可能少的比较紧急的功能,底半部就由tasklet或者工作队列的方法实现 。

3.15 自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还是信号量?还是两者都能用?为什么?

   使用自旋锁的进程不能睡眠,使用信号量的进程可以睡眠。
   中断服务例程中的互斥使用的是自旋锁,原因是在中断处理例程中,硬中断是关闭的;但是要注意这样会丢失可能到来的中断。

3.16 原子操作你怎么理解?为了实现一个互斥,自己定义一个变量作为标记来作为一个资源只有一个使用者行不行?

   原子操作指的是无法被打断的操作

3.17 insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?

   insmod调用init函数,rmmod调用exit函数。设计时需要严格记住相关内存的操作后必须在exit中进行释放,避免内存泄漏。如存储,ioremap,定时器,工作队列等等。
   卸载失败原因是因为有进程正在使用该模块,这跟我们在windows下拔出正在使用的U盘类似。

3.18 设备驱动程序中如何注册一个字符设备?分别解释一下它的几个参数的含义?注册一个字符设备驱动有两种方法:

(1)void cdev_init(struct cdev *cdev, struct file_operations *fops)
   该注册函数可以将cdev结构嵌入到自己的设备特定的结构中。cdev是一个指向结构体cdev的指针,而fops是指向一个类似于file_operations结构(可以是file_operations结构,但不限于该结构)的指针.
(2) int register_chrdev(unsigned int major, const char *name , struct file_operations *fopen);
   该注册函数是早期的注册函数,major是设备的主设备号,name是驱动程序的名称,而fops是默认的file_operations结构(这是只限于file_operations结构)。对于register_chrdev的调用将为给定的主设备号注册0-255作为次设备号,
并为每个设备建立一个对应的默认cdev结构。

3.19 Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备?

字符设备:
   字符设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少实现open,close,read和write系统调用。字符终端、串口、鼠标、键盘、摄像头、声卡和显卡等就是典型的字符设备。
块设备:
   和字符设备类似,块设备也是通过/dev目录下的文件系统节点来访问。块设备上能够容纳文件系统,如:u盘,SD卡,磁盘等。
   字符设备和块设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。

3.20 linux中引入模块机制有什么好处?

(1) 应用程序在退出时,可以不管资源的释放或者其他的清除工作,但是模块的退出函数却必须仔细此撤销初始化函数所作的一切。
(2)该机制有助于缩短模块的开发周期。即:注册和卸载都很灵活方便。

3.21 copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?

   由于内核空间和用户空间是不能互相访问的,如果需要访问就必须借助内核函数进行数据读写。
copy_to_user():完成内核空间到用户空间的复制
copy_from_user():是完成用户空间到内核空间的复制
   一般用于file_operations结构里的read,write,ioctl等内存数据交换作用的函数。当然,如果ioctl没有用到内存数据复制,那么就不会用到这两个函数。

3.22 请简述中断与DMA的区别。Linux设备驱动程序中,使用哪个函数注册和注销中断处理程序?

DMA:
   是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制,使用DMA可以使系统CPU从实际的I/O数据传输过程中摆脱出来,从而大大提高系统的吞吐率.
中断:
   是指CPU在执行程序的过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回源程序被中断的位置并继续执行。
   所以中断和DMA的区别就是DMA不需CPU参与而中断是需要CPU参与的。
注册中断:
int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id);
参数意义依次是:中断号,中断处理函数,中断管理有关的掩码,中断请求设备名,中断信号线。
过程是:dev_name设备请求中断->cpu分配中断号->设置中断管理的掩码->分配中断信号线->处理中断函数->完成之后再根据设置情况返回原处理程序处继续处理程序。
注销中断:
Void free_irq(unsigned int irq, void *dev_id);
释放中断和中断信号线

3.23 写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的?

  • 第一:中断处理例程应该尽量短,把能放在后半段(tasklet,等待队列等)的任务尽量放在后半段。写一个中断服务程序要注意快进快出,在中断服务程序里面尽量快速采集信息,包括硬件信息,然后退出中断,要做其它事情可以使用工作队列或者tasklet方式。也就是中断上半部和下半部。
  • 第二:中断服务程序中不能有阻塞操作。应为中断期间是完全占用CPU的(即不存在内核调度),中断被阻塞住,其他进程将无法操作;
  • 第三:中断服务程序注意返回值,要用操作系统定义的宏做为返回值,而不是自己定义的OK,FAIL之类的。

3.24 内核函数mmap的实现原理,机制?

   mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。
   另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。

3.25 linux调度原理?

参考:https://blog.csdn.net/janneoevans/article/details/8125106

3.26 驱动里面为什么要有并发、互斥的控制?如何实现?讲个例子?

   并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共 享资源(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions)。
   解决竞态问题的途径是保证对共享资源的互斥访问,所谓互斥访问就是指一个执行单元 在访问共享资源的时候,其他的执行单元都被禁止访问。
   访问共享资源的代码区域被称为临界区,临界区需要以某种互斥机 制加以保护,中断屏蔽,原子操作,自旋锁,和信号量都是linux设备驱动中可采用的互斥途径。

3.27 Linux内存泄漏检测工具

https://www.cnblogs.com/guochaoxxl/p/6970090.html

3.28 设备树

https://www.cnblogs.com/haofei2haojie/p/8466881.html

4 外设接口

4.1 串口

4.1.1 如何判断两个串口未知的波特率?

示波器;抓包

4.1.2 串口如何发送浮点类型数据?

拆分发送,接收端组合;采用共用体;

4.1.3 UART如何保证数据传输的正确性?

(1)在数据位的两端添加了起始位,奇偶校验位,停止位等用于进行数据的同步和纠错
(2)接收端用16倍波特率对数据进行采样,取采样中间点的值,这样可以很大程度上确保采样数据的正确性
(3)通过流控制信号(如上面的uart_rts,uart_cts信号)确保数据传输的正确性,不会发生underrun和overrun

4.1.4 保证数据无串扰,参数设置正确。但是串口A传输float类型给传输B,但是之间的数据总是错误,请问可能是什么原因?

考虑大小端问题

4.1.5 A传输一个buffer给B串口,请问B接收后如何解析出来?

4.1.6 串口A发送给串口B,A、B串口端的大小端不同, 会不会造成数据出错,怎么处理?

串口两端利用union判断大小端,不一致需要逐位调换顺序,组合后数据即正确。

4.2 IIC

常问时序图、同步与仲裁
参考:https://blog.csdn.net/MoLiYw/article/details/101103224

4.3 SPI

常问时序图、与UART及IIC区别
参考:https://blog.csdn.net/MoLiYw/article/details/101106543

4.3.1 uart、iic、spi比较?

参考:https://blog.csdn.net/sinat_20265495/article/details/72417137

4.4 LCD

参考:https://blog.csdn.net/MoLiYw/article/details/101108969

4.5 Nor/Nand

4.5.1 Nand Flash和Nor Flash的区别?

6 项目

6.1 介绍一个最满意软件的项目,用到了那些技术,实现了哪些功能?

题目来源:
由于时间比较赶,部分答案直接摘取了相关文章的内容,可以通过链接直接进去看,对此向这些博主表示感谢!
[1] https://www.nowcoder.com/discuss/16963
[2] http://news.eeworld.com.cn/mcu/2018/ic-news121942596.html
[3] https://blog.csdn.net/weixin_42832780/article/details/93626215
[4] https://blog.csdn.net/qq_31505483/article/details/75012661
[5] https://blog.csdn.net/zqixiao_09/article/details/50937907
[6] https://blog.csdn.net/kai_zone/article/details/82021233

你可能感兴趣的:(嵌入式笔试面试)