ALSA PCM hw_ptr/appl_ptr 指针模型
文章目录1. 背景2. 基本概念3. snd_pcm_update_hw_ptr0(): 维护 hw_ptr4. __snd_pcm_lib_xfer(): 维护 appl_ptr5. Capture 方向5. Playback 方向6. boundary 的作用7. trace 视角下的变化规律7.1 Capture7.2 Playback8. 总结1. 背景本文基于 Audio Codec 的 ALSA PCM trace整理hw_ptr、appl_ptr、hw_ptr_base、buffer_size、boundary的作用以及它们在 capture/playback 方向上的变化规律。在分析 ALSA PCM 低延迟音频路径时trace 中经常出现类似事件hwptr: pcmC2D0p/sub0: POS/IRQ: pos..., old..., base..., period64, buf256 applptr: pcmC2D0p/sub0: prev..., curr..., avail..., period64, buf256这些字段背后对应 ALSA PCM runtime 中的几组核心指针。理解它们是分析 XRUN、period wakeup、用户态读写节奏和端到端延迟的基础。2. 基本概念buffer_size PCM ring buffer 的真实大小单位是 frames。 例如 buffer_size256period_size64表示 ring buffer 有 4 个 period。 hw_ptr ALSA 维护的硬件逻辑进度。 Capture: 硬件/DMA 已经写入到哪里。 Playback: 硬件/DMA 已经播放/消费到哪里。 appl_ptr ALSA 维护的应用逻辑进度。 Capture: 应用已经读取到哪里。 Playback: 应用已经写入到哪里。 hw_ptr_base 用于把驱动返回的环形 pos 转换成单调递增 hw_ptr 的基准。 boundary ALSA 逻辑指针的巨大回卷边界用于防止 hw_ptr/appl_ptr 无限增长。驱动的pointer()只能返回真实 ring buffer 内的位置pos 0 ~ buffer_size - 1例如buffer_size 256pos: 0 - 64 - 128 - 192 - 0 - 64 ...如果只看pos无法判断第二次0是“回到起点”还是“已经播放/录制了 256 frames”。所以 ALSA 使用hw_ptr hw_ptr_base pos把环形位置转换成逻辑单调进度pos: 0 64 128 192 0 64 hw_ptr_base: 0 0 0 0 256 256 hw_ptr: 0 64 128 192 256 3203. snd_pcm_update_hw_ptr0(): 维护 hw_ptr相关代码位于sound/core/pcm_lib.c核心逻辑可以简化为old_hw_ptrruntime-status-hw_ptr;possubstream-ops-pointer(substream);hw_baseruntime-hw_ptr_base;new_hw_ptrhw_basepos;if(new_hw_ptrold_hw_ptr){hw_baseruntime-buffer_size;new_hw_ptrhw_basepos;}runtime-hw_ptr_basehw_base;runtime-status-hw_ptrnew_hw_ptr;它做了三件事1. 调用底层驱动 pointer() 得到 DMA 当前 ring 位置 pos。 2. 用 hw_ptr_base pos 得到逻辑 hw_ptr。 3. 如果 pos 回卷则 hw_ptr_base buffer_size。trace 示例hwptr: IRQ: pos0, old192, base0, period64, buf256这表示驱动返回pos0旧hw_ptr192。ALSA 判断硬件跨过 ring 末尾于是hw_ptr_base 0 256 new_hw_ptr 256 0 256所以硬件进度不是倒退到 0而是前进到 256。4. __snd_pcm_lib_xfer(): 维护 appl_ptr__snd_pcm_lib_xfer()是 ALSA read/write 的公共传输函数同样位于sound/core/pcm_lib.c核心流程可以简化为availis_playback?snd_pcm_playback_avail(runtime):snd_pcm_capture_avail(runtime);framesmin(size,avail);appl_ptrruntime-control-appl_ptr;appl_ofsappl_ptr%runtime-buffer_size;copy_data(appl_ofs,frames);appl_ptrframes;pcm_lib_apply_appl_ptr(substream,appl_ptr);也就是说Capture: 应用从 ring buffer 读走 frames。 appl_ptr 前进。 Playback: 应用向 ring buffer 写入 frames。 appl_ptr 前进。trace 示例applptr: prev256, curr288, avail0, period64, buf256表示应用指针从256前进到288本次传输了32 frames。更新后avail0。5. Capture 方向Capture 中hw_ptr 硬件写入录音数据的位置 appl_ptr 应用读取录音数据的位置公式位于include/sound/pcm.h逻辑是capture_availhw_ptr-appl_ptr;capture_hw_availbuffer_size-capture_avail;含义snd_pcm_capture_avail() 已经 capture 好、应用可以读取的数据量。 snd_pcm_capture_hw_avail() ring buffer 剩余空闲空间硬件还可以继续写入的数据量。视图Capture ring: ---- appl_ptr -------- hw_ptr ---- ---- 可读数据 ---- hw_ptr 前进 capture_avail 增大 appl_ptr 前进 capture_avail 减小如果capture_avail持续增大说明应用读得慢最终可能 overrun。5. Playback 方向Playback 中hw_ptr 硬件播放/消费的位置 appl_ptr 应用写入播放数据的位置核心公式playback_availhw_ptrbuffer_size-appl_ptr;playback_hw_availbuffer_size-playback_avail;含义snd_pcm_playback_avail() ring buffer 剩余可写空间应用还能写多少播放数据。 snd_pcm_playback_hw_avail() 已经填充、等待硬件播放的数据量。视图Playback ring: ---- hw_ptr -------- appl_ptr ---- --- 待播放数据 --- hw_ptr 前进 playback_avail 增大 appl_ptr 前进 playback_avail 减小如果playback_hw_avail持续减小说明应用写得慢最终可能 underrun。6. boundary 的作用hw_ptr和appl_ptr是逻辑单调指针但不能无限增长所以 ALSA 设置了一个很大的boundary。可以理解为buffer_size 是真实 DMA ring 的大小。 boundary 是 ALSA 逻辑 ring 的大小。通常boundary远大于buffer_size并且是buffer_size的整数倍。这样绝大多数时间里hw_ptr/appl_ptr都像单调递增计数器只有运行很久后才在boundary回卷。它的意义是1. 避免逻辑指针无限增长导致溢出。 2. 让 hw_ptr - appl_ptr 这类差值计算在长时间运行中仍然成立。 3. 避免真实 ring buffer 的频繁回卷影响上层逻辑。7. trace 视角下的变化规律7.1 Capture典型事件hwptr: IRQ: pos64, old0, base0, period64, buf256 applptr: prev0, curr64, avail0, period64, buf256含义DMA 捕获了 64 frames。 应用读取了 64 frames。 读取后 capture_avail0没有积压。如果每个 period 都呈现类似模式说明录音侧稳定应用及时消费数据。7.2 Playback典型事件hwptr: POS: pos32, old0, base0, period64, buf256 applptr: prev256, curr288, avail0, period64, buf256含义硬件已经播放/消费了 32 frames。 ring buffer 空出了 32 frames。 应用补写了 32 frames。 补写后 playback_avail0buffer 再次填满。因此 playback 中applptr不一定每次都按period_size64前进。它按当前可写空间和应用请求大小前进可能是 32也可能是 64。8. 总结buffer_size 真实 PCM ring buffer 大小。 hw_ptr_base 把驱动返回的环形 pos 转换成逻辑 hw_ptr。 hw_ptr 硬件进度时间轴。 appl_ptr 应用进度时间轴。 boundary ALSA 逻辑时间轴的最大回卷边界。最终可以用一句话概括hw_ptr 描述硬件走到哪里appl_ptr 描述应用走到哪里 buffer_size 描述真实 ring 大小 hw_ptr_base 负责消除真实 ring 回卷 boundary 负责限制逻辑指针无限增长。低延迟音频分析时最核心就是观察这两条时间轴的距离Capture: hw_ptr - appl_ptr 太大 应用读慢可能 overrun。 Playback: appl_ptr - hw_ptr 太小 应用写慢可能 underrun。在64 frames / 48kHz测试中capture/playback 的hwptr和applptr都能稳定推进没有 xrun说明单向录音和单向播放路径在空载下是健康的。