(嵌入式面经)第11章 20+公司面经杂谈(六):字节跳动、小米、算能科技、格力、恒玄科技
本篇涉及的所有问题概要:大家可以试试在看参考答案前,提前尝试解答,以便明确自身知识点的不足部分!
1.串口没有时钟线,是怎么工作的,而为什么SPI需要时钟线?
2.有没有用过printf打印这个函数,它的底层是如何实现的?
3.如何保证线程间的正确性?
4.Linux中字符设备与块设备的主要区别是什么?
5.NAND Flash 与 NOR Flash 的区别是什么?
6.主设备号和次设备号的用途是什么?
7.串口波特率计算,115200下,数据吞吐量一般为多少(1S传输多少字节的数据)?
8.在程序中你是如何接收并解析大数据包(例如1K字节以上)的?
9.在 FreeRTOS 中,哪些事件会触发任务调度?
10.专栏订阅奖励(支持模仿)——个人创新点问答:在你的FreeRTOS PLUS中是如何实现支持不同场景(工作(空闲任务)、空闲(定时唤醒))下自适应低功耗休眠策略的?
---------------------------------------------------------------------------------------------------
1.串口没有时钟线,是怎么工作的,而为什么SPI需要时钟线?
在串口通信中,虽然没有专用的时钟线,但通信仍然能够正常进行。
其原因是串口(通常是UART,即通用异步收发传输器)采用的是 异步通信 模式。以下是具体的工作原理和实现方式:
1. 异步通信的工作原理
在异步串口通信中,数据的传输依赖于双方事先约定好的波特率(Baud Rate)。
波特率是通信中数据传输的速率,它决定了每秒钟传输多少位数据。
通过设定相同的波特率,发送方和接收方就能在同样的时间间隔内收发数据。
2. 如何保证时序同步?
在没有时钟线的情况下,通信双方是如何保持时序同步的呢?
数据格式:
- 在串口通信中,数据是通过一定格式进行传输的。
- 每个数据单元通常由一个 起始位、数据位、可选的 校验位 和 停止位 组成。
- 起始位用来标识数据传输的开始,而停止位用来标识数据的结束。
- 这使得接收方能够准确地知道何时开始和结束读取数据。
波特率:
- 发送方和接收方都使用相同的波特率,这意味着它们在同一时间间隔内发送和接收相同数量的位(即每秒传输的比特数)。
- 因此,只要双方在通信开始前设定了相同的波特率,就能保证数据能够同步传输。
- 即便没有专用的时钟信号,接收方也能够根据这些预先设定好的时间间隔正确地解码收到的数据。
时钟恢复:
- 接收方通过 采样 发送的数据,通常会根据一个固定的采样频率(即波特率)来确定数据的比特值。
- 虽然没有时钟线,但接收方会使用自己的本地时钟来从收到的信号中恢复数据位的信息。
- 接收方将数据流中的每一位数据分为若干小段,按时钟频率的时间段进行采样,确保准确地读取数据位。
3. 为什么 SPI 必须加时钟线?
SPI(串行外设接口)与串口(UART)不同,它使用 同步通信,这意味着它需要时钟线。
SPI 需要时钟线来确保发送和接收设备能够同时在相同的时刻同步交换数据。
相比之下,串口通信通过波特率来同步数据,因此不需要时钟线。
4.代码示例:使用UART进行串行通信
以下是一个简单的使用UART进行串行通信的C语言代码示例:
初始化UART:UART_Init
函数初始化UART,并设置波特率。在这个例子中,波特率被设置为9600。
发送数据:通过UART_Transmit
函数将字符发送到串口。该函数会等待直到发送缓冲区为空,然后发送数据。
接收数据:通过UART_Receive
函数接收一个字符。此函数会阻塞,直到接收到数据。
输出示例:假设程序发送字符 'A',接收并打印出来,输出是 :Received data: A 。
总结
2.有没有用过printf打印这个函数,它的底层是如何实现的?
1. printf 函数介绍
printf
是 C 语言中的标准库函数,主要用于将格式化数据输出到标准输出(通常是屏幕)。
它的底层实现非常复杂,因为它需要处理各种格式化操作,并将数据最终输出。
2. printf 的底层实现步骤
printf
的工作流程大致可以分为以下几个步骤:
解析格式字符串:
printf
需要解析传入的格式字符串,识别其中的格式化占位符(如%d
、%s
等),并处理它们。
参数提取和类型转换:
printf
使用va_list
(变长参数)来访问参数列表。- 通过
va_start
和va_arg
宏,printf
按照格式字符串的要求提取不同类型的参数。
格式化输出:
- 根据格式标识符(如
%d
、%f
等),printf
将数据转换成适当的格式。 - 例如,整数需要转化为字符串,浮点数需要根据精度要求进行格式化。
缓冲区管理:
- 格式化后的数据被存入内部缓冲区。为了优化效率,数据在缓冲区中暂存,直到缓冲区满或者遇到换行符时才会被刷新,写入到标准输出。
最终输出:
write
系统调用或类似机制输出到标准输出设备(通常是终端或屏幕)。printf
可能通过串口或者其他通信接口输出数据。3. 为什么 printf 实现复杂?
printf
需要处理不同类型的格式(整数、浮点数、字符串等)并进行各种格式化操作。它还需要支持变长参数,处理数据时需要考虑类型安全和对齐问题。通过使用 va_list
进行变长参数处理,printf
能够支持多种数据类型。
4. 简化版 printf
示例
下面是一个简化版的 printf
实现,使用 va_list
来处理变长参数:
va_list args;
:声明一个 va_list
类型的变量,用于保存变长参数列表。va_start(args, format);
:初始化 va_list
,使其指向格式化字符串之后的第一个变长参数。va_arg(args, type);
:从 args
中提取下一个参数,并根据指定的类型进行转换(如 int
、char*
等)。putchar(*ptr);
:当当前字符不是格式标识符(如 %d
或 %s
)时,直接输出普通字符。va_end(args);
:结束变长参数的处理,释放资源。总结
printf
,因为它的底层实现可能涉及较多的内存操作和格式化计算。printf
可能会导致较大的内存开销,可以使用更轻量的输出方法。3.如何保证线程间的正确性?
在多线程编程中,保证线程的正确性是非常重要的,因为多个线程共享资源,
必须采取适当的同步方法来避免数据竞争、死锁等问题。
介绍几种常见的保证线程正确性的方法,并通过代码示例进行说明。
1. 使用互斥锁(Mutex)
互斥锁是一种常见的同步机制,它用于保护共享资源,确保同一时刻只有一个线程能够访问资源,
从而避免多个线程同时访问共享资源时产生的竞态条件。
shared_resource
,确保同一时刻只有一个线程可以修改它。2. 使用读写锁(Read-Write Lock)
读写锁允许多个线程同时读取共享数据,但只有一个线程能够写共享数据,适合于读多写少的场景。
3. 使用条件变量(Condition Variables)
条件变量允许线程等待某个条件成立,并且在该条件满足时通知其他线程。常与互斥锁一起使用。
4. 使用原子操作(Atomic Operations)
原子操作保证某些操作在多线程环境中不可分割,避免了锁的使用,通常用于简单的计数或标志位操作。
- atomic_fetch_add_explicit 是一个原子操作,用于原子地增加
counter
的值。
5. 避免死锁
死锁发生在多个线程之间,互相等待对方释放锁的情况,导致所有线程无法继续执行。为避免死锁,可以:
- 避免锁嵌套(避免在持有一个锁时请求其他锁)
- 使用锁顺序(确保按固定顺序请求锁)
- 使用超时机制(避免无限等待)
总结
为了保证线程的正确性,需要使用同步机制(如互斥锁、读写锁、条件变量等)来保护共享资源,防止数据竞争。
还可以通过原子操作和线程安全的数据结构来简化编程。有效的线程管理能够提高系统的稳定性和性能。
4.Linux中字符设备与块设备的主要区别是什么?
在 Linux 操作系统中,字符设备和块设备是两种非常常见的硬件设备类型。
它们在数据传输方式、访问方式和设备驱动管理方面有明显的不同。
1. 字符设备与块设备的主要区别
数据传输方式
访问方式
- 字符设备:采用顺序访问方式,数据逐字节读取或写入。字符设备不支持在设备的任意位置进行随机访问,所有的读写操作都是按顺序进行的。
- 块设备:支持随机访问,可以在设备的任何位置读取或写入数据。块设备的驱动程序会对数据块进行优化,例如调度算法、重排序等,以提升访问性能。
设备驱动与管理
- 字符设备:驱动程序较为简单,主要负责字符的读写和控制操作。内核通过字符设备文件来管理字符设备。
- 块设备:驱动程序较为复杂,需要处理缓存管理、数据块调度、错误恢复等功能。块设备的管理通常涉及更复杂的 I/O 调度器和缓存机制。
2. 字符设备与块设备对比
3. 代码示例
字符设备模拟
字符设备通过字节流方式处理数据。下面代码示例模拟了字符设备的读写操作:
- 该代码模拟字符设备的行为,首先向文件
char_device.txt
写入字母 A 到 Z,然后读取并按字节输出这些字符。
块设备模拟
块设备通过固定大小的数据块(如 512 字节)进行读写。以下代码示例,模拟了一个块设备的读写操作。
- 该代码模拟块设备的操作,通过将数据分为多个 512 字节的数据块进行写入,并读取第一个数据块并输出其前 20 字节。
总结
- 字符设备:数据传输按字节流处理,实时且顺序读写,适用于如键盘、鼠标等设备,驱动程序较为简单。
- 块设备:数据传输按固定大小的数据块进行,支持随机访问,适用于硬盘、U盘等存储设备,驱动程序较为复杂,涉及缓存和调度优化。
5.NAND Flash 与 NOR Flash 的区
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。