嵌入式面试汇总(更新中)

CSDN主页https://blog.csdn.net/weixin_46144018?spm=1000.2115.3001.5343

b站主页https://space.bilibili.com/174043195?spm_id_from=333.337.0.0

linux

1、LINUX下的Socket套接字和Windows下的WinSock有什么共同点?请从C/C++语言开发的角度描述,至少说出两点共同点。(10分,说得好的每点加5分,没有上限。精通SOCK编程者破格录用。)

本题的目的就是考考应试者的SOCK功底,以及应用能力。答案可谓是五花八门,但是答对的少得可怜。其实答案很多,也很好回答。可见面试者功底确实一般,对SOCK编程只是一知半解。更有甚者,把TCP/IP的原理搬出来讲了一大堆,却无一条符合要求。
第1题,答中一个得5分,答出其它正确答案的,也得5分。
a)都基于TCP/IP协议,都提供了面向连接的TCP SOCK和无连接的UDP SOCK。
b)都是一个sock结构体。
c)都是使用sock文件句柄进行访问。
d)都具有缓冲机制。

2. Linux有内核级线程么?

答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象DOS这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会;而内核线程则没有各个制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。

3.使用线程是如何防止出现大的波峰。

答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队等候。

4.进程死锁的原因

进程死锁是指多个进程在竞争资源时陷入无限等待的状态,无法继续执行下去。死锁通常涉及多个资源和多个进程,其中每个进程都在等待其他进程释放资源,从而导致所有进程都无法继续执行。进程死锁的主要原因可以归结为以下几点:

  1. 互斥条件(Mutual Exclusion): 进程对某些资源的访问是排他性的,即同一时间只能有一个进程访问该资源。如果多个进程同时获取了某些资源,并且彼此互斥,那么可能会发生死锁。
  2. 占有并等待(Hold and Wait): 进程在等待其他进程的资源同时,仍然保持自己已经获取的资源。这导致其他进程无法访问这些资源,可能会在等待中陷入死锁。
  3. 不可剥夺条件(No Preemption): 已经分配给某个进程的资源不能被强制性地剥夺,只能由进程自己释放。如果一个进程无法获取所需资源,同时也无法释放已经获取的资源,就可能导致死锁。
  4. 循环等待(Circular Wait): 多个进程形成一个循环,每个进程都在等待下一个进程所拥有的资源。这种情况下,进程之间会形成一个闭环,导致死锁。

为了避免进程死锁,可以采取一些策略和方法:

  • 破坏死锁的四个条件: 为了防止死锁,可以破坏死锁的四个条件之一。例如,引入资源的抢占、限制进程的最大资源需求、资源预分配等方法。
  • 死锁检测和恢复: 定期检测系统中是否发生了死锁,如果检测到死锁,可以通过终止某些进程来恢复系统正常。
  • 资源分配策略: 采用合适的资源分配策略,以降低死锁的发生概率,如银行家算法。
  • 避免死锁: 通过合理的资源分配,预测进程的资源需求,以避免进程死锁。

进程死锁是操作系统中一个重要的概念,需要在设计和编写多进程程序时注意避免和解决死锁问题。

5.请问单核机器上写多线程程序,是否需要考虑加锁,为什么?

在单核机器上写多线程程序,仍然需要线程锁。因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。

6.多线程,线程同步的几种方式

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;

线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。

线程间通信的方式:

1、临界区:

通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;

2、互斥量 Synchronized/Lock:

采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问

3、信号量 Semphare:

为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。

4、事件(信号),Wait/Notify:

通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作

7.Linux的4种锁机制:

互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒

读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。

自旋锁:spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费CPU资源。

RCU:即read-copy-update,在修改数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后,再将老数据update成新的数据。使用RCU时,读者几乎不需要同步开销,既不需要获得锁,也不使用原子指令,不会导致锁竞争,因此就不用考虑死锁问题了。而对于写者的同步开销较大,它需要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操作。在有大量读操作,少量写操作的情况下效率非常高。

RT-THREAD

1.在RT-Thread中,也提供了互斥量。关于RT-Thread互斥量的描述,不正确的是()

A.互斥量又叫相互排斥的信号量,是一种特殊的二值信号量。

B.拥有互斥量的线程拥有互斥量的所有权。

C.互斥量不支持递归访问。

D.互斥量只能由持有线程释放。

在RT-Thread中,互斥量又叫相互排斥的信号量,是一种特殊的二值信号量。互斥量和信号量不同的是:拥有互斥量的线程拥有互斥量的所有权,互斥量支持递归访问且能防止线 程优先级翻转;并且互斥量只能由持有线程释放,而信号量则可以由任何线程释放。 故C描述错误

2.在RT-Thread中,信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或 互斥的目的。关于RT-Thread信号量的描述,错误的是()

A.每个信号量对象都有一个信号量值和一个线程等待队列。

B.当信号量值为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上。

C.在 RT-Thread 中,采用信号量控制块管理信号量。

D.当信号量不可用,有多个线程等待时,等待队列中先进入的线程将先获得等待的信号量。

在RT-Thread中,每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值 对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以 被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可 用的信号量实例(资源)。 当信号量不可用,有多个线程等待时,等待线程队列既可以按照先进先出的方式排队,先进入的线程将先获得等待的信号量,也可以等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。用户可自行配置。故D描述错误

3.在RT-Thread中,空闲线程是系统创建的最低优先级的线程,关于空闲线程的描述,错误的是()

A.线程状态永远为就绪态。

B.空闲线程不能被挂起。

C.空闲线程会回收被删除线程的资源。

D.空闲线程不能运行用户态任务。

在RT-Thread中,空闲线程是系统创建的最低优先级的线程,线程状态永远为就绪态。当系统中无其他就绪线程存在时, 调度器将调度到空闲线程,它通常是一个死循环,且永远不能被挂起。若某线程运行完毕,系统将自动删除线程:自动执行 rt_thread_exit() 函数,先将该线程从系统就绪 队列中删除,再将该线程的状态更改为关闭状态,不再参与系统调度,然后挂入 rt_thread_defunct 僵尸 队列中,最后空闲线程会回收被删除线程的资源。 空闲线程也提供了接口来运行用户设置的钩子函数,在空闲线程运行时会调用该钩子函数运行用户设置的一些任务。故D描述错误

4.下列关于RT-Thread的I/O设备管理,说法错误的是()?

A.RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成

B.RT-Thread实现了按数字编号访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。

C.RT-Thread支持对不同的设备可以挂接相应的事件

D.在RT-Thread中,当设备事件触发时,由驱动程序通知给上层的应用程序。

RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。

它实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备;注意并不是按数字编号访问。

在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。

当设备事件触发时,由驱动程序通知给上层的应用程序。

5.RT-Thread内核对象分为两类:静态内核对象和动态内核对象,关于内核对象和动态内核对象区别的描述,不正确的是()

A.静态线程对象的线程控制块与栈空间是编译时决定的。

B.动态线程对象的线程控制块与栈空间是动态分配的。

C.静态对象会占用 RAM 空间,不依赖于内存堆管理器,当对象被删除后,占用的 RAM 空间被释放。

D.动态对象则依赖于内存堆 管理器,运行时申请 RAM 空间。

静态内核对象和动态内核对象主要有以下区别:

1、静态线程对象的线程控制块与栈空间是编译时决定的。而动态线程对象的线程控制块与栈空间是动态分配的。

2、静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆 管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放。

这两种方式各有利弊,在实际编程中,可以根据实际环境需求选择具体使用方式。

故C描述错误

6.STM32可支持RT-Thread操作系统的移植,下列关于RT-Thread移植在STM32上的烧录文件,说法错误的是()?

A.它的烧录文件又称为可执行映像文件,一般是bin文件或hex文件

B.它包含 RO 段和 RW 段两个部分

C.它的 RO 段中保存了 Code、RO-data 的数据

D.它的RW 段保存了 RW-data 和 ZI-data 的数据

RT-Thread的程序软件烧录到STM32中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。

它是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:

其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在烧录的映像文件中。

7.下列关于RT-Thread的内存管理,说法错误的是()?

A.RT-Thread 支持静态内存池管理及动态内存堆管理

B.当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的

C.RT-Thread支持针对大内存块的分配管理方法,它叫slab管理算法

D.RT-Thread支持针对小内存块的分配管理方法,它叫memheap 管理算法
RT-Thread 支持静态内存池管理及动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒

RT-Thread 操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法。总体上可分为两类:内存堆管理与内存池管理,而内存堆管理又根据具体内存设备划分为三种情况:

第一种是针对小内存块的分配管理(小内存管理算法);

第二种是针对大内存块的分配管理(slab 管理算法);

第三种是针对多内存堆的分配情况(memheap 管理算法)。

综上可知,本题答案应选D。

8.系统线程是指由系统创建的线程,用户线程是由用户程序调用线程管理接口创建的线 程,在以下线程中属于RT-Thread系统线程的是()

A.主线程 B.空闲线程 C.用户编写的打印线程 D.处理中断服务的线程

系统线程是指由系统创建的线程,用户线程是由用户程序调用线程管理接口创建的线 程,在 RT-Thread 内核中的系统线程有空闲线程和主线程。用户编写的打印线程和处理中断服务的线程都属于用户线程,运行在用户态。故正确答案是AB

9.RT-Thread内核对象分为两类:静态内核对象和动态内核对象,关于内核对象和动态内核对象区别的描述,不正确的是()

A.静态线程对象的线程控制块与栈空间是编译时决定的。

B.动态线程对象的线程控制块与栈空间是动态分配的。

C.静态对象会占用 RAM 空间,不依赖于内存堆管理器,当对象被删除后,占用的 RAM 空间被释放。

D.动态对象则依赖于内存堆 管理器,运行时申请 RAM 空间。
静态内核对象和动态内核对象主要有以下区别:

1、静态线程对象的线程控制块与栈空间是编译时决定的。而动态线程对象的线程控制块与栈空间是动态分配的。

2、静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆 管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放。

这两种方式各有利弊,在实际编程中,可以根据实际环境需求选择具体使用方式。

故C描述错误

操作系统

1.列举几种进程的同步机制,并比较其优缺点。

答:原子操作、信号量机制、自旋锁、管程、会合、分布式系统

2.优先级反转问题在嵌入式系统中是一中严重的问题,必须给与足够重视。

a) 首先请解释优先级反转问题
b) 很多RTOS提供优先级继承策略(Priority inheritance)和优先级天花板策略(Priority ceilings)用来解决优先级反转问题,请讨论这两种策略。
答:
优先级反转:高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上来看,好像是中优先级的任务比高优先级任务具有更高的优先权。
具体来说,当高优先级任务正等待信号量(此信号量被一个低优先级任务拥有着)的时候,一个介于两个任务优先之间的中等优先级任务开始执行,这就会导致一个高优先级任务在等待一个低优先级任务,而低优先级任务却无法执行类似死锁的情形发生。
优先级继承策略:进程调度算法对获取到临界资源的进程(A)增加其优先级为所有等待该资源的进程中的最高优先级。一旦进程(A)释放了该资源,就恢复到原来的优先级。
优先级天花板策略:给每个信号量设置一个优先级天花板,优先级天花板的值大于所有使用该信号的任务的优先级,当某个任务得到该信号量时,将其优先级置为优先级天花板的值。
权重:中
备注:平常编程时用不到,但需要了解运行原理,类似的还有内存管理的原理。

3.操作系统采用缓冲技术,能够减少CPU(A)次数,从而提高资源的利用率。

A 中断
B 访问
C 控制
D 依赖
解析: A
引入缓冲的主要原因包括:缓和CPU与I/O设备间速度不匹配的矛盾;减少对CPU的中断频率,放宽对中断响应时间的限制;提高CPU和I/O设备之间的并行性。所以采用缓冲技术,可减少对CPU的中断次数,从而提高系统效率。
权重:中

4.uClinux 和linux的区别(或者说rtos和linux的区别)

对uCLinux来说,其设计针对没有MMU的处理器,不能使用处理器的虚拟内存管理技术。uCLinux仍然采用存储器的分页管理,系统在启动时把实际存储器进行分页。在加载应用程序时程序分页加载。但是由于没有MMU管理,所以实际上uCLinux采用实存储器管理策略。uCLinux系统对于内存的访问是直接的,所有程序中访问的地址都是实际的物理地址。操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。
权重:较高

5.中断(interrupt,如键盘中断)与异常(exception,如除零异常)有何区别?

异常:在产生时必须考虑与处理器的时钟同步,实践上,异常也称为同步中断。在处理器执行到由于编程失误而导致的错误指令时,或者在执行期间出现特殊情况(如缺页),必须靠内核处理的时候,处理器就会产生一个异常。
所谓中断应该是指外部硬件产生的一个电信号,从cpu的中断引脚进入,打断cpu当前的运行;
所谓异常,是指软件运行中发生了一些必须作出处理的事件,cpu自动产生一个陷入来打断当前运行,转入异常处理流程。
权重:中

6. 优先级反转问题在嵌入式系统中是一中严重的问题,必须给与足够重视。

a) 首先请解释优先级反转问题
b) 很多RTOS提供优先级继承策略(Priority inheritance)和优先级天花板策略(Priority ceilings)用来解决优先级反转问题,请讨论这两种策略。
答:
高优先级任务需要等待低优先级任务释放资源,而低优先级任务又正在等待中等优先级任务的现象叫做优先级反转
优先级继承策略(Priority inheritance):继承现有被阻塞任务的最高优先级作为其优先级,任务退出临界区,恢
复初始优先级。
优先级天花板策略(Priority ceilings):控制访问临界资源的信号量的优先级天花板。
优先级继承策略对任务执行流程的影响相对教小,因为只有当高优先级任务申请已被低优先级任务占有的临界资源
这一事实发生时,才抬升低优先级任务的优先级。
权重:中

7.进程调度策略。

先进先出算法,最短CPU运行期优先调度算法,轮转法,多级队列方法
权重:较高

8.进程间通讯机制有哪些?

答:linux下使用的进程间通信的方式主要有:管道和有名管道,信号,消息队列,共享内存,信号量,套接字。
权重:较高

====

9.比较一下进程和线程的区别?

(1)、调度:线程是CPU调度和分派的基本单位
(2)、拥有资源:

  • 进程是系统中程序执行和资源分配的基本单位
  • 线程自己一般不拥有资源(除了必不可少的程序计数器,一组寄存器和栈),但他可以去访问其所属进程的资源,
    如进程代码,数据段以及系统资源(已打开的文件,I/O设备等)。
    (3)系统开销:
  • 同一进程中的多个线程可以共享同一地址空间,因此它们之间的同步和通信的实现也比较简单
  • 在进程切换的时候,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置;
    而线程切换只需要保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作,从而能更有效地使用系统资源和
    提高系统吞吐量。
    权重:较高
10.进程和线程的区别:

​ 1:调度:线程作为调度和分配的基本单元,进程作为拥有资源的基本单位;
​ 2:并发性:不仅进程可以并发执行,同一进程内的线程也可以并发执行。
​ 3:拥有资源:进程是拥有资源的基本独立单元,线程不拥有资源,但可以访问进程内的资源;
​ 4:在创建或撤销线程时,由于系统都要为之分配和回收内存资源,导致系统的开销明显大于创建或撤销线程时的开销。
权重:较高

11.死锁的四个条件及处理方法。

(1)互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
   解决死锁的方法分为死锁的预防,避免,检测与恢复三种
权重:高

12.描述实时系统的基本特性

​ 在特定时间内完成特定的任务,实时性与可靠性。
权重:高

13.描述一下嵌入式操作系统与通用操作系统的区别

嵌入式操作系统和通用操作系统在设计和应用上有许多差异。以下是它们之间的主要差别:

  1. 用途和应用场景:
    • 嵌入式操作系统: 这种操作系统专门设计用于嵌入式系统,如嵌入式设备、嵌入式控制器、嵌入式单板计算机等。它们通常用于特定应用领域,如工业自动化、医疗设备、汽车电子、家电等。
    • 通用操作系统: 通用操作系统(也称为桌面操作系统或服务器操作系统)设计用于通用计算机系统,如个人计算机、笔记本电脑、服务器等。它们面向多种应用领域,提供广泛的功能和服务。
  2. 资源约束:
    • 嵌入式操作系统: 嵌入式系统通常具有有限的计算资源(如内存、处理能力),嵌入式操作系统需要在这些资源的限制下运行。它们通常需要在资源受限的环境下高效运行,同时满足特定的实时性要求。
    • 通用操作系统: 通用操作系统运行在资源更为丰富的计算机上,具有更大的内存、更高的处理能力和更多的外围设备支持。
  3. 实时性要求:
    • 嵌入式操作系统: 嵌入式系统中的许多应用需要满足实时性要求,即在预定的时间范围内响应和处理事件。嵌入式操作系统通常需要提供实时性能能力,以确保系统对于实时任务的及时响应。
    • 通用操作系统: 通用操作系统通常不会提供强实时性能能力,因为大多数应用并不要求在严格的实时性要求下运行。
  4. 定制化和配置性:
    • 嵌入式操作系统: 由于嵌入式系统的多样性,嵌入式操作系统通常允许较高程度的定制化和配置。用户可以根据应用需求选择和裁剪操作系统的组件和功能。
    • 通用操作系统: 通用操作系统更多地关注通用性,提供一套标准的功能和特性,较少提供定制化选项。
  5. 文件系统和网络支持:
    • 嵌入式操作系统: 嵌入式操作系统可能需要支持小型、定制的文件系统,同时在资源受限的环境中提供网络功能。文件系统和网络支持通常根据应用需求进行优化。
    • 通用操作系统: 通用操作系统提供广泛的文件系统和网络支持,以适应各种通用计算需求。
  6. 开发和调试:
    • 嵌入式操作系统: 嵌入式系统的开发和调试可能更为复杂,因为需要考虑硬件和实时性等因素。嵌入式开发通常需要使用交叉编译工具链,并在嵌入式硬件上进行测试和调试。
    • 通用操作系统: 通用操作系统的开发和调试相对较为简单,因为它们在更通用的计算机上运行,开发工具和环境更为常见和标准化。

总体而言,嵌入式操作系统和通用操作系统在设计、应用和性能方面有着明显的差异,取决于它们所服务的不同领域和需求。

14.操作系统中进程调度策略有哪几种?

操作系统中有多种进程调度策略,用于决定在多个就绪态进程之间选择哪个进程获得 CPU 时间片来执行。以下是常见的几种进程调度策略:

  1. 先来先服务调度(First-Come, First-Served,FCFS): 按照进程到达的顺序分配 CPU 时间片。优点是简单且公平,但可能会导致长作业等待时间过长(所谓的"饥饿"现象)。
  2. 最短作业优先调度(Shortest Job Next,SJN): 选择执行时间最短的进程,以最小化平均等待时间。优点是最大程度地减少平均等待时间,但需要提前知道每个进程的执行时间,且可能导致长作业等待时间较长。
  3. 最短剩余时间优先调度(Shortest Remaining Time First,SRTF): 在 SJN 的基础上,允许在执行过程中抢占进程,即如果有新进程到达并其剩余执行时间比当前进程短,则可以切换执行。能够最大程度地减少等待时间,但需要频繁的上下文切换,可能会引入开销。
  4. 轮转调度(Round Robin,RR): 将 CPU 时间划分成时间片,每个进程在一个时间片内执行。当时间片用完后,将当前进程放到队列末尾,选择下一个进程执行。适用于多任务环境,保证每个进程都有机会执行,但可能会导致上下文切换频繁。
  5. 优先级调度(Priority Scheduling): 为每个进程分配一个优先级,CPU 时间片将被分配给具有最高优先级的进程。可以是静态优先级(提前设置)或动态优先级(根据执行情况调整)。但可能导致优先级较低的进程饥饿。
  6. 多级反馈队列调度(Multilevel Feedback Queue,MLFQ): 将就绪队列划分为多个队列,每个队列有不同的优先级。进程在初始队列中执行,根据时间片用尽或其他条件调整优先级,可能会被移动到更高或更低的队列中。
  7. 最高响应比优先调度(Highest Response Ratio Next,HRRN): 在每个时间片内选择相应比最高的进程执行,响应比定义为(等待时间 + 服务时间)/ 服务时间。优先选择等待时间长且服务时间短的进程。

C语言

1.关键字STATIC的作用是什么?

这个简单的问题很少有人能回答完全。在C语言中,关键字STATIC有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

2.关键字CONST有什么含意?

我只要一听到被面试者说:“CONST意味着常数”,我就知道我正在和一个业余者打交道。去年DAN SAKS已经在他的文章里完全概括了CONST的所有用法,因此ESP(译者:EMBEDDED SYSTEMS PROGRAMMING)的每一位读者应该非常熟悉CONST能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出CONST意味着"只读"就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下SAKS的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题:
下面的声明都是什么意思?

CONST INT A; INT CONST A; CONST INT *A; INT * CONST A; INT CONST * A CONST;
前两个的作用是一样,A是一个常整型数。第三个意味着A是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思A是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着A是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 CONST,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字CONST呢?我也如下的几下理由:

1) 关键字CONST的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用CONST的程序员很少会留下的垃圾让别人来清理的。)
2) 通过给优化器一些附加的信息,使用关键字CONST也许能产生更紧凑的代码。
3) 合理地使用关键字CONST可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少BUG的出现。
3.关键字VOLATILE有什么含意?并给出三个不同的例子。

一个定义为VOLATILE的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是VOLATILE变量的几个例子:

1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(NON-AUTOMATIC VARIABLES)
3) 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到VOLATILE变量。不懂得VOLATILE的内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得VOLATILE完全的重要性。
1)一个参数既可以是CONST还可以是VOLATILE吗?解释为什么。
2); 一个指针可以是VOLATILE 吗?解释为什么。
3); 下面的函数有什么错误:

INT SQUARE(VOLATILE INT *PTR)
{
RETURN *PTR * *PTR;
}

下面是答案:
1)是的。一个例子是只读的状态寄存器。它是VOLATILE因为它可能被意想不到地改变。它是CONST因为程序不应该试图去修改它。
2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个BUFFER的指针时。

3) 这段代码有点变态。这段代码的目的是用来返指针*PTR指向值的平方,但是,由于*PTR指向一个VOLATILE型参数,编译器将产生类似下面的代码:

INT SQUARE(VOLATILE INT *PTR)
{
INT A,B;
A = *PTR;
B = *PTR;
RETURN A * B;
}

由于*PTR的值可能被意想不到地该变,因此A和B可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

LONG SQUARE(VOLATILE INT *PTR)
{
INT A;
A = *PTR;
RETURN A * A;
}

位操作(BIT MANIPULATION)

4.下面的代码输出是什么,为什么?

VOID FOO(VOID)
{
UNSIGNED INT A = 6;
INT B = -20;
(A+B > 6) ? PUTS(“> 6”) : PUTS(“<= 6”);
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

5.尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(HEAP)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?

这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. PLAUGER, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:
下面的代码片段的输出是什么,为什么?

CHAR *PTR;
IF ((PTR = (CHAR *)MALLOC(0)) == NULL)
PUTS(“GOT A NULL POINTER”);
ELSE
PUTS(“GOT A VALID POINTER”);

这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数MALLOC,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是"GOT A VALID POINTER"。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。

6.TYPEDEF 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子:

#DEFINE DPS STRUCT S *
TYPEDEF STRUCT S * TPS;

以上两种情况的意图都是要定义DPS 和 TPS 作为一个指向结构S指针。哪种方法更好呢?(如果有的话)为什么?
这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:TYPEDEF更好。思考下面的例子:

DPS P1,P2;
TPS P3,P4;

第一个扩展为

STRUCT S * P1, P2;
.
上面的代码定义P1为一个指向结构的指针,P2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了P3 和P4 两个指针。

7.C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

INT A = 5, B = 7, C;
C = A+++B;

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

C = A++ + B;

因此, 这段代码持行后A = 6, B = 7, C = 12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。

8.嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

A more obscure approach is:
一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

9.void的含义

void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。
  void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义:
void a;
  这行语句编译时会出错,提示“illegal use of type ‘void’”。不过,即使void a的编译不会出错,它也没有任何实际意义。
  void真正发挥的作用在于:
  (1) 对函数返回的限定;
  (2) 对函数参数的限定。

我们将在第三节对以上二点进行具体说明。
  众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。

例如:
float *p1;
int *p2;
p1 = p2;
  其中p1 = p2语句会编译出错,提示“‘=’ : cannot convert from ‘int *’ to ‘float *’”,必须改为:
p1 = (float *)p2;
  而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:
void *p1;
int *p2;
p1 = p2;
  但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错:
void *p1;
int *p2;
p2 = p1;
  提示“‘=’ : cannot convert from ‘void *’ to ‘int *’”。

10.堆和栈的基本知识

基础知识:
堆栈是一种简单的数据结构,是一种只允许在其一端进行插入或删除的线性表。允许插入或删除操作的一端称为栈顶,另一端称为栈底,对堆栈的插入和删除操作被称为入栈和出栈。有一组CPU指令可以实现对进程的内存实现堆栈访问。其中,POP指令实现出栈操作,PUSH指令实现入栈操作。CPU的ESP寄存器存放当前线程的栈顶指针,EBP寄存器中保存当前线程的栈底指针。CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从 EIP寄存器中读取下一条指令的内存地址,然后继续执行。


堆(Heap)栈(Stack)
1、内存分配方面:
堆:一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式是类似于链表。可能用到的关键字如下:new、malloc、delete、free等等。
栈:由编译器(Compiler)自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、申请方式方面:
堆:需要程序员自己申请,并指明大小。在c中malloc函数如p1 = (char *)malloc(10);在C++中用new运算符,但是注意p1、p2本身是在栈中的。因为他们还是可以认为是局部变量。
栈:由系统自动分配。 例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间。
3、系统响应方面:
堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
4、大小限制方面:
堆:是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
5、效率方面:
堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
栈:由系统自动分配,速度较快。但程序员是无法控制的。
6、存放内容方面:
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈,然后是函数中的局部变量。 注意: 静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
7、存取效率方面:
堆:char *s1 = “Hellow Word”;是在编译时就确定的;
栈:char s1[] = “Hellow Word”; 是在运行时赋值的;用数组比用指针速度要快一些,因为指针在底层汇编中需要用edx寄存器中转一下,而数组在栈上直接读取。

11.局部变量能否和全局变量重名?

答:能,局部会屏蔽全局。要用全局变量,需要使用"::"
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

12.如何引用一个已经定义过的全局变量?

答:extern

13.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

答:可以,在不同的C文件中以static形式来声明同名全局变量。
可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错

14.语句for( ;1 ;)有什么问题?它是什么意思?

答:和while(1)相同。

15. do……while和while……do有什么区别?

答:前一个循环一遍再判断,后一个判断以后再循环

16.请写出下列代码的输出内容

#include
main()
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10*a++;
printf(“b,c,d:%d,%d,%d”,b,c,d);
return 0;
}
答:10,12,120

17.若整数值范围是 -32768 至 32767。在执行下面的表达式之后, i 的结果是什么?

int i=32766;
i=i+1; i =
i=i+1; i =

  1. 初始值为 i = 32766
  2. 执行 i = i + 1,即 i = 32766 + 1,结果为 i = 32767
  3. 执行 i = i + 1,即 i = 32767 + 1,这将导致整数溢出,因为 32768 超出了整数范围,根据有符号整数的溢出规则,会循环回到最小值 -32768。因此,结果为 i = -32768

所以,执行完这两个表达式后,i 的值将变为 -32768

18.简述strcpy、sprintf 与memcpy 的区别

操作对象不同,strcpy 的两个操作对象均为字符串,sprintf 的操作源对象可以是多种数据类型, 目的操作对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。

执行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。

实现功能不同,strcpy 主要实现字符串变量间的拷贝,sprintf 主要实现其他数据类型格式到字 符串的转化,memcpy 主要是内存块间的拷贝。

注意:strcpy、sprintf 与memcpy 都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来 选择合适的函数实现拷贝功能。

19.(void )ptr和((void **))ptr的结果是否相同? 其中ptr为同一个指针(中等题)

(void )ptr 和((void*))ptr 值是相同的。

20.要对绝对地址0x100000赋值,我们可以用(unsigned int *)0x100000=1234; 那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?(中等题)

((void ()( ))0x100000 ) ( );

21.给定结构体(基础题)

struct A

{

​ char t:4;

​ char k:4;

​ unsigned short i:8;

​ unsigned long m;

};

问sizeof(struct A)是多少?

32位8

64位16

22.编写函数void hton(float val,char *buf)把小端序的val转换成大端序的val,存放到buf中(基础题)
union endian
{
	char a[4];
	float b;
}big;
void hton(float val,char *buf)
{
	char temp;
	big.b=val;
	temp=big.a[0];
	big.a[0]=big.a[3];
	big.a[3]=temp;
	//第2字节与第3字节交换
	temp=big.a[1];
	big.a[1]=big.a[2];
	big.a[2]=temp
	strcpy(buf,big.b);
}
void main()
{
	float val=0x12345678;
	char buf[];
	float(val,buf);
}

程序崩溃,指针str与形参指针p不是同一个地址,所以,free ()对str操作则是释放一个空的指针,会出错。

23.现有1M长度的char数组,请将每个字节进行高低位互换(一个字节的八位二进制逆序),如果我们需要频繁地处理这样的数据,请实现一个高效的处理函数(中等题)
unsigned char shift_fun2(unsigned char data)

{
    data=(data<<4)|(data>>4);
    data=((data<<2)&0xcc)|((data>>2)&0x33);
    data=((data<<1)&0xaa)|((data>>1)&0x55);
    printf(" after shift fun2 data=%x \n",data);
    return data;
}

C++

1.C++中为什么用模板类。

答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

2.引用与指针有什么区别?
  1. 引用必须被初始化,指针不必。
  2. 引用初始化以后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。
3.堆栈溢出一般是由什么原因导致的?

没有回收垃圾资源。

4.什么函数不能声明为虚函数?

constructor函数不能声明为虚函数。

5.不能做switch()的参数类型是

switch的参数不能为实型

6.在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?

答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。

7. C++中为什么用模板类。

答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

8. 函数模板与类模板有什么区别?

答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。

9.new/delete和malloc/free的区别

1.malloc/free是C/C++的库函数,需要stdlib.h;new/delete是C++的关键字;

2.都可用于申请动态内存和释放内存,new/delete在对象创建的时候自动执行构造函数,对象消亡前自动执行析构函数,底层实现其实也是malloc/free

3.new无需指定内存块的大小,编译器会根据类型信息自行计算;malloc需要显式地支持所需内存的大小

4.new返回指定类型的指针,无需进行类型转换;malloc默认返回类型为void*,必须强行转换为实际类型的指针

5.new内存分配失败时会抛出bad_alloc异常;malloc失败时返回NULL

10.面向对象和面向过程的区别

面向对象方法中,把数据和数据操作放在一起,组成对象;对同类的对象抽象出其共性组成类;类通过简单的接口与外界发生联系,对象和对象之间通过消息进行通信。面向对象的三大特性是"封装、“多态”、“继承”,五大原则是"单一职责原则"、“开放封闭原则”、“里氏替换原则”、“依赖倒置原则”、“接口分离原则”。

而面向过程方法是以过程为中心的开发方法,它自顶向下顺序进行, 程序结构按照功能划分成若干个基本模块,这些模块形成树状结构。

(过程)优点:

性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗源;比如嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。缺点:没有面向对象易维护、易复用、易扩展。

(对象)优点:

易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统。缺点:性能比面向过程低。

11.对虚函数和多态的理解

多态的实现主要分为静态多态和动态多态,静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。举个例子:一个父类类型的指针指向一个子类对象时候,使用父类的指针去调用子类中重写了的父类中的虚函数的时候,会调用子类重写过后的函数,在父类中声明为加了virtual关键字的函数,在子类中重写时候不需要加virtual也是虚函数。

虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。

12.简述类成员函数的重写、重载和隐藏的区别

(1)重写和重载主要有以下几点不同。

范围的区别:被重写的和重写的函数在两个类中,而重载和被重载的函数在同一个类中。

参数的区别:被重写函数和重写函数的参数列表一定相同,而被重载函数和重载函数的参数列表一 定不同。

virtual 的区别:重写的基类中被重写的函数必须要有virtual 修饰,而重载函数和被重载函数可以被 virtual 修饰,也可以没有。

(2)隐藏和重写、重载有以下几点不同。

与重载的范围不同:和重写一样,隐藏函数和被隐藏函数不在同一个类中。

参数的区别:隐藏函数和被隐藏的函数的参数列表可以相同,也可不同,但是函数名肯定要相同。 当参数不相同时,无论基类中的参数是否被virtual 修饰,基类的函数都是被隐藏,而不是被重写。

注意:虽然重载和覆盖都是实现多态的基础,但是两者实现的技术完全不相同,达到的目的也是完 全不同的,覆盖是动态态绑定的多态,而重载是静态绑定的多态。

进程线程

1.进程死锁的原因和4个必要条件

答:资源竞争及进程推进顺序非法;互斥、请求保持、不可剥夺、环路

2.信号量最常用的两个功能

信号量是用来调协进程对共享资源的访问的。
用于进程中传递信号的一个整数值。
权重:较高

3.进程调度策略。

先进先出算法,最短CPU运行期优先调度算法,轮转法,多级队列方法
权重:较高

4.进程间通讯机制有哪些?

答:linux下使用的进程间通信的方式主要有:管道和有名管道,信号,消息队列,共享内存,信号量,套接字。
权重:较高

====

5.比较一下进程和线程的区别?

(1)、调度:线程是CPU调度和分派的基本单位
(2)、拥有资源:

  • 进程是系统中程序执行和资源分配的基本单位
  • 线程自己一般不拥有资源(除了必不可少的程序计数器,一组寄存器和栈),但他可以去访问其所属进程的资源,
    如进程代码,数据段以及系统资源(已打开的文件,I/O设备等)。
    (3)系统开销:
  • 同一进程中的多个线程可以共享同一地址空间,因此它们之间的同步和通信的实现也比较简单
  • 在进程切换的时候,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置;
    而线程切换只需要保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作,从而能更有效地使用系统资源和
    提高系统吞吐量。
    权重:较高
6.进程和线程的区别:

​ 1:调度:线程作为调度和分配的基本单元,进程作为拥有资源的基本单位;
​ 2:并发性:不仅进程可以并发执行,同一进程内的线程也可以并发执行。
​ 3:拥有资源:进程是拥有资源的基本独立单元,线程不拥有资源,但可以访问进程内的资源;
​ 4:在创建或撤销线程时,由于系统都要为之分配和回收内存资源,导致系统的开销明显大于创建或撤销线程时的开销。
权重:较高

7.死锁的四个条件及处理方法。

(1)互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
   解决死锁的方法分为死锁的预防,避免,检测与恢复三种
权重:高

8.LINUX中的管道指什么,重定向又指什么?

管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:

  1. 其本质是一个伪文件(实为内核缓冲区)
  2. 由两个文件描述符引用,一个表示读端,一个表示写端。
  3. 规定数据从管道的写端流入管道,从读端流出。
    管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
    管道的局限性:
    ① 数据自己读不能自己写。
    ② 数据一旦被读走,便不在管道中存在,不可反复读取。
    ③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
    ④ 只能在有公共祖先的进程间使用管道。
    常见的通信方式有,单工通信、半双工通信、全双工通信。

重定向:文件标识符是从0到9结束的整数,指明了与进程有关的特定数据流源。当Linux系统启动一个进程(该进程可能用于执行Shell命令)时,将自动为该进程打开三个文件:标准输入、标准输出和标准错误输出,分别由文件标识符0、1、2标识,如果进程要打开其他的输入和输出文件,则从3开始标识。另外3-9是保留的标识符,可以把这些标识符指定为标准输入、标准输出和标准错误输出的临时连接。通常这样可以解决好多复杂的重定向请求。
例如可以将标准输出重定向到一个文件。
权重:中

单片机

1.中断的概念?简述中断的过程。

可以描述一下isr、dsr,中断上半部、中断下半部,软中断
权重:较高

2.嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?

这个问题用几个解决方案。我首选的方案是:

WHILE(1)
{

}

一些程序员更喜欢如下方案:

FOR(;😉
{

}

这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:"我被教着这样做,但从没有想到过为什么。"这会给我留下一个坏印象。

第三个方案是用 GOTO
LOOP:

GOTO LOOP;
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。

3.中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __INTERRUPT。下面的代码就使用了__INTERRUPT关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__INTERRUPT DOUBLE COMPUTE_AREA (DOUBLE RADIUS)
{
DOUBLE AREA = PI * RADIUS * RADIUS;
PRINTF(“\NAREA = %F”, AREA);
RETURN AREA;
}

这个函数有太多的错误了,以至让人不知从何说起了:
1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。

2) **ISR 不能传递参数**。如果你没有看到这一点,你被雇用的机会等同第一项。
3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4) 与第三点一脉相承,PRINTF()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。
4.描述一下嵌入式基于ROM的运行方式基于ram的运行方式有什么区别。

在嵌入式系统中,程序的运行方式可以基于 ROM(只读存储器)或 RAM(随机访问存储器)。这两种方式有一些重要的区别,下面是它们的描述:

基于 ROM 的运行方式:
  1. 只读存储器: ROM 是只读存储器,意味着它的内容在制造时被写入,之后不可修改。程序和数据通常被预先存储在 ROM 中,因此在系统运行时无法更改。
  2. 稳定性和持久性: 由于 ROM 中的内容是固定的且不可修改,这种运行方式在系统开机后可以确保程序的稳定性和持久性。系统断电后重新上电,程序仍然保持不变。
  3. 启动和初始化: 基于 ROM 的系统通常在上电后会从 ROM 中加载启动代码,这些代码会初始化系统的基本设置和状态,然后开始执行主程序。
  4. 存储容量: ROM 的存储容量有限,因此只适用于存储相对较小的程序和固定的数据。
基于 RAM 的运行方式:
  1. 随机访问存储器: RAM 允许读取和写入操作,程序和数据可以在运行时加载到 RAM 中,并且可以修改。
  2. 灵活性: 基于 RAM 的运行方式更灵活,允许程序在运行时进行修改、扩展和更新,因此适用于需要动态变化的系统。
  3. 启动和初始化: 基于 RAM 的系统可能需要一个初始化程序或者引导加载器从其他存储介质(如 ROM 或闪存)中加载程序和数据到 RAM 中,然后开始执行。
  4. 存储容量: RAM 的存储容量可能相对较大,允许存储更大规模的程序和数据。然而,RAM 中的数据在断电后会丢失。
区别:
  • 基于 ROM 的运行方式在开机后可以保持程序稳定性和持久性,但不允许在运行时修改。基于 RAM 的运行方式更加灵活,但程序和数据会在断电后丢失。
  • 基于 ROM 的系统通常较少受到硬件损坏或不稳定因素的影响,因为 ROM 是只读的。基于 RAM 的系统可能受到硬件损坏的影响,导致数据丢失或系统不稳定。
  • 基于 ROM 的存储容量有限,适合存储较小的程序。基于 RAM 的存储容量相对更大,适合存储较大的程序和数据,但需要考虑断电数据丢失的问题。
  • 基于 ROM 的系统启动速度通常较快,因为启动代码位于固定位置。基于 RAM 的系统可能需要更长的启动时间,因为需要从其他存储介质加载程序和数据。

选择哪种运行方式取决于嵌入式系统的需求,包括稳定性、可靠性、灵活性、存储需求以及启动速度等因素。

5.讲一下stm32的时钟系统

答:STM32微控制器的时钟系统由多个时钟源、时钟树和时钟控制器组成,用于提供系统时钟和外设时钟。以下是 STM32 时钟系统的主要组成部分:

外部时钟晶振:外部时钟源为 STM32 提供了稳定的时钟信号,通常为晶振或外部时钟信号。

内部振荡器:STM32 还集成了一个内部振荡器,可用于在缺少外部时钟源时提供基本时钟。

PLL(锁相环):PLL 可以将输入时钟倍频或分频,提供更高的系统时钟速度。PLL 还可以配置为多个时钟域,以支持多个时钟频率和时钟分频。

时钟树:时钟树是一个包含多个时钟分频器和多路选择器的层次结构,可将输入时钟源分配给各种系统和外设时钟域。

时钟控制器:时钟控制器用于配置时钟源、时钟树和时钟分频,以及为各种外设提供时钟信号。

通过配置 STM32 的时钟系统,可以控制各种外设的时钟速度和时钟源,并提供稳定的系统时钟。这对于保证系统性能和稳定性非常重要。

6.C语言中堆和栈的区别;推挽输出是什么?

答:C语言中的堆和栈是内存管理的两种方式,主要区别如下:

  1. 分配方式:栈是自动分配的,而堆是手动分配的。

  2. 内存分配大小:栈分配的内存大小固定,堆分配的内存大小可以根据需要动态调整。

  3. 内存释放:栈的内存由系统自动管理,不需要手动释放;而堆的内存必须由程序员手动释放,否则会造成内存泄漏。

  4. 内存访问方式:栈的内存访问速度较快,因为它们是顺序分配的;而堆的内存访问速度较慢,因为它们是动态分配的。

推挽输出是一种电子电路输出方式,用于将微控制器的数字输出信号转换为高电平或低电平的电压输出。推挽输出通常由两个晶体管(PNP 和 NPN)组成,其中一个负责输出高电平,另一个负责输出低电平。在推挽输出电路中,当输出高电平时,PNP晶体管打开,NPN晶体管关闭,反之亦然。推挽输出电路常用于驱动电机、灯光等负载,具有较高的输出电流和电压稳定性。

7.假如一个单片机上电后不运行,这是为什么?

答:

  1. 程序错误:单片机程序中的错误可能会导致单片机无法正常运行,例如,程序逻辑错误、死循环等。

  2. 外设故障:如果单片机依赖于外设进行操作,外设故障可能会导致单片机无法正常运行。例如,电源模块、时钟模块、存储器模块等出现故障。

  3. 电源问题:如果单片机供电不足或者不稳定,可能会导致单片机无法正常运行。例如,电池电量不足、电源干扰等。

  4. 外部环境干扰:单片机可能会受到外部环境干扰,例如,电磁场干扰、静电干扰等。

答:如果单片机上电后不运行,需要进行一系列的排查和调试。例如,检查程序是否正确、检查外设是否正常、检查电源是否正常、检查单片机是否受到外部干扰等。针对不同的问题,可能需要采取不同的解决方案。

8.一个温度传感器模块,按照正确的使用方式和参数指标,对人体皮肤测量之后,得到的温度一直都是35摄氏度,这是为什么?

答:如果一个温度传感器模块,按照正确的使用方式和参数指标,对人体皮肤测量之后,得到的温度一直都是35摄氏度,可能有以下原因:

  1. 温度传感器模块工作不正常:温度传感器模块可能出现故障或者损坏,导致无法正确测量温度。可以尝试更换传感器模块或者进行维修。

  2. 温度传感器模块读取数据不正确:温度传感器模块读取数据的方式可能不正确,导致读取的数据始终为35摄氏度。可以检查读取数据的代码是否正确。

  3. 测量位置不正确:人体皮肤温度的测量位置可能不正确,导致得到的温度始终为35摄氏度。可以尝试改变测量位置,例如测量腋下温度、耳温等。

  4. 测量环境温度恒定:如果测量环境的温度恒定为35摄氏度,温度传感器模块可能无法正确测量出人体皮肤的温度。可以将测量环境温度调整到与人体皮肤温度有一定差异的温度。

需要综合考虑以上因素进行排查,找出导致测量结果异常的原因。

9.请说一下5单片机的中断处理流程

答:51单片机的中断处理流程通常包括以下步骤:

  1. 中断请求:外部设备或者程序通过特定的方式发送中断请求信号,使得51单片机进入中断响应状态。

  2. 中断优先级判断:当出现多个中断请求时,51单片机会根据中断优先级进行判断,优先处理优先级高的中断。

  3. 中断向量定位:51单片机通过中断向量表查找中断处理程序的入口地址,从而定位中断处理程序。

  4. 中断处理程序执行:51单片机跳转到中断处理程序的入口地址处开始执行中断处理程序。

  5. 中断标志清除:在中断处理程序中,需要清除相应的中断标志位,表示该中断已被处理完毕。

  6. 恢复现场:如果中断处理程序改变了程序状态和寄存器内容,需要在中断处理程序执行完成后恢复现场,返回到原来的程序流程中。

  7. 继续执行原程序:恢复现场之后,51单片机会继续执行原来的程序流程。

需要注意的是,中断处理程序需要尽可能地短小精悍,避免执行时间过长导致其他中断无法得到及时响应,从而影响系统的实时性。同时,需要合理设置中断优先级,确保各个中断能够得到合理的响应和处理。

10.传感器的输出引脚是高阻抗好还是低阻抗好?

答:传感器的输出引脚是高阻抗还是低阻抗好,这要视具体的应用场景和设计需求而定。

一般来说,传感器的输出引脚采用高阻抗设计,可以减小对传感器的负载,同时也有利于减小电源消耗,提高系统的灵敏度和精度。高阻抗输出一般需要接入运放等信号处理电路进行信号放大和滤波,从而提高信号的稳定性和可靠性。

但是,在一些特定的应用场合中,低阻抗输出也可能是更好的选择。例如,在高速信号传输、大功率负载驱动、长距离信号传输等场合,采用低阻抗输出可以降低信号传输线路的噪声、抗干扰能力更强、驱动能力更强,同时也可以提高信号的可靠性和稳定性。

因此,在设计传感器输出电路时,需要根据具体的应用场景和系统需求进行综合考虑,权衡高阻抗和低阻抗的优缺点,选择合适的方案。

11.为什么开启GPIO模块前会有时钟触发器?

答: 为了降低功耗,STM32在每个模块前面都加了一个时钟触发器默认关闭,当需要使用哪个模块时,开启时钟触发器,才会对该模块供电。否则该模块处于断电状态。

12.假设STM32是3.3V的标准电压:

​ 将 0~0.25V看成低电平
​ 将 2.75~3.3V看成高电平
​ 套在一个较高或较低的电平上

一般 MCU 包含的存储空间有:片内 Flash 与片内 RAM,RAM 相当于内存,Flash 相当于硬盘。编译 器会将一个程序分类为好几个部分,分别存储在 MCU 不同的存储区。 在RT-Thread中,程序所占用的空间分布如下:

Code:代码段,存放程序的代码部分;

RO-data:只读数据段,存放程序中定义的常量;

RW-data:读写数据段,存放初始化为非 0 值的全局变量;

ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;

网络

1.TCP/IP模型(按从高到低的顺序)

应用层

传输层

网络层

网络接口层

2.OSI参考模型(按从高到低的顺序)

应用层

表示层

会话层

传输层

网络层

数据链路层

物理层

3.下列那些不时网际层协议

A、IP
B、ARP
C、MAC
D、ICMP
E、IGMP

网际层(Internet Layer)是 TCP/IP 模型中的一层,主要负责数据的路由和转发。以下是在网际层中常见的协议:

A、IP(Internet Protocol):IP 是网际层中最重要的协议,负责在不同的网络之间传递数据包。

B、ARP(Address Resolution Protocol):ARP 位于链路层,用于将 IP 地址解析为物理 MAC 地址。

D、ICMP(Internet Control Message Protocol):ICMP 是用于传递网络错误和控制消息的协议,常用于网络诊断和错误报告。

E、IGMP(Internet Group Management Protocol):IGMP 用于在多播通信中管理主机和路由器之间的组成员关系。

C、MAC(Media Access Control):MAC 地址是数据链路层中设备的唯一标识,但不是网际层协议,而是更低层次的协议。

因此,不属于网际层协议的是 C、MAC。

4. TCP和 UDP的优缺点

TCP(传输控制协议)和 UDP(用户数据报协议)是两种常用的传输层协议,它们在不同的应用场景中具有各自的优点和缺点:

TCP 的优点:

  1. 可靠性: TCP 提供可靠的数据传输机制,通过序号、确认和重传等机制,确保数据在传输过程中不丢失、不重复和按序传递。
  2. 流量控制: TCP 使用滑动窗口机制来控制发送方的数据发送速率,防止接收方过载。这有助于防止网络拥塞。
  3. 错误校正: TCP 通过校验和和确认机制来检测和纠正传输中的数据错误,确保数据的完整性。
  4. 顺序性: TCP 保证数据按照发送的顺序传递给接收方,不会发生乱序。
  5. 适用于长连接: TCP 支持建立长时间的连接,适用于应用需要保持持久连接的场景,如网页浏览、文件下载等。

TCP 的缺点:

  1. 较高的开销: TCP 需要维护连接状态、确认机制等,因此相对于 UDP 来说,具有较高的开销。
  2. 较慢的速度: 由于 TCP 需要进行确认和重传等操作,使得其相对于 UDP 来说传输速度较慢。
  3. 不适用于实时性要求高的应用: 由于 TCP 的确认和重传机制,它可能引入一定的延迟,不适合实时性要求较高的应用,如实时音视频通信。

UDP 的优点:

  1. 低开销: UDP 相对于 TCP 来说,没有确认和重传机制,因此开销较低,适用于网络资源有限的情况。
  2. 快速传输: 由于没有确认和重传等机制,UDP 传输速度较快,适合实时性要求高的应用。
  3. 适用于实时通信: UDP 适用于实时音视频通信、在线游戏等需要快速传输的应用场景。

UDP 的缺点:

  1. 不可靠性: UDP 不提供可靠的数据传输机制,传输过程中数据可能丢失、重复或乱序。
  2. 不支持流量控制: UDP 不支持像 TCP 那样的流量控制机制,可能导致网络拥塞。
  3. 不适用于长连接: UDP 不支持长时间的连接,每次传输都是独立的,适用于短暂通信。

在选择使用 TCP 还是 UDP 时,需要根据应用的具体需求和场景来判断,权衡其可靠性、速度、实时性等因素。

5. TCP/IP 建立连接的过程?

答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
  第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK (ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

6.软链接和硬链接区别

为了解决文件共享问题,Linux引入了软链接和硬链接。除了为Linux解决文件共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。若1个inode号对应多个文件名,则为硬链接,即硬链接就是同一个文件使用了不同的别名,使用ln创建。若文件用户数据块中存放的内容是另一个文件的路径名指向,则该文件是软连接。软连接是一个普通文件,有自己独立的inode,但是其数据块内容比较特殊。

总结:

1、软链接以路径的形式存在,硬链接以文件副本的形式存在;

2、软链接可以跨文件系统,硬链接不可以;

3、软链接可以对目录进行链接,硬链接不可以。

7.HTTP和HTTPS的区别,以及HTTPS有什么缺点?

HTTP协议和HTTPS协议区别如下:

1)HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输的数据则是经过TLS加密后的,HTTPS具有更高的安全性

2)HTTPS在TCP三次握手阶段之后,还需要进行SSL 的handshake,协商加密使用的对称加密密钥

3)HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书

4)HTTP协议端口是80,HTTPS协议端口是443

HTTPS优点:

HTTPS传输数据过程中使用密钥进行加密,所以安全性更高

HTTPS协议可以认证用户和服务器,确保数据发送到正确的用户和服务器

HTTPS缺点:

HTTPS握手阶段延时较高:由于在进行HTTP会话之前还需要进行SSL握手,因此HTTPS协议握手阶段延时增加

HTTPS部署成本高:一方面HTTPS协议需要使用证书来验证自身的安全性,所以需要购买CA证书;另一方面由于采用HTTPS协议需要进行加解密的计算,占用CPU资源较多,需要的服务器配置或数目高

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值