嵌入式笔试面试刷题(day17)


前言

本篇文章接着为大家讲解嵌入式的笔试和面试专题。

一、单片机中断处理的流程是什么?

单片机的中断处理是一种用于提高系统响应速度和效率的重要机制。中断处理流程大致可以分为以下几个步骤:

  1. 中断发生
    当外部事件或内部事件触发中断源时,中断信号会被送到中断控制器。常见的中断源包括定时器溢出、中断引脚信号变化、串口接收完成等。

  2. 中断请求
    中断控制器检测到中断信号后,向CPU发出中断请求。如果CPU当前允许中断(即全局中断使能位已设置),CPU会在当前指令执行完毕后响应中断请求。

  3. 中断响应
    CPU完成当前指令后,执行以下操作:

保存现场:保存当前的程序计数器(PC)和其他重要寄存器的内容,以便在中断处理完成后恢复。
关闭全局中断:在处理中断过程中,为了防止嵌套中断,通常会临时关闭全局中断(具体实现可能因单片机而异)。
跳转到中断服务程序(ISR):CPU从中断向量表中读取对应中断的入口地址,并跳转到该地址开始执行中断服务程序。

  1. 中断服务程序(ISR)
    ISR是专门用于处理特定中断事件的代码,其主要步骤包括:

保存上下文:保存使用的寄存器状态(这一步有时在ISR开始时自动完成)。
处理中断:执行中断处理逻辑,例如读取传感器数据、处理通讯数据、控制外设等。
清除中断标志:在ISR结束前清除相应的中断标志位,防止同一中断反复触发。
恢复上下文:恢复寄存器状态,使程序可以无缝回到中断前的状态。

  1. 中断返回
    ISR完成后,执行中断返回指令(如RETI或IRET),CPU进行以下操作:

恢复现场:恢复中断前保存的程序计数器和其他寄存器内容。
重新使能中断:如果之前关闭了全局中断,在此步骤中重新使能。
继续执行主程序:CPU返回到中断发生时的程序继续执行。

二、进程间通信中使用锁和同步的目的是什么?

  1. 确保数据一致性
    在多进程或多线程环境中,不同进程或线程可能会同时访问和修改共享资源(如共享内存、文件等)。如果没有同步机制,这些操作可能会相互干扰,导致数据的不一致或损坏。

  2. 防止竞态条件
    竞态条件指的是多个进程或线程在没有适当同步的情况下竞争使用共享资源,从而导致不可预测的结果。例如,两个进程同时读取和修改一个共享变量,可能会导致最终的结果不正确。使用锁可以确保在同一时间只有一个进程或线程可以访问共享资源,从而避免竞态条件。

  3. 实现互斥(Mutex)
    互斥锁(Mutex)是一种常见的同步机制,它确保在同一时间只有一个进程或线程能够进入临界区(Critical Section)。通过使用互斥锁,可以保护共享资源不被多个进程或线程同时访问。

  4. 控制同步顺序
    有些时候,进程或线程之间需要按照一定的顺序执行操作。同步机制(如信号量、条件变量等)可以帮助协调这些操作顺序,确保按预期进行。

三、define和const在内存占用上的差异是什么?

#defineconst在内存占用上的差异可以从编译时处理和运行时行为两个方面进行分析。这两者在C/C++编程中都用于定义常量,但它们的机制和在内存中的表现有所不同。

#define

#define是一个预处理指令,用于定义符号常量。在编译前,预处理器会将所有的#define符号替换为其定义的值。#define在预处理阶段处理,不会分配内存空间。

示例:

#define PI 3.14

在编译时,所有的PI都会被替换为3.14

  • 内存占用#define不会分配任何内存。它只是文本替换,因此不会占用运行时内存。但如果在多个地方使用这个常量,每个地方都会生成一份拷贝,这可能会增加代码大小。

const

const关键字用于定义一个常量变量。它是类型安全的,编译器会为它分配内存,并且该变量在运行时存在。

示例:

const double pi = 3.14;

在编译时,pi被分配一个内存位置,并在运行时使用这个内存位置的值。

  • 内存占用const变量会占用内存空间,因为它需要存储变量的值。编译器会在合适的内存区域(通常是数据段)为其分配空间。

比较

  1. 内存分配

    • #define:不分配内存,只进行文本替换,不会增加运行时的内存占用。
    • const:分配内存来存储常量值,运行时占用内存。
  2. 类型安全

    • #define:没有类型检查,只是简单的文本替换,可能导致类型错误。
    • const:有类型检查,类型安全,避免类型错误。
  3. 调试

    • #define:在调试时,预处理器替换后难以直接看到原始定义。
    • const:在调试时可以直接看到并检查常量变量的值。
  4. 作用域

    • #define:宏的作用域从定义点开始,到文件结束,或者通过#undef取消定义。
    • const:具有正常的变量作用域,可以是局部的、全局的或是类成员。

例子对比

考虑以下代码:

使用#define

#define SIZE 100
int arr[SIZE];

使用const

const int size = 100;
int arr[size];
  • 在第一个例子中,SIZE在预处理阶段会被替换为100,不会占用运行时内存。
  • 在第二个例子中,size会在数据段分配内存,并且在运行时占用内存。

总结来说,#define适用于简单的符号替换和宏定义,而const适用于需要类型安全和运行时可见的常量。#define不会占用运行时内存,而const会占用运行时内存,但带来了更多的类型安全和调试便利。

四、波特率是什么,为什么双方波特率要相同,高低波特率有什么区别

波特率(Baud Rate)是指每秒钟传输的符号(信号变化)的次数,用于衡量数据传输速率。它通常以每秒波特(Baud)为单位表示。波特率在串行通信中尤为重要,例如在串口通信(如UART)中。

波特率的定义
波特率(Baud Rate)是每秒传输的信号变化(或符号)数。例如,如果波特率是9600 Baud,则每秒钟有9600个信号变化。需要注意的是,波特率不一定等同于比特率(Bit Rate)。比特率是每秒传输的位数,而波特率是每秒传输的符号数。一个符号可以表示多于一位数据(如某些调制方式中)。

双方波特率必须相同的原因
在串行通信中,发送端和接收端必须以相同的波特率工作,这是因为:

同步性:波特率决定了发送和接收数据的速度。如果两端的波特率不同,接收端将无法正确解析发送端的数据,因为数据的起止点无法对齐。
数据完整性:不同波特率会导致数据位的错误解读,从而产生数据错误或丢失。
高低波特率的区别
传输速度:

高波特率:每秒传输更多的数据,因此可以提高通信速度。例如,115200 Baud 比 9600 Baud 传输数据更快。
低波特率:每秒传输的数据较少,通信速度较慢。
误码率:

高波特率:由于数据传输速度快,对传输介质的要求更高,更容易受到噪声和干扰的影响,从而可能增加误码率。
低波特率:传输速度慢,通常更稳定,误码率较低,对传输介质的要求较低。
信号质量:

高波特率:对于长距离传输,高波特率信号衰减和失真较严重,可能需要更好的传输介质或补偿措施(如中继器、调制解调器)。
低波特率:长距离传输时信号衰减和失真较小,适合质量较差的传输介质。
系统资源占用:

高波特率:需要更高的处理能力和更快速的硬件支持,可能会增加系统资源的占用。
低波特率:对硬件和处理能力的要求较低,系统资源占用较少。
选择波特率的考虑因素
应用需求:根据应用对数据传输速率的要求选择适当的波特率。
传输距离:长距离传输通常选择较低的波特率以确保信号质量。
传输介质:考虑传输介质的质量和抗干扰能力。
硬件性能:确保硬件能够支持所选择的波特率。

五、cache的作用

缓存(Cache)的主要作用是加速数据访问和提高系统性能。它是一种高效的临时存储机制,用于存储经常访问的数据,以便更快地响应数据请求。缓存广泛应用于计算机系统、网络、数据库和浏览器等各个领域。

缓存的作用

提高数据访问速度:

减少延迟:缓存存储经常访问的数据,减少从主存或远程服务器获取数据的时间,显著降低数据访问的延迟。
加快响应时间:对相同数据的多次请求可以直接从缓存中读取,从而提供更快的响应。
减少系统负载:

降低I/O操作:缓存减少了对磁盘、数据库或网络的频繁访问,降低了这些资源的负载。
分担服务器压力:在Web应用中,缓存可以缓解服务器的压力,减少对后端服务器的请求次数。

节省带宽:

减少网络传输:缓存存储静态资源(如网页、图像、视频等),减少了对这些资源的重复下载,节省了网络带宽。
优化流量:通过缓存中继节点(如CDN),将数据存储在离用户更近的地方,减少跨区域的数据传输,提高传输效率。

提高系统性能:

加速计算:缓存存储中间计算结果,避免重复计算,提高计算任务的效率。
平衡负载:通过分布式缓存系统,将数据分布存储在多个节点,平衡负载,提高系统的可扩展性和可靠性。

六、实时查看线程进程状态的linux命令

在Linux中,有多个命令可以用来实时查看线程和进程的状态。以下是一些常用的命令和它们的简要说明:

1. top

top命令是一个实时显示系统任务的命令,它默认显示进程信息。使用-H选项可以显示线程信息。

top -H

top命令的输出中,你可以看到每个线程的ID、CPU使用率、内存使用情况等信息。

2. htop

htoptop命令的增强版本,提供了更友好的用户界面和更多的功能。在htop中,你可以按 F2 进入设置,然后启用显示线程。

htop

按下F2 (或S),然后选择“Display options”,启用“Show custom thread names”。

3. ps

ps命令可以显示当前系统的进程和线程信息。使用-T选项可以查看某个特定进程的线程信息。

ps -eLf

或者查看某个特定进程的线程:

ps -T -p <PID>

4. pidstat

pidstat命令是sysstat包的一部分,用于收集每个进程的统计信息。使用-t选项可以显示线程信息。

pidstat -t

5. perf top

perf top命令是一个强大的性能监控工具,可以用来查看系统的实时性能数据,包括线程信息。

perf top

6. watch

watch命令可以用来定期执行指定命令,从而达到实时监控的效果。例如,使用watch结合ps命令来实时查看线程信息:

watch -n 1 "ps -eLf"

-n 1表示每秒更新一次。

7. pstree

pstree命令以树形结构显示进程和线程的关系,使用-p选项显示进程ID,使用-T选项显示线程。

pstree -p -T

8. strace

strace命令可以跟踪系统调用和信号,可以用于诊断和调试程序,查看进程和线程的运行状态。

strace -f -p <PID>

以上命令提供了多种方式来实时查看和监控Linux系统中的线程和进程状态,根据需要选择合适的工具即可。

七、结构体和联合体的区别以及内存对齐方式,如何取消对齐方式

结构体和联合体的区别

1. 结构体(struct)
  • 定义:结构体是一种用户定义的数据类型,它可以包含多个不同类型的数据成员。
  • 内存布局:结构体的每个成员都有自己的内存空间,成员的内存地址是连续的,并且成员按照定义的顺序存储。
  • 访问:结构体的所有成员可以同时存在,访问某个成员不会影响其他成员。
  • 示例
    struct ExampleStruct {
        int a;
        float b;
        char c;
    };
    
2. 联合体(union)
  • 定义:联合体是一种用户定义的数据类型,它可以包含多个不同类型的数据成员,但所有成员共享同一块内存空间。
  • 内存布局:联合体的所有成员占用同一块内存,大小等于其最大成员的大小。
  • 访问:在任何时刻,联合体中只能有一个成员是有效的,访问某个成员会覆盖其他成员。
  • 示例
    union ExampleUnion {
        int a;
        float b;
        char c;
    };
    

内存对齐方式

内存对齐是指数据在内存中按一定的规则对齐存储,以提高访问速度。内存对齐方式通常由编译器决定,但可以通过编译指令或属性进行控制。

对齐原则
  1. 对齐边界:每个数据成员的地址必须是其类型大小的倍数。例如,4字节的int类型必须对齐到4的倍数的地址。
  2. 结构体对齐:结构体的大小必须是其最大成员对齐大小的倍数。
  3. 填充字节:为了满足对齐要求,编译器会在成员之间插入填充字节。
示例
struct ExampleStruct {
    char c;
    int i;
};

在32位系统上,char的对齐要求是1字节,int的对齐要求是4字节。为了对齐,编译器会在ci之间插入3个填充字节。

取消对齐方式

在某些情况下,可能需要取消对齐以节省内存。可以通过编译器指令来实现取消对齐。

使用#pragma pack

#pragma pack指令可以设置和恢复对齐方式。

#pragma pack(push, 1)
struct ExampleStruct {
    char c;
    int i;
};
#pragma pack(pop)

上述代码将结构体ExampleStruct的对齐方式设置为1字节对齐,然后恢复原有对齐方式。

使用__attribute__((packed))

在GNU编译器中,可以使用__attribute__((packed))来取消对齐。

struct ExampleStruct {
    char c;
    int i;
} __attribute__((packed));

上述代码将结构体ExampleStruct设为无对齐。

注意事项

取消对齐可能会导致性能下降,因为处理器对未对齐的数据访问速度较慢。此外,某些硬件可能不支持未对齐的数据访问,可能会引发异常。因此,在取消对齐时需谨慎。

总结

  • 结构体:每个成员都有独立的内存空间,可以同时存在,访问顺序按定义顺序。
  • 联合体:所有成员共享同一内存空间,只能有一个成员有效,覆盖其他成员。
  • 内存对齐:通过对齐边界和填充字节提高访问速度。
  • 取消对齐:通过#pragma pack__attribute__((packed))指令实现,但需考虑性能和兼容性。

总结

本篇文章就讲解到这里了,我们下篇文章再见。

相关推荐

  1. 嵌入笔试面试(day17)

    2024-06-07 08:18:08       19 阅读
  2. 嵌入面试

    2024-06-07 08:18:08       58 阅读
  3. 嵌入学习 Day17

    2024-06-07 08:18:08       49 阅读
  4. 常见Linux嵌入C语言笔试面试

    2024-06-07 08:18:08       31 阅读
  5. 嵌入一些面试

    2024-06-07 08:18:08       22 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-07 08:18:08       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-07 08:18:08       101 阅读
  3. 在Django里面运行非项目文件

    2024-06-07 08:18:08       82 阅读
  4. Python语言-面向对象

    2024-06-07 08:18:08       91 阅读

热门阅读

  1. PHP Standards Recommendations(PSR)

    2024-06-07 08:18:08       30 阅读
  2. leetcode 2938.区分白球与黑球

    2024-06-07 08:18:08       29 阅读
  3. 【随手记】maplotlib.use函数设置图像的呈现方式

    2024-06-07 08:18:08       32 阅读
  4. 基于springboot的公交线路查询系统源码数据库

    2024-06-07 08:18:08       23 阅读
  5. 力扣算法题:跳跃游戏 -- 多语言实现

    2024-06-07 08:18:08       26 阅读
  6. 大数据技术Hbase列数据库——topic2

    2024-06-07 08:18:08       31 阅读
  7. 弹球大挑战:Python与Pygame的互动游戏教程

    2024-06-07 08:18:08       33 阅读
  8. 跟着GPT学设计模式之观察者模式

    2024-06-07 08:18:08       27 阅读