秋招面试问答

目录

请你自我价绍一下

【开场白+项目介绍】

我叫XXX,就读于XXXX大学电子信息控制工程方向,主攻网络化运动控制方向,应聘嵌入式软件开发的岗位。研一结课后就在导师的公司做嵌入式软件开发,主要接手的项目有XXX,XXX,XXX等项目,目前在开发高速裁床控制系统。

介绍一下你的主要项目

我目前在开发裁床的一个项目,主要工作就是解析PC端通过网口下发的PLT文件,控制刀具在床面上1 : 1的切出目标图形,解析HMI触控屏通过232或422发送过来的命令,控制电机的启停,存储用户输入的各种参数。

你在项目中遇到哪些问题?是怎么解决的?

之前遇到一个客户,他们在机器不用工作的时候,也需要网络一直连接,但是我们的通信在三五个小时后就会断开,只能重启系统,这样的话,客户设置的一些参数就会消失,这样肯定是不可以的。

所以我们找程序的问题,找心跳包是不是发送失败,没有重发,有2开启TCP的keepalive保活机制,但是效果都不咋样,最后我们直接读取Lan8720这个网口的状态寄存器,当网线插入,该寄存器的bit2就置1,否则为0。所以我就检测这个寄存器,当该位为0的话,我就让控制器再次重连服务器。

你平时怎么学习专业知识的?以什么样的方式?

我学习一般分为两种:一种就是在解决问题的过程中学习,比如串口发送数据为什么要关中断(不能被接受中 断干扰,导致数据发送错误),遇到这些问题,我现在一般都在博客上记录下来,上网查找资料,可能在解决这个问题的时候,会遇到一连串的问题,我会继续查找资料,把相关的知识点都记录下来,当问题足够多的时候,我再把他们分门别类的整理。

第二种就是系统的学习了,如果有比较充足的时间,我会拿着一本书,一点点的去读,去理解这些内容。这就是我日常的一个学习方式。

重写一下strcpy()函数

char *strcpy(char* strDest, char* strSrc)

{

assert((strDest != NULL) && (strSrc != NULL));

if(strDest == strSrc) return strDest;

char *address = strDest;

while((*strDest++ = *strSrc++) != '\0') ;

return address;

}

寄存器的第三位的值从0变成1(1),第二位的值从1变成0(0)

GPIO |= (1<<3);         原理是1左移三位,再或上GPIO,并不会改变其他位的值
GPIO &= ~(1<<2); 原理是1左移2位,在进行取反,最后再与上GPIO,并不会改变其他位的值

什么是用户空间和内核空间?

内核空间是Linux内核的运行空间,用户空间是用户程序的运行空间。为了安全,他们是隔离的,即使用户程序崩溃了,内核也不受影响。

当进程运行在内核空间时,就处于内核态;当进程运行在用户空间时,就处于用户态。

用户栈与内核栈是同一块区域么?有什么区别?

用户栈与内核栈是两块独立的区域。

用户栈指向用户内存空间,用于保存线程调用的参数、返回值和局部变量

内核栈指向内核地址空间,保留中断现场,特别是嵌套中断(先进后出),保存操作系统相互调用的参数、返回值和局部变量

内核空间与用户空间的通讯方式?

1、API

read, write, ioctl

get_user(x, ptr), put_user(x, ptr)

copy_from_user, copy_to_user

2、虚拟文件系统

/proc

3、netlink

4、文件

5、mmap系统调用

6、信号

为什么要用uboot?不用可以么?

1、uboot主要用来引导内核启动的和一些板子上硬件的驱动

2、理论上可以的,实际没人这样用。把uboot的代码放进内核中,板子也可以启动,但是当客户该需求,需要固件升级,内核就要下载新的固件覆盖自己,结果中途写错了一个字节,再次开机,已经变成砖头了。

如何保证多线程读写同一个变量(原子性)

1、加锁(标志位):操作变量之前标志位,置1;操作完标志位,置0

2、C++11的原子类型atomic(atomic g_num = 0),对于共享变量能用原子类型的就不要用加锁机制

volatile关键字

功能:volatile定义的变量会在程序外被改变,防止对寄存器进行优化,每次都必须从内存中读取,而不能重复使用放在cache和寄存器中的备份

使用场景:

1、中断服务器程序中修改的供其他程序检测的变量需要加volatile

2、多任务环境下各任务间共享的标志位应该加volatile

3、存储器映射的硬件寄存器通常也要加volatile

static关键字

改变函数和变量的作用域

1、修饰局部变量

这个变量的作用域只是本函数,如果多次调用函数的话,这个变量只会被初始化一次。

2、修饰全局变量

这个变量的作用域只在本文件内

3、修饰函数

函数的作用域只在本文件内

ARM有几个寄存器,什么是CPSRSPSR

37个寄存器

CPSR

程序状态寄存器,用于保存当前程序的一些状态,比如上下文的一些寄存器内容。

SPSR

备份的程序状态寄存器,主要是中断发生时,用来存储CPSR的值。

Linux设备驱动分类

1、字符设备驱动(/dev):鼠标、键盘、显示器

字符设备是以不定长度的字符流的方式,顺序访问的

2、块设备驱动(/dev):硬盘、U盘

块设备是以固定大小长度,随机访问的

3、网络设备驱动:蓝牙、WiFi、网卡

网络设备是一个net_device结构,并通过register_netdev注册到系统里,可以通过ifconfig -a命令查看

专业课中,那个课学的比较好?

C语言、操作系统、计算机组成原理

除了课堂以外,某个领域有没有深耕?私下有没有花时间去学习?

操作系统、计算机组成原理,C/C++

C/C++哪个更熟悉?做过开发么?

C语言更熟悉,做过开发

Linux操作系统熟悉么?

熟悉,日常使用没问题

进程与线程的区别

1、进程是系统进行资源分配和调度的一个基本单位(各种表格、内存空间、磁盘空间、IO设备),线程是CPU调度和分配的基本单位(独有的程序计数器、一组寄存器和栈)。

2、进程有自己的独立地址空间,线程共享进程的内存空间

3、进程切换的开销大,线程切换开销小

4、多进程程序中,一个进程死掉了,并不会对另外一个进程造成影响;而多线程程序只要一个线程死掉了,整个进程也跟着死掉了

uboot如何引导内核启动的?

1、加载内核到DDR,去DDR中启动内核镜像

2、校验内核格式

3、内核传参:三个参数(R0、R1、R2)

        R0:默认0

        R1:CPU ID,标识计算机系统的型号

        R2:启动参数数据结构的首地址

4、跳转执行内核

对树熟悉么,说一下那些树,有什么特点

满二叉树:所有分支都存在左子树与右子树,并且所有叶子结点都在同一层。

完全二叉树:最下层的叶子结点集中在树的左部。

【注】满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树

二叉搜索树:左子树上的所有节点的值都小于根节点的值,右子树上所有节点的值都大于根节点的值;二叉搜索树的中序遍历的结果是升序的

你期望的薪资是多少?

公司应该都有一个统一的标准吧,按照标准来就好了,16K差不多吧

你是北方人,对工作地点有要么?

没有,我主要看重我在公司干什么

如何判断一个数是不是质数?

自旋锁和信号量说一下

自旋锁:当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是和否能够被获取,直到获取到锁才退出循环。

信号量:锁的另一种实现机制,当得到信号量时,进程或线程才能进入临界区。信号量与自旋锁最大的不同点在于,当一个进程试图去获取一个已经被锁定的信号量时,该进程不会像自旋锁一样自旋忙等,而是会将自身加入一个等待队列中去睡眠,直到其他进程释放信号量后,处于等待队列的进程才会被唤醒。当进程唤醒后,就立刻重新从睡眠的地方开始执行,又一次试图获得信号量,当获得信号量后,程序继续执行。

二叉树什么时候会退化?什么是平衡二叉树?

比如在二叉搜索树里面,如果一直从小到大插入元素,就退化成一个链表了,为了防止退化,才出现平衡二叉树

平衡二叉树(AVL树):左子树与右子树的高度之差的绝对值小于等于1

cache一致性

主要是指某些操作(DMA)导致Cache和内存(DDR)之间的数据不一致,且我们无法正确读取到新的数据。

初始化的时候一定要清空

uboot启动为什么要关闭中断?MMUDCACHE之类的

不用必须关吧,uboot就是引导内核启动的,而且uboot启动只是把需要的打开了,其他不需要的都可以关闭。当然也可以打开。DCHCHE之类的必须关闭,因为SDRAM没有初始化,可能会取到错误的数据。

bin文件和elf文件的区别

elf文件是gcc编译出来的,通常gcc test.c -o test,生成的test文件就是elf格式的文件,在linuxshell下输入./test就可以执行。(ELF文件包含符号表、汇编等)

bin文件是经过压缩的可执行文件,去掉elf格式的东西,是直接的内存映像的表示。在系统没有加载操作系统的时候可以执行。(bin文件是将elf文件中的代码段、数据段,还有一些自定义的段抽取出来做成一个内存的镜像)

什么时候用哈希?哈希冲突如何解决?

对时间要求比较高,对占用内存空间大小要求不高。

开放地址法、再哈希法

如果一笔订单需要你的上级审核,但是上级很忙,你会怎么办?

如果这个客户是和我们第一次交易并且时间有很紧急,那么可以考虑找上上一级的领导。如果这个客户已经和我们有过多次交易,并且上级没时间,那么一般公司会有应对措施,按照公司的流程走就好了。

如果你上级领导和上上级领导同时给你安排任务,时间重合,只有精力完成一个,且都很紧急,你会怎么做?

1、像这种情况比较少,因为一般来说,越级管理是比较少的,或者说是不提倡的。

2、如果真的遇到的话,我自己要先对这两件事有一个基本的判断;主动和直属上级去沟通,说明情况,并说说自己的解决方案。

比如:

1、王总,是这样的,有点工作上的事情,想请教您。最近手头上有一个比较紧急的项目,但是今天刘总,临时又安排了一个工作,但和现在手头上的事情有些冲突,怕不能按时完成,我想再和您确认一下时间线。

2、或者两个任务一定会有先后顺序,所以极少的情况下,两个会撞到一起:所以我们再接到第二个任务的时候,就可以和负责人说清楚,目前自己手头的事情,以及要完成的时间。

3、如果我们要说一下自己的建议的话,可以这样说:领导,您看这两个事情,确实比较重要,都属于紧急重要的事情,我这边可能没办法一个人完成,如果可以的话,我能否邀请孙同事和我一起,我想应该可以按时完成的。

函数指针,函数的参数为int,返回值为字符指针

char* (*p)(int);

宏定义求最大值

#define MAX(a, b) (a) > (b) ? a : b

什么是MMU

MMU,全程Memory Manange Unit(存储器管理单元),目的是实现虚拟地址到物理地址的转换(映射)

为什么需要MMU

为了解决已下两个问题:

a) 为了在小内存中运行大程序,实际情况中,程序容量大于内存容量。

b) 系统中有很多程序需要同时执行,它们要求的内存空间比实际的内存空间大

MMU为什么可以解决以上两个问题呢?

MMU可以虚拟地址空间,将地址空间放大。作为32位系统,其虚拟地址空间可达到2^32,即地址空间为0~0xffffffff,共4G空间。这个空间比实际内存空间大很多。

MMU怎么实现虚拟地址到物理地址的转换?

映射。MMU将虚拟地址空间和物理地址空间划分为大小一样的小空间(称为段或页),然后在物理空间和虚拟空间之间建立一一对应的关系

进程间的通信方式有哪些?

管道(Pipe、FIFO)、信号、信号量、消息队列、共享内存(最快)、套接字(socket)

僵尸进程,孤儿进程

孤儿进程:父进程结束,子进程被init进程(PID 1)接收的进程

僵尸进程:子进程退出,父进程没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。

两个Linux操作系统之间有什么命令进行文件传输

1、scp:scp就是secure copy,是用来进行远程文件拷贝的。数据传输使用 ssh

2、wget:通过HTTP、HTTPS、FTP三个最常见的TCP/IP协议下载

3、rsync:sync是类unix系统下的数据镜像备份工具

说一下你知道的排序算法

不稳定排序:快速排序、希尔排序、选择排序、堆排序

稳定排序:冒泡排序、插入排序、归并排序、基数排序、计数排序

说一下快排的过程,快排的时间复杂度

快排最坏O(n^2),最好和平均O(nlogn)

选择一个数,一般是第一个数,把小于它的放在左边,不小于它的放在右边,然后进行递归左半部和右半部。

翻转字符串中的单词

typedefdefine有什么区别

typedef在编译时处理,具有类型检查的功能;有自己的作用域;定义指针的别名时,可以连续定义两个指针变量

define在预编译时展开,不会进行错误检查,只是简单的字符替换;没有作用域限制;不能连续定义两个指针变量

数组小标可以为负数么?

可以。

int main()  

{  

    int a[ 5 ] = { 0, 1, 2, 3, 4 }; 

    int* p = &a[ 4 ];   

    for ( int i = -4; i <= 0; i++ ) 

    { 

        std::cout << p[ i ] << std::endl; 

    } 

    return 0; 

}

输出结果:0 1 2 3 4

指针变量p指向了数组a的最后一个元素,然后让i从-4递增,其实p[i] = a[i+4]

不能用sizeof()函数,如何判断操作系统是16位还是32位?

在16位系统中,int变量的范围-32768~32767,当32767 + 1变为-32768,就可以判断该系统是16位的

中断响应的执行流程,听过顶半部和底半部么?

CPU接受中断 -> 保存中断上下文 -> 跳转到中断处理 -> 执行中断上半部 -> 执行中断下半部 -> 恢复中断上下文

顶半部执行一般都是比较紧急的任务,如清中断;底半部执行的一般都是一些不太紧急的任务,节约中断处理时间

你知道的Linux指令有哪些?

ls、ps、rm、mv、vim、mkdir、find、touch、cat、more、chmod、ifconfig

busybox是什么?

缩小版的Unix系统常用命令工具箱。主要包含了一些常用的Linux指令、环境

什么是根文件系统?

根文件系统是内核启动时所挂载的第一个文件系统,内核代码映像文件保存在根文件系统中。

程序的编译分为几部分?

预编译、编译、汇编、链接

为什么是四字节对齐,为什么需要对齐?

对于32位系统来说,四字节对齐能够使CPU访问速度提高,同时有效的节约存储空间。

如何求一个结构体成员变量的地址?

offsetof(type, member))   ((size_t) &((type*)0)->member)

函数指针和指针函数

函数指针:是指向函数的指针变量,即本质是一个指针变量;

指针函数:是一个返回值为地址的函数,本质是一个函数。

什么是野指针?如何避免?

野指针:指向不确定地址的指针变量,即没有初始化。使用野指针容易因为内存泄漏出现段错误。

避免:当一个指针指针没有指向的时候,一般默认指向NULL;使用malloc分配内存

malloc使用步骤:

1)分配内存(若分配成功,返回内存的首地址;分配失败,返回NULL)

2)检查是否分配成功(若失败,则exit(1)退出程序)

3)清空内存中的数据(memset或bzero)

4)使用内存

5)释放内存(free,这时指针变成了野指针)

6)让指针指向NULL

哈希表是什么?如何使用?

在记录的存储位置和它Key建立一个确定的对应关系f(映射关系),使Key与结构中唯一一个存储位置相对应,对于这种映射关系,我们称之为哈希函数,按照这个思想建立的表,我们称之为哈希表或散列表。

unordered_map<string, int> my_map

my_map["小明"] = 20;

SPI是什么?有几根线?几种模式?

SPI:串行外部设备接口,是一种全双工、高速、同步的通信总线

用途:Flash、数模转换器、信号处理器、控制器、EEPROM存储器

SPI通常有四根线:SCK(串行时钟)、CS(片选信号)、MOSI(主发从收信号)、MISO(主收从发信号)

四种模式:通过CPHA(时钟相位)和CPOL(时钟极性)来控制设备的通信模式

CPHA = 0,数据采样是在第一个边沿,数据发送是在第二个边沿

CPHA = 1,数据采样是在第二个边沿,数据发送是在第一个边沿

CPOL = 0,当SCLK = 0时处于空闲状态,有效状态就是SCLK处于高电平时

CPOL = 1,当SCLK = 1时处于空闲状态,有效状态就是SCLK处于低电平时

堆和栈有什么区别?

1、栈由系统分配;堆是人为申请开辟

2、栈获得的空间较小;堆获得的空间较大

3、栈由系统分配,速度快;堆一般速度比较慢

4、栈是连续的空间;堆是不连续的空间

调用函数时,哪些内容需要压栈?

1、参数入栈:从右向左依次压入栈

2、返回地址入栈:供函数返回时继续使用

3、代码区跳转:从当前代码区跳转到被调用函数的入口处

4、栈帧调整:

1)保存当前栈帧状态值,以备后面恢复本栈帧时使用(EBP入栈)

2)将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)

3)给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈顶)

uboot启动前需要做哪些事情?

第一阶段:

1)设置CPU工作模式为特权(SVC)模式

2)关看门狗,关中断,关MMU和Cache

3)初始化内存、串口、网口

4)加载uboot第二阶段的代码到RAM

5)设置栈

6)清除bss段启动垃圾

7)跳转到第二阶段代码入口

第二阶段:

1)初始化外设(初始化串口模块,SPI模块,IIC模块,A/D模块,PWM模块,CAN模块,EEPROM)

2)检测系统内存映射

3)将内核映像和根文件系统映像从存储区域(Flash、SD Card、eMMC)读取到RAM中

4)为内核设置启动参数

5)调用内核

uboot启动时使用的是物理地址还是虚拟地址?MMU需要开启么?

关闭MMU,MMU是用于虚拟地址向物理地址进行一个映射的结构。

在uboot阶段操作,直接操作物理地址,不需要转换

x86汇编和Arm汇编有什么区别?

两者完全不同

Arm是精简指令集

x86是复杂指令集

介绍一个你熟悉的驱动程序

1、看原理图,确定控制LED的引脚

2、看主芯片的芯片手册,确定如何设置控制这个引脚

3、写程序, 操作GPIO

1)打开时钟源

2)选择模式

3)设置电平

4、编写Makefile

Linux系统的启动流程

1、BIOS阶段:BIOS中的代码把引导器加载进如机器的内存中,控制权交给引导器

2、Loader阶段:引导器把Linux内核加载进入内存中,确定文件系统的位置,将根文件系统的镜像加载进入内存中,控制权交给内核

3、Kernel阶段:挂载根文件系统,内核启动第一个进程init,启动“/bin/sh”,提供给用户一个人机交互界面

4、init进程阶段:查找第一个硬件配置文件“/etc/inittab”,启动系统服务进程,启动用户登录界面

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值