1.关于atomic和mutex的区别

2.项目描述

3.自我介绍
您好,我本科专业是环境科学,但因为自己比较喜欢编程,所以从大学期间开始系统学习 C++。目前主要掌握 C++、数据结构、Linux、多线程以及 Qt 开发。C++方面学习过面向对象、STL、内存管理等基础知识,也通过技术博客和项目进行巩固。Linux方面主要学习了常用命令、进程线程以及多线程同步。
项目方面,我做过一个基于 Qt 的音乐播放器,以及一个仿 muduo 的高并发服务器,对 Qt 开发和 Linux 网络编程都有一定实践。实习期间主要负责 Qt 上位机开发,其中比较核心的是 FPGA IQ 数据采集链路,包括 UDP 通信、协议封装、数据包重排、IQ 数据处理、实时显示和数据落盘。目前我希望找 C++/Qt、Linux C++ 或网络编程相关的开发岗位。
4.你为什么专业是环境科学,却来做 C++?
我本科虽然是环境科学,但我在大学期间逐渐发现自己对编程和计算机系统更感兴趣,所以从 C++ 开始自学。最开始主要学习数据结构和 C++ 基础,之后为了真正做项目又继续学习 Linux、网络编程、多线程和 Qt。
我不是只停留在课程学习,而是通过音乐播放器、服务器以及实习项目不断验证自己是否适合这个方向。到实习以后,我实际参与了 FPGA IQ 数据采集和 Qt 上位机开发,也进一步确定自己希望以后从事 C++ 开发。
5.你 Linux 学到了什么?
Linux 方面我主要掌握常用命令和基本开发环境使用,比如文件、进程、网络相关操作;编程方面学习过 Linux 多线程,包括线程创建、线程同步、互斥以及线程间通信。另外我做过基于 Linux 的高并发服务器项目,所以对 Socket、TCP/UDP、IO 多路复用这些网络编程内容也有实际接触。
6.你实习主要做什么?
我实习主要负责频谱监测设备的 Qt 上位机开发,其中我负责的核心模块是 IQ 数据采集链路。具体来说,上位机需要接收 FPGA 通过 UDP 发送过来的 IQ 数据,我负责了应用层通信协议的封装和对接,之后实现 UDP 数据接收、数据包重排、IQ 帧重组、数据处理和 Qt Charts 实时显示,同时还负责原始 IQ 数据的文件落盘。
7.你说协议封装,具体怎么做的?
协议主要分成控制命令和数据包两部分。控制方向由上位机根据采集参数组装命令发送给 FPGA;数据方向由 FPGA 按照约定的数据结构封装 IQ 数据。数据包中包含当前包的 packet_id、这一帧的 total_packets、当前有效数据长度 packet_length 以及 IQ 数据 payload。
我们和硬件工程师确认这些字段的定义后,上位机根据 packet_id 把收到的数据放到对应的缓存位置,最后根据 total_packets 判断一帧是否接收完整。
8.UDP 为什么会乱序?
因为 UDP 本身不提供可靠、有序的传输保证,所以发送端连续发送多个数据报之后,接收端不能假设它们一定按照发送顺序到达。因此我们在应用层协议中增加了 packet_id,接收端根据这个序号把数据包放回对应位置,从而完成乱序重排。
9.你们 UDP 有没有考虑丢包?
我们在应用层通过 total_packets、packet_id 和 packet_length 对 IQ 数据进行分包和组帧。UDP 本身不保证可靠传输,所以理论上存在丢包问题。对于实时频谱监测场景,如果为了一个丢失的数据包阻塞等待重传,会增加处理延迟,因此是否重传需要根据应用场景权衡。如果是实时监测,可以选择丢弃当前不完整帧继续处理下一帧;如果是对原始 IQ 数据完整性要求较高的采集场景,则可以结合 FPGA 侧缓存设计 ACK/NACK 和重传机制。
10.你介绍一下你在这个项目中主要负责什么?整个数据链路是怎么样的?
我主要负责 IQ 数据接收和后处理这一部分。FPGA 将一帧原始 IQ 数据分成多个 UDP 数据包发送到上位机,我负责接收并解析数据,根据包序号、总包数和包长度等信息对数据进行重排和组帧,放入应用层缓冲区。之后根据配置的抽取参数对 IQ 数据进行处理,再通过 Qt 的跨线程消息机制将处理后的数据投递到 UI 主线程,使用 Qt Charts 绘制实时 IQ 波形。同时将原始 IQ 数据保存为 BIN 文件,便于后续分析和数据回放。
11.如何判断缺包、如何决定等待还是放弃
一帧为单位,用包序号判断是否收齐0到N-1号包;在一个很短的超时窗口内等待剩余包,超时仍缺则记为不完整帧。不阻碍实时显示,并记录完整率作为质量指标。这个做法和实时业务目标一致,在取舍上更偏向可用性而非传输完美无缺。
12.颜色地址
Pastel Color Tones Color Scheme - Palettes - SchemeColor.com
13.讲一讲什么是信号和槽机制
信号槽是 Qt 元对象系统提供的对象间通信机制,用来解耦。 moc 在编译预处理阶段扫描头文件,生成元对象相关代码。调用 connect 的时候,会把槽函数注册到这个信号对应的回调链表里面,不是全局哈希表。 emit 发射信号的时候,会遍历该信号绑定的所有槽函数,传递参数并执行。 信号是事件发生时对外发出的通知,没有函数体,但可以携带参数,必须写在 signals 下,通过 emit 触发。 槽本质就是成员函数。Qt5 新的函数指针 connect 语法,普通成员函数不需要写 slots 关键字也能作为槽;槽的作用就是收到信号后执行业务逻辑。
14.请问槽函数的执行是同步的还是异步的?

15.你们的设备的协议是如何规定的?
我们项目没有直接使用 HTTP,而是基于 UDP/TCP 之上的自定义二进制应用层协议。代码中的这些 struct 相当于协议报文的数据结构定义,程序根据结构体填充同步字、长度、请求 ID、命令字以及具体参数,然后经过协议封装/序列化,通过 Socket 发送给设备,设备按照相同的字段约定进行解析。
16.你在简历中写的数据完整率是什么意思?你怎么证明是你的分包方案把 90% 提升到了 95%?
原来的数据传输以一帧 IQ 数据为单位,一帧大约 4096 Byte,数据量较大。后来我在协议层增加了帧 ID、包序号、帧长度等字段,将一帧数据拆分成多个较小的数据包进行传输。接收端根据帧 ID 和包序号进行重组,并以完整帧作为统计单位计算数据完整率,使完整率从原来的约 90% 提升到了 95%左右。
我通过实际运行测试,对修改前后的数据完整率进行了对比。在相同测试条件和测试时长下,修改前完整率大约 90%,增加协议字段并进行分包重组后,完整率提升到约 95%。这里的完整率是按照完整 IQ 帧数量与理论应接收帧数量的比例进行统计。
项目中原先通过 senddatastruct.h 和 receivedatastruct.h 分别定义上下位机之间的控制命令和接收数据结构;后续通过 NewProtocol.h 重新定义了一套统一的协议报文,其中 Packet_Command 负责参数配置,Packet_Data 负责分包数据传输。
相比旧协议,新协议最大的变化是协议结构更加统一。旧协议主要按照发送命令和接收数据分别定义结构,新协议则把命令和数据统一封装成标准数据包,并且对数据分包中的总包数、包序号、有效长度进行了明确描述,更方便上位机进行协议解析和IQ数据的组帧处理。
17.你的频谱上位机开发为什么要用dialog而不是widget


18.如何确定一包数据是否完整到达?


19.如何解决cpu问题?
我的实现中每 100ms 检查一次数据更新状态,如果允许绘制,就通过 QtConcurrent 异步执行 paint。paint 内部需要复制采集数据、进行数据抽取以及最大保持、最小保持、平均等轨迹处理,最后再更新 Qt Charts 曲线。因此数据量较大或者刷新频率较高时,CPU 开销主要来自数据处理和图表重绘。为了控制开销,我做了复用缓冲区、预分配以及超过 5000 点时进行 max/min 抽取等优化,同时通过原子标志避免 paint 重入。"