简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
-
-
- [🌻1. 前言](#🌻1. 前言)
- [🌻2. 用法与应用场景](#🌻2. 用法与应用场景)
- [🌻3. 调用流程剖析](#🌻3. 调用流程剖析)
-
- [3.1 核心步骤](#3.1 核心步骤)
- [3.2 涉及核心时序图](#3.2 涉及核心时序图)
- [🌻4. 实战应用案例](#🌻4. 实战应用案例)
- [🌻5. 用法总结](#🌻5. 用法总结)
-
🌻1. 前言
本篇目的:Android tinyalsa深度解析之 pcm_close 调用流程与实战。
🌻2. 用法与应用场景
pcm_close 是 tinyalsa 库中用于关闭 PCM 设备并释放所有相关资源的终点函数。它不仅负责用户态内存的回收,更重要的是触发内核驱动释放物理硬件资源。
- 用法 :
int pcm_close(struct pcm *pcm); - 返回值 :成功返回 0。若传入指针无效,函数会安全返回。
- 应用场景:
- 结束音频会话:在播放或录音任务完成后,正常关闭设备节点。
- 错误处理路径 :当
pcm_open成功但后续的pcm_prepare或数据读写发生严重错误时,必须调用此函数进行资源清理。 - 动态路由切换:在 Android 系统切换音频路径(如 Speaker 切换至有线耳机)时,HAL 层通常需要关闭当前的 PCM 实例并重新配置。
🌻3. 调用流程剖析
3.1 核心步骤
- 指针合法性校验 :检查传入的
pcm是否为NULL或指向bad_pcm。tinyalsa的设计确保了对非法句柄调用 close 也不会导致系统崩溃。 - 停止流传输 :如果设备仍处于
RUNNING状态,底层驱动会在关闭文件描述符前尝试停止 DMA 传输。 - 解映射 MMAP 缓冲区 :检查
pcm->mmap_buffer。如果用户以PCM_MMAP模式打开设备,pcm_close会自动调用munmap释放映射的内存块。 - 触发内核 Release :执行系统调用
close(pcm->fd)。这会进入内核 ALSA 核心层,调用驱动的release回调函数,进而关闭硬件功放或重置硬件寄存器。 - 内存释放 :调用
free(pcm)销毁用户态的结构体实例,完成最后的清理工作。
关键技术:坏句柄安全回收
tinyalsa 在 pcm_open 失败时也会返回一个结构体(fd 为 -1)。pcm_close 能够识别这种"坏句柄"并仅释放内存而不尝试执行内核关闭操作,这简化了 HAL 层的异常处理逻辑。
3.2 涉及核心时序图
Audio Codec/DMA Kernel ALSA Core VFS (close syscall) tinyalsa (pcm_close) Audio HAL Audio Codec/DMA Kernel ALSA Core VFS (close syscall) tinyalsa (pcm_close) Audio HAL alt [MMAP 模式已启用] 调用 pcm_close(pcm) 1. 验证 pcm 指针及状态 2. munmap(pcm->>mmap_buffer) 3. close(pcm->>fd) 触发 snd_pcm_release 停止 DMA, 降低功耗 硬件已重置 返回 4. free(pcm) 释放结构体 返回 0
🌻4. 实战应用案例
此 Demo 展示了在 Android HAL 开发中,如何规范地使用 pcm_close 并通过指针置空防止野指针异常。
c
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
/**
* 模拟 HAL 层安全释放音频流的逻辑
*/
void hal_pcm_release(struct pcm **pp_pcm) {
if (pp_pcm == NULL || *pp_pcm == NULL) {
return;
}
/* 1. 执行 tinyalsa 关闭操作 */
printf("HAL: Closing pcm device...\n");
int ret = pcm_close(*pp_pcm);
if (ret != 0) {
printf("HAL: Warning - pcm_close returned error code %d\n", ret);
}
/* 2. 极其重要:手动将原指针置为 NULL
* 防止 HAL 层其他线程误用已释放的内存 (Use-After-Free)
*/
*pp_pcm = NULL;
printf("HAL: Resource cleanup complete.\n");
}
int main() {
struct pcm_config config = {
.channels = 2,
.rate = 44100,
.period_size = 1024,
.period_count = 4,
.format = PCM_FORMAT_S16_LE
};
// 打开设备 (声卡 0, 设备 0, 播放模式)
struct pcm *out_stream = pcm_open(0, 0, PCM_OUT, &config);
if (out_stream && pcm_is_ready(out_stream)) {
printf("HAL: Audio session started.\n");
// ... 此处执行音频数据写入 pcm_write ...
}
// 无论打开成功还是失败,最终都要调用清理函数
hal_pcm_release(&out_stream);
return 0;
}
🌻5. 用法总结
| 特性 | 详情描述 |
|---|---|
| 内存管理 | 完全清理 。内部集成了对 mmap 缓冲区和 pcm 结构体本身的 free。 |
| 调用配对 | 必须与 pcm_open 配对。漏掉 close 会导致声卡被占用(Device Busy)以及系统 fd 泄露。 |
| 线程安全 | 非线程安全 。严禁在主线程 close 的同时在另一线程执行 pcm_write,需在 HAL 层加锁。 |
| 硬件状态 | 重置硬件。该函数执行后,内核通常会关闭相关的 DAC/ADC 电源以节省功耗。 |
| 容错性 | 高容忍度。可以安全处理未成功 ready 的 pcm 指针,不会造成内核异常。 |