当前位置: 首页 > news >正文

MTK外包面经

交叉编译是什么?在嵌入式中使用交叉编译有什么好处?

交叉编译是一种在一个平台(主机平台)上编译出能在另一个不同架构或操作系统的平台(目标平台)上运行的程序的技术。这里的 “平台” 通常指硬件架构(如 x86、ARM、RISC-V)和操作系统(如 Linux、Windows、嵌入式实时系统)的组合。

举个例子:

  • 你在 x86 架构的 Linux 电脑(主机) 上编写代码,编译出能在 ARM 架构的嵌入式设备(目标平台,如树莓派、路由器) 上运行的程序,这就是交叉编译。
  • 反之,如果在目标平台上直接编译(比如在树莓派上编译自身运行的程序),则称为 “本地编译”。

好处:

嵌入式设备(如单片机、传感器、智能手表、路由器等)通常具有 CPU 性能弱、内存小、存储有限 的特点,根本无法运行复杂的编译工具链(如 GCC、Clang)可以通过交叉编译可以解决该问题

嵌入式开发中,目标设备可能种类繁多(如 ARM、RISC-V、MIPS 等不同架构),且调试接口各异(如 JTAG、UART):

  • 开发者无需在每个目标设备上配置编译环境,只需在主机上安装对应架构的交叉编译工具链(如arm-linux-gnueabihf-gcc),即可统一管理多平台开发。
  • 避免了在嵌入式设备上操作命令行、处理依赖冲突等繁琐工作,减少人为错误。

KMP模式匹配算法

KMP 模式匹配算法是一种高效的字符串匹配算法,由 Knuth、Morris 和 Pratt 三人共同提出,其核心思想是利用已匹配的信息避免不必要的字符比较,从而将时间复杂度从朴素算法的 O (n*m)(n 为主串长度,m 为模式串长度)优化到 O (n+m)。

一、问题背景:字符串匹配的痛点

字符串匹配的目标是:在主串(如"ABCABCDABABCDABCDABDE")中查找模式串(如"ABCDABD")的位置。朴素算法的问题在于:当匹配失败时,主串指针会回溯到起始位置的下一位,导致大量重复比较。例如:

  • 主串:A B C A B C D...
  • 模式串:A B C D...前 3 位匹配成功,第 4 位失败(主串是A,模式串是D)。朴素算法会让主串指针从第 4 位回溯到第 2 位,重新比较,效率低下。

KMP 的核心优化:不回溯主串,只移动模式串

KMP 的关键是通过模式串自身的结构,计算出一个 “部分匹配表”(Partial Match Table,简称 PMT,也叫前缀函数),利用它来确定匹配失败时模式串应该向右移动多少位,从而避免主串指针回溯。

二、部分匹配表(PMT)的定义

对于模式串的每个位置i(从 0 开始),PMT [i] 表示:模式串中前i+1个字符组成的子串中,最长的 “前缀” 与 “后缀” 相等的长度

  • 前缀:不包含最后一个字符的所有头部子串(如"ABCD"的前缀为"A""AB""ABC")。
  • 后缀:不包含第一个字符的所有尾部子串(如"ABCD"的后缀为"D""CD""BCD")。

示例:模式串"ABCDABD"的 PMT 表

模式串索引i0123456
模式串字符ABCDABD
PMT[i]0000120
  • i=0(子串"A"):无前缀 / 后缀,PMT [0]=0。
  • i=4(子串"ABCDA"):最长相等前缀"A"与后缀"A",长度 1,故 PMT [4]=1。
  • i=5(子串"ABCDAB"):最长相等前缀"AB"与后缀"AB",长度 2,故 PMT [5]=2。

三、KMP 算法的匹配过程

  1. 预处理:计算模式串的 PMT 表,得到一个与模式串等长的数组(通常将 PMT 优化为 “next 数组”,方便计算移动距离)。
  2. 匹配阶段
    • i指向主串当前位置,j指向模式串当前位置(初始均为 0)。
    • 若主串s[i] == 模式串p[j],则i++j++,继续匹配。
    • 若不相等:
      • j > 0,则j = next[j-1](根据 PMT 调整模式串位置,避免主串回溯)。
      • j == 0,则i++(主串后移一位,模式串从头开始)。
    • j == 模式串长度时,匹配成功,返回起始位置i - j

示例:主串"ABCABCDABABCDABCDABDE"匹配模式串"ABCDABD"

  • 当匹配到i=6j=6时,主串s[6] = 'C',模式串p[6] = 'D',不匹配。
  • 此时j=6,查 PMT [j-1] = PMT [5] = 2,故j = 2(模式串右移6-2=4位)。
  • 主串i不回溯,继续比较s[6]p[2]'C' == 'C'),后续匹配顺利完成。

可以去B站看看该算法的视频动画, 能加深理解

快排

快速排序(Quick Sort)是一种高效的排序算法,由计算机科学家 Tony Hoare 在 1960 年提出。它的核心思想是 “分而治之”,通过选择一个 “基准元素” 将数组分为两部分,使得左半部分的元素都小于等于基准,右半部分的元素都大于等于基准,然后递归地对两部分进行排序。

快排的核心步骤

  1. 选择基准(Pivot):从数组中挑选一个元素作为基准(通常选第一个、最后一个或中间元素,也可随机选择)。
  2. 分区(Partition):将数组重新排列,所有比基准小的元素移到基准左边,比基准大的元素移到基准右边(相等的元素可放任意一边)。此时基准的位置已确定(最终排序后的位置)。
  3. 递归排序:对基准左右两侧的子数组分别重复上述步骤,直到子数组长度为 0 或 1(天然有序)。

在视频流中AvPacket的定义和存取方式

在 FFmpeg 多媒体处理框架中,AVPacket是用于存储编码后的音视频数据包的核心结构体,是音视频流处理(如读取、编码、解码、封装、传输)的关键数据载体。它主要用于在不同模块(如解复用器、编码器、解码器、复用器)之间传递编码后的原始数据(如 H.264/H.265 视频帧、AAC 音频帧)。

一、AVPacket的定义与核心成员

AVPacket的定义位于 FFmpeg 的libavcodec/avpacket.h头文件中,其核心成员如下(简化版):

typedef struct AVPacket {uint8_t *data;          // 存储编码后的音视频数据(原始字节流)int size;               // data的大小(字节数)int64_t pts;            // 显示时间戳(Presentation Time Stamp,单位:流的时间基)int64_t dts;            // 解码时间戳(Decoding Time Stamp,单位:流的时间基)int stream_index;       // 该数据包所属的流索引(如视频流、音频流)int flags;              // 标志位(如AV_PKT_FLAG_KEY表示关键帧)AVPacketSideData *side_data; // 附加数据(如字幕、加密信息等)int side_data_elems;    // 附加数据的数量int64_t duration;       // 数据包的持续时间(单位:流的时间基)void *opaque;           // 自定义私有数据// ... 其他辅助成员
} AVPacket;

关键成员说明:

  • data 和 size:最核心的字段,存储编码后的原始数据(如视频的 NAL 单元、音频的帧数据)。
  • pts 和 dts:时间戳,用于同步音视频播放顺序(视频可能存在 B 帧,导致 DTS≠PTS)。
  • stream_index:标识该数据包属于哪个流(一个媒体文件可能包含多个流,如视频流、音频流、字幕流)。
  • flags:重要标志,例如 AV_PKT_FLAG_KEY 表示该数据包是关键帧(可独立解码),非关键帧(如 P 帧、B 帧)依赖其他帧。

二、AVPacket的生命周期与存取方式

AVPacket的使用需严格遵循 FFmpeg 的内存管理规则,避免内存泄漏或数据错误。核心操作包括初始化、填充数据、传递、释放等。

1. 初始化与分配

使用前需初始化AVPacket,通常有两种方式:

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>// 方式1:静态初始化(栈上分配)
AVPacket pkt;
av_init_packet(&pkt); // 初始化成员(data=NULL, size=0, 其他字段置默认值)// 方式2:动态分配(堆上分配,需手动释放)
AVPacket *pkt = av_packet_alloc(); // 分配并初始化,返回指针

2. 填充数据(获取数据包)

AVPacket的数据通常来自解复用器(demuxer) 或编码器(encoder)

  • 从媒体文件中读取(解复用):通过av_read_frame()AVFormatContext中读取数据包:

    AVFormatContext *fmt_ctx = avformat_alloc_context();
    avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL); // 打开媒体文件
    avformat_find_stream_info(fmt_ctx, NULL); // 获取流信息AVPacket *pkt = av_packet_alloc();
    while (av_read_frame(fmt_ctx, pkt) >= 0) { // 循环读取数据包// pkt->stream_index 标识当前包属于哪个流if (pkt->stream_index == video_stream_index) {// 处理视频包(如解码、存储)} else if (pkt->stream_index == audio_stream_index) {// 处理音频包}av_packet_unref(pkt); // 重置数据包(重要:避免内存泄漏)
    }
    
  • 从编码器输出(编码生成):编码器编码完成后,通过avcodec_receive_packet()获取编码后的AVPacket

    AVCodecContext *enc_ctx; // 已初始化的编码器上下文
    AVPacket *pkt = av_packet_alloc();
    // 向编码器发送原始帧(avcodec_send_frame())后,接收编码包
    while (avcodec_receive_packet(enc_ctx, pkt) == 0) {// 处理编码后的数据包(如写入文件、传输)av_packet_unref(pkt);
    }
    

3. 数据访问与修改

直接通过datasize访问编码数据,例如解析视频 NAL 单元:

// 假设pkt是一个H.264视频包,data中存储NAL单元
if (pkt->stream_index == video_stream_index && pkt->size > 0) {uint8_t *data = pkt->data;int size = pkt->size;// 遍历NAL单元(H.264 NAL以0x000001或0x00000001为起始码)// ...(解析逻辑)
}

修改数据时需注意内存管理,若需替换data,需先释放原有数据:

av_packet_unref(pkt); // 释放原有data
pkt->data = new_data; // 指向新数据
pkt->size = new_size; // 设置新大小

4. 释放与重置

AVPacket使用完后必须释放资源,核心函数:

  • av_packet_unref(pkt):重置数据包,释放dataside_data等动态分配的内存,但不释放pkt本身(适用于循环复用的情况)。
  • av_packet_free(&pkt):释放pkt指向的结构体及内部资源(适用于动态分配的AVPacket*)。
// 静态初始化的pkt:使用后重置
av_init_packet(&pkt); // 或 av_packet_unref(&pkt);// 动态分配的pkt:使用后释放
av_packet_free(&pkt); // pkt会被置为NULL

三、注意事项

  1. 引用计数AVPacket内部通过引用计数管理data的生命周期(类似AVFrame),av_packet_ref(pkt, src)可创建引用,av_packet_unref()减少引用计数,计数为 0 时释放内存。
  2. 时间基转换ptsdts的单位是所属流的时间基(AVStream->time_base),需转换为标准时间(如秒)时,需用av_q2d(stream->time_base) * pkt->pts
  3. 关键帧判断:通过pkt->flags & AV_PKT_FLAG_KEY判断是否为关键帧,对视频解码和 Seek 操作至关重要。
  4. 线程安全AVPacket本身不是线程安全的,多线程操作需加锁。

总结

AVPacket是 FFmpeg 中编码数据的 “容器”,承载了音视频流的核心信息(数据、时间戳、流索引等)。其存取方式需遵循 FFmpeg 的内存管理规则,核心流程为:初始化→填充数据(解复用 / 编码)→访问 / 处理→释放 / 重置。理解AVPacket的使用是掌握 FFmpeg 音视频处理的基础。

在实现视频回放功能时, 在拉流过程中应该边拉流边存储吗?

在实现回放功能时,“边拉流边存储” 是一种常见且合理的方案,但需根据具体场景(如实时性要求、存储资源、回放需求)灵活设计。以下从技术逻辑、优缺点和适用场景展开分析:

一、为什么需要 “边拉流边存储”?

回放功能的核心是 “能够重现历史流数据”,而流数据(如 RTSP/RTMP 直播流、传感器实时数据流)具有实时性、连续性和一次性的特点(不存储则会丢失)。因此,若需要回放,必须在拉流的同时对数据进行持久化存储,否则后续无法追溯历史内容。

二、边拉流边存储的实现逻辑

典型流程如下:

  1. 拉流模块:通过网络协议(如 RTSP、HTTP-FLV)从源端(摄像头、服务器)拉取实时流数据(通常是编码后的AVPacket或原始字节流)。
  2. 分流处理:拉取的流数据分为两路:
    • 实时处理:直接传给解码 / 渲染模块,实现实时预览。
    • 存储处理:按固定格式(如 MP4、TS、自定义分片文件)写入本地磁盘或数据库。
  3. 存储格式设计
    • 视频流通常按时间分片存储(如每 5 分钟一个 MP4 文件),便于后续按时间范围检索。
    • 需记录每个分片的时间戳(开始 / 结束时间),作为回放时定位的索引。
  4. 回放时:根据用户选择的时间范围,从存储中读取对应分片文件,重新组装为流并播放。

三、优点

  1. 保证回放数据的完整性:实时存储避免流数据丢失,确保任何时间点的内容都可回放。
  2. 低延迟实时预览:拉流后可同时满足 “实时看” 和 “事后存”,无需二次拉流。
  3. 灵活的回放定位:通过时间戳索引,可快速定位到任意时刻的历史内容(类似视频播放器的进度条)。
  4. 适应高并发场景:若回放请求较多,可直接从存储读取,避免反复请求源端流(减轻源端压力)。

四、潜在问题与解决方案

  1. 存储资源消耗大

    • 视频流(尤其是高清)占用空间大(如 1080P H.264 流约 100-300MB / 小时)。
    • 解决方案:设置存储生命周期(如保留 7 天),自动清理过期文件;采用压缩编码(如 H.265)减少体积。
  2. 写入性能瓶颈

    • 高码率流(如 4K 视频)可能导致磁盘写入速度不足,出现卡顿或丢帧。
    • 解决方案:使用 SSD 提升写入速度;采用缓存机制(如先写入内存缓冲区,再异步刷盘);降低存储码率(与实时预览码率分离)。
  3. 时间同步问题

    • 存储的流数据时间戳若与实际时间不同步,会导致回放定位不准。
    • 解决方案:拉流时记录精确的接收时间戳(而非流内自带的相对时间),作为存储索引的基准。
  4. 格式兼容性

    • 直接存储原始流(如 RTSP 的 RTP 包)可能不便于回放(需专用解析器)。
    • 解决方案:存储为通用容器格式(如 MP4、TS),支持主流播放器直接播放。

五、是否必须 “边拉边存”?

并非所有场景都需要:

  • 不需要:若回放仅针对 “最近的短时间内容”(如直播回放延迟 10 分钟内),可临时缓存到内存(无需持久化),但风险是断电或崩溃会丢失数据。
  • 必须:若需要长期回放(如监控录像需保存 30 天)、支持任意时间点回溯,或流源不可重复获取(如一次性直播),则必须边拉边存。

六、总结

在大多数需要可靠回放的场景中,“边拉流边存储” 是最优选择,它能平衡实时性与可追溯性,且通过合理的存储策略(分片、生命周期管理、缓存)可规避资源消耗问题。核心是设计好存储格式和索引机制,确保回放时能高效定位和读取历史数据。

随机 crash

“随机 crash”(随机崩溃)指的是程序或系统在无固定规律、不可预测的情况下突然停止运行的现象。其核心特点是:同样的操作步骤,有时正常运行,有时突然崩溃;崩溃时间、场景不固定,难以通过简单复现来定位原因,是开发和调试中非常棘手的问题。

随机 crash 的常见表现

  • 程序突然退出(无任何提示)或弹出错误窗口(如 “程序已停止工作”“Segmentation Fault”)。
  • 崩溃时可能伴随日志输出(如核心转储 core dump、系统日志 dmesg 中的错误),但也可能无任何日志。
  • 在不同环境(如不同硬件、系统版本)或不同负载下,崩溃概率可能不同(如高并发时更易触发)。

随机 crash 的典型原因

随机 crash 通常与内存操作不当、并发竞争、资源竞争等 “不确定性” 问题相关,具体包括:

  1. 内存访问错误

    • 野指针:访问已释放的内存(如 free 后未置空的指针,再次访问时该内存可能已被其他模块占用,数据随机变化)。
    • 缓冲区溢出:写入数据超过数组 / 缓冲区的边界,覆盖相邻内存(可能破坏函数栈、堆结构,导致程序执行流紊乱,崩溃时机取决于被覆盖的数据是否关键)。
    • 内存泄漏累积:长期运行后内存耗尽(OOM),但崩溃时间取决于内存泄漏速度和系统可用内存,表现为随机崩溃。

    例:某程序在循环中动态分配内存 malloc(1024) 但未释放,短时间运行正常,几小时后因内存耗尽随机崩溃。

  2. 多线程 / 并发竞争

    • 数据竞争(Data Race):多个线程同时读写共享变量,且未加锁保护。例如,线程 A 正在修改变量 count,线程 B 同时读取 count,可能导致 count 值异常(如读取到中间值),进而引发逻辑错误(如数组越界),崩溃与否取决于线程调度顺序。
    • 死锁 / 活锁:线程因争夺资源陷入无限等待,虽不直接崩溃,但可能导致程序无响应,被系统强制终止(表现为 “随机” 退出)。
    • 线程安全问题:调用非线程安全的函数(如 C 标准库的 strtokrand),多线程同时操作时可能导致内部状态错乱,触发不可预测的崩溃。

    例:两个线程同时向同一个链表插入节点,未加互斥锁,可能导致链表指针断裂(如 next 指针被同时修改),多数时候正常,偶尔因调度冲突崩溃。

  3. 资源竞争与耗尽

    • 文件描述符 / 句柄泄漏:频繁打开文件、网络连接但未关闭,导致系统资源耗尽(如 Linux 下默认文件描述符上限为 1024),后续 open/socket 调用失败,若程序未处理错误,可能访问无效句柄崩溃,崩溃时机取决于资源消耗速度。
    • 动态链接库(DLL/SO)冲突:程序加载多个版本不兼容的库(如不同版本的 libc),或库文件被动态替换(如更新软件时),导致符号解析错误,崩溃时机与库加载顺序相关。
  4. 硬件 / 环境因素

    • 内存硬件故障:物理内存存在坏块,程序运行时恰好访问到该区域,导致随机崩溃(可通过 memtest 工具检测)。
    • CPU 缓存 / 指令重排:多线程环境下,CPU 指令重排可能导致共享变量的可见性问题(未使用 volatile 或内存屏障),引发逻辑错误,崩溃概率与 CPU 调度相关。
    • 系统资源限制:如栈空间不足(递归过深未限制)、进程数 / 线程数达到系统上限,触发崩溃的时机取决于具体操作路径。

如何调试随机 crash?

由于随机性强,调试难度高,常用手段包括:

  1. 开启核心转储(core dump):配置系统生成崩溃时的内存快照(如 ulimit -c unlimited),事后用 gdb 分析 core 文件,定位崩溃时的函数调用栈和变量状态。
  2. 添加详细日志:在关键操作(内存分配、线程同步、资源访问)处打印日志,记录时间戳、变量值、线程 ID,通过日志回溯崩溃前的异常状态。
  3. 使用工具检测
    • 内存问题:valgrind(检测内存泄漏、野指针)、AddressSanitizer(ASAN,检测缓冲区溢出)。
    • 并发问题:ThreadSanitizer(TSAN,检测数据竞争)、Helgrind(检测死锁)。
  4. 压力测试与复现:通过脚本模拟高并发、高负载场景(如循环执行操作 10 万次),提高崩溃概率,缩小排查范围。

总结

随机 crash 本质是程序中隐藏的 “不确定性缺陷”(如内存错误、并发竞争)在特定条件下被触发的结果。其随机性源于硬件调度、线程执行顺序、资源分配等不可控因素,调试时需结合工具、日志和压力测试,从 “不确定性” 中寻找 “确定性” 的规律。

IPC进程间通信有哪些

进程间通信(IPC,Inter-Process Communication)是指不同进程之间交换数据、同步操作或传递信号的机制。由于进程拥有独立的内存空间,无法直接访问彼此的地址空间,因此需要通过操作系统提供的特定接口实现通信。以下是常见的进程间通信方式,按适用场景和特性分类:

一、基于内存的通信(高效,适用于同一主机)

1. 共享内存(Shared Memory)

  • 原理:多个进程通过映射同一块物理内存到各自的虚拟地址空间,直接读写这块内存实现通信,无需数据拷贝。
  • 特点
    • 速度最快(无内核中转,直接内存访问)。
    • 需配合同步机制(如信号量、互斥锁)防止数据竞争。
  • 适用场景:高频、大数据量通信(如视频处理、实时数据共享)。
  • 实现:Linux 下通过 shmgetshmat 系统调用;Windows 下通过 CreateFileMapping

2. 内存映射文件(Memory-Mapped Files)

  • 原理:将磁盘文件映射到进程的虚拟内存,进程通过读写内存间接操作文件,多个进程映射同一文件即可共享数据。
  • 特点
    • 数据持久化(同步到磁盘),适合需要持久化的共享数据。
    • 速度接近共享内存,适用于大文件共享。
  • 适用场景:跨进程共享大型数据集(如数据库缓存、日志文件共享)。
  • 实现:Linux 下通过 mmap;Windows 下通过 MapViewOfFile

二、基于消息的通信(灵活,支持不同粒度数据)

1. 管道(Pipe)

  • 原理:内核中的一个缓冲区,通过文件描述符操作,数据单向流动(半双工),只能在父子进程或兄弟进程间使用(基于 fork 继承)。
  • 特点
    • 简单易用,适用于单向、流式数据传递。
    • 容量有限(通常几 KB),满了会阻塞写操作。
  • 适用场景:命令行管道(如 ls | grep)、父子进程间简单数据传递。
  • 实现:Linux/UNIX 下通过 pipe 系统调用。

2. 命名管道(Named Pipe / FIFO)

  • 原理:与管道类似,但通过文件系统中的路径名标识,允许任意进程(无亲缘关系)通过路径访问,支持双向通信(需两个 FIFO)。
  • 特点
    • 突破管道的亲缘关系限制,可在任意进程间使用。
    • 数据在内存中传递,不持久化。
  • 适用场景:同一主机上无亲缘关系的进程通信(如客户端 - 服务器模型)。
  • 实现:Linux 下通过 mkfifo 创建;Windows 下称为 “命名管道”(CreateNamedPipe)。

3. 消息队列(Message Queue)

  • 原理:内核维护的一个消息链表,进程可按类型发送 / 接收消息(结构化数据),支持异步通信。
  • 特点
    • 数据有格式(消息类型 + payload),可按类型读取,无需顺序接收。
    • 生命周期独立于进程(进程退出后消息可保留)。
  • 适用场景:需要按优先级或类型处理的消息(如任务调度、日志收集)。
  • 实现:Linux 下通过 msggetmsgsndmsgrcv;Windows 下通过 “消息队列” API。

三、基于信号的通信(简单通知,适用于事件触发)

信号(Signal)

  • 原理:操作系统向进程发送的异步通知(如 SIGINT 中断、SIGTERM 终止),进程可注册信号处理函数响应。
  • 特点
    • 传递的是 “事件” 而非数据,只能携带有限信息(信号编号)。
    • 处理机制简单,适合紧急事件(如程序异常终止、用户中断)。
  • 适用场景:进程间简单通知(如子进程退出通知父进程、超时提醒)。
  • 实现:Linux 下通过 kill 发送信号,signal 或 sigaction 注册处理函数。

四、基于同步的通信(协调进程执行顺序)

1. 信号量(Semaphore)

  • 原理:一个计数器,用于控制多个进程对共享资源的访问,通过 P(减 1,资源占用)和 V(加 1,资源释放)操作实现同步。
  • 特点
    • 主要用于同步(防止竞态条件),而非传递数据。
    • 支持多个进程同时访问有限资源(如限制 5 个进程同时读写文件)。
  • 适用场景:共享资源的并发控制(如数据库连接池、多进程文件写入)。
  • 实现:Linux 下通过 semgetsemop;Windows 下通过 CreateSemaphore

2. 互斥锁(Mutex)

  • 原理:特殊的信号量(值只能为 0 或 1),确保同一时间只有一个进程访问共享资源(互斥访问)。
  • 特点
    • 比信号量更简单,专用于 “排他性” 访问控制。
    • 支持 “所有权”(谁加锁谁解锁),防止误操作。
  • 适用场景:保护临界区(如单例模式初始化、共享内存写操作)。
  • 实现:Linux 下通过 pthread_mutex_t(线程互斥锁,扩展到进程需共享内存);Windows 下通过 CreateMutex

五、网络通信(跨主机,基于网络协议)

1. Socket(套接字)

  • 原理:通过网络协议(TCP/UDP)实现进程通信,可在同一主机(localhost)或不同主机间使用。
  • 特点
    • 通用性强,支持跨主机、跨平台通信。
    • TCP 提供可靠的字节流,UDP 提供不可靠的 datagram。
  • 适用场景:客户端 - 服务器模型(如 Web 服务、分布式系统)、跨主机进程通信。
  • 实现:几乎所有操作系统都支持(socketbindconnect 等系统调用)。

2. RPC(远程过程调用)

  • 原理:通过网络调用远程进程的函数,屏蔽网络细节,让跨进程调用像本地函数一样简单。
  • 特点
    • 基于 Socket 封装,简化跨主机通信编程。
    • 支持同步 / 异步调用,常用于分布式系统。
  • 适用场景:分布式服务(如微服务间调用、分布式计算)。
  • 实现:gRPC、Thrift、XML-RPC 等框架。

总结:选择依据

需求场景推荐方式核心优势
同一主机,高频大数据量共享内存 / 内存映射文件速度最快,无数据拷贝
同一主机,简单数据传递命名管道 / 消息队列易用性好,支持无亲缘关系进程
跨主机通信Socket / RPC通用性强,支持网络传输
进程同步 / 资源控制信号量 / 互斥锁防止竞态条件,保证安全性
简单事件通知信号轻量,适合紧急事件

实际开发中,常组合使用多种 IPC(如共享内存 + 信号量:共享内存传递数据,信号量控制访问)。

STL容器有哪些, 它们各自有哪些适用场景?

STL(Standard Template Library,标准模板库)提供了多种容器(Container),用于存储和管理数据,每种容器有其独特的底层结构和适用场景。以下是常用 STL 容器的分类、特性及典型应用场景:

一、序列容器(Sequential Containers)

插入顺序存储元素,元素位置与插入顺序相关,不自动排序。

容器底层结构核心特性时间复杂度(增删查)适用场景
vector动态数组内存连续,支持随机访问;尾部增删高效,中间 / 头部增删需移动元素。随机访问:O (1);尾部增删:O (1);中间增删:O (n)需频繁随机访问、尾部插入,且元素数量变化可控(如存储列表、缓存数据)。
deque双端队列(分段连续)支持双端高效增删,随机访问效率略低于vector;内存分配更灵活(避免大内存块)。随机访问:O (1);双端增删:O (1);中间增删:O (n)需频繁在头部和尾部操作(如队列、栈的混合场景,滑动窗口缓冲区)。
list双向链表内存不连续,不支持随机访问;任意位置增删仅需修改指针,效率高。随机访问:O (n);任意增删:O (1)(已知位置时)需频繁在中间插入 / 删除(如链表式数据、频繁重组的列表)。
forward_list单向链表仅支持单向遍历,内存开销比list小;尾部增删效率低(需遍历)。随机访问:O (n);头部增删:O (1);其他:O (n)内存受限,且仅需单向遍历、频繁头部操作(如轻量级链表、简单队列)。
array静态数组大小固定,编译期确定;内存连续,随机访问高效,不支持动态扩容。随机访问:O (1);增删:不支持(大小固定)存储固定长度的数据(如坐标、配置参数),需避免动态内存分配的场景。

二、关联容器(Associative Containers)

元素按键(Key)排序存储,支持快速查找,底层多为红黑树(平衡二叉树)。

容器底层结构核心特性时间复杂度(增删查)适用场景
set红黑树存储唯一键(Key 即 Value),自动按键排序;不允许重复元素。增删查:O (log n)需去重并排序的场景(如存储唯一 ID、排序的标签集合)。
multiset红黑树set类似,但允许重复元素(键可重复)。增删查:O (log n)需排序且允许重复的场景(如统计频率、存储多个相同优先级的任务)。
map红黑树存储键值对(Key-Value),键唯一且排序;通过键快速查找值。增删查:O (log n)需键值映射且按键排序的场景(如字典、配置表、用户信息索引)。
multimap红黑树map类似,键可重复(一个键对应多个值)。增删查:O (log n)一对多映射且需排序(如按类别分组的列表、多值索引)。

三、无序关联容器(Unordered Associative Containers)

元素无序存储,底层为哈希表,查找效率极高(平均 O (1))。

容器底层结构核心特性时间复杂度(平均)适用场景
unordered_set哈希表存储唯一键,无序;哈希函数决定存储位置,查找速度快于set增删查:O (1);最坏:O (n)需快速去重但无需排序(如判断元素是否存在、黑名单过滤)。
unordered_multiset哈希表unordered_set类似,允许重复键。增删查:O (1);最坏:O (n)需快速插入重复元素且无需排序(如统计词频、临时缓存多个相同值)。
unordered_map哈希表存储键值对,键唯一且无序;查找速度快于map,但内存开销更大。增删查:O (1);最坏:O (n)需高效键值查找且无需排序(如缓存、哈希表索引、快速数据映射)。
unordered_multimap哈希表unordered_map类似,键可重复。增删查:O (1);最坏:O (n)一对多映射且需快速查找(如日志按类型分组、多值哈希索引)。

四、容器适配器(Container Adapters)

基于其他容器封装的特殊接口,不直接提供迭代器,专注特定功能。

容器底层默认容器核心特性适用场景
stackdeque后进先出(LIFO),仅支持栈顶操作(push/pop/top)。需栈结构的场景(如表达式求值、递归模拟、深度优先搜索 DFS)。
queuedeque先进先出(FIFO),仅支持队尾插入、队头删除(push/pop/front/back)。需队列结构的场景(如任务调度、广度优先搜索 BFS、消息队列)。
priority_queuevector优先级队列,元素按优先级排序(默认最大堆),每次弹出优先级最高的元素。需按优先级处理元素(如任务调度器、最大 / 最小值实时获取)。

选择容器的核心依据

  1. 是否需要排序:需排序用set/map;无需排序且需快速查找用无序容器(unordered_*)。
  2. 访问方式:随机访问优先vector/deque;频繁增删中间元素用list
  3. 性能需求:高频查找用哈希容器(unordered_*);内存敏感用forward_list/array
  4. 功能场景:栈 / 队列操作选适配器;键值映射用map/unordered_map

例如:

  • 存储用户 ID 并去重,且需按 ID 排序 → set
  • 存储学生成绩(学号→分数),需快速查询 → unordered_map
  • 实现一个 undo/redo 功能(后进先出) → stack
  • 存储动态增长的日志列表,需频繁遍历和尾部添加 → vector
http://www.dtcms.com/a/581604.html

相关文章:

  • [linux] grep命令的使用
  • 前后端跨域问题解决
  • 通往AGI的模块化路径:一个可能的技术架构(同时解答微调与RAG之争)
  • cartographer ros 配置详解
  • 告别人工登高 无人机智能巡检平台让效率提升300%
  • docker登录ghcr.io
  • 网站评估 源码wordpress 建立数据库连接时出错 用户名密码可能不正确
  • 划清界限:深度解读EUDR法案的适用范围,谁将受到冲击?
  • 数据结构初阶:Java中的Stack和Queue
  • Node.js环境变量配置的实战技术
  • 帮人做网站赚多少钱邯郸市内最新招聘信息
  • 提问:Flutter 项目在浏览器中运行失败是怎么回事?
  • Node.js 多进程
  • 基于spark岗位招聘推荐系统 基于用户协同过滤算法 Django框架 数据分析 可视化 大数据 (建议收藏)✅
  • 《Flutter全栈开发实战指南:从零到高级》- 12 -状态管理Bloc
  • 装饰工程东莞网站建设百度seo外包
  • CSS 提示工具:高效开发利器
  • IDE 开发的一天
  • Jwt令牌、过滤器、拦截器快速入门
  • 做画找图网站网站建设的公司合肥
  • h5支付宝支付 - 支付宝文档中心1.登录 支付宝开放平台 创建 网页/移动应用
  • Java八股—MySQL
  • 网站显示目录北京网站建设华大
  • Go中的泛型编程和reflect(反射)
  • Go Ebiten小游戏开发:扫雷
  • TransformerLLM(大语言模型)的核心底层架构
  • 网站设计的毕业设计百度建设网站
  • 【GitHub热门项目】(2025-11-07)
  • Vue Router (动态路由匹配)
  • python+django/flask的在线学习系统的设计与实现 积分兑换礼物