PulseAudio 音频服务中,大部分音频数据缓冲区都用 pa_memblock
结构来管理,包括音频数据在 PulseAudio 音频服务和它的客户端之间的跨进程传递,PulseAudio 音频服务的数据通路中各个节点之间的音频数据传递,及 PulseAudio 音频服务和 ALSA 音频设备之间的音频数据交换。pa_memblock
结构定义 (位于 pulseaudio/src/pulsecore/memblock.c) 如下:
struct pa_memblock {PA_REFCNT_DECLARE; /* the reference counter */pa_mempool *pool;pa_memblock_type_t type;bool read_only:1;bool is_silence:1;pa_atomic_ptr_t data;size_t length;pa_atomic_t n_acquired;pa_atomic_t please_signal;union {struct {/* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */pa_free_cb_t free_cb;/* If type == PA_MEMBLOCK_USER this is passed as free_cb argument */void *free_cb_data;} user;struct {uint32_t id;pa_memimport_segment *segment;} imported;} per_type;
};
pa_memblock
结构中,实际保存音频数据的内存缓冲区的地址保存在 data
原子指针字段中,音频数据内存缓冲区的长度保存在 length
字段中。
不同场合下使用的音频数据缓冲区具有不同的特性,音频数据缓冲区因特性不同有多种不同的类型,pa_memblock
结构即有多种不同的类型,类型由 pa_memblock
结构的 type
字段描述。类型主要有如下这些:
typedef enum pa_memblock_type {PA_MEMBLOCK_POOL, /* Memory is part of the memory pool */PA_MEMBLOCK_POOL_EXTERNAL, /* Data memory is part of the memory pool but the pa_memblock structure itself is not */PA_MEMBLOCK_APPENDED, /* The data is appended to the memory block */PA_MEMBLOCK_USER, /* User supplied memory, to be freed with free_cb */PA_MEMBLOCK_FIXED, /* Data is a pointer to fixed memory that needs not to be freed */PA_MEMBLOCK_IMPORTED, /* Memory is imported from another process via shm */PA_MEMBLOCK_TYPE_MAX
} pa_memblock_type_t;
各个 pa_memblock
类型特性如下:
- PA_MEMBLOCK_POOL:
pa_memblock
结构本身和它所管理的音频数据内存缓冲区都在 内存池 中分配; - PA_MEMBLOCK_POOL_EXTERNAL:
pa_memblock
结构本身在堆上分配,它所管理的音频数据内存缓冲区在 内存池 中分配; - PA_MEMBLOCK_APPENDED:
pa_memblock
结构本身和它所管理的音频数据内存缓冲区都在堆上分配; - PA_MEMBLOCK_USER:
pa_memblock
结构本身在堆上分配,它所管理的音频数据内存缓冲区由使用方传入,音频数据内存缓冲区的释放方法也由使用方传入; - PA_MEMBLOCK_FIXED:
pa_memblock
结构本身在堆上分配,它所管理的音频数据内存缓冲区由使用方传入,但这块音频数据内存缓冲区不需要释放,如 PulseAudio 音频服务和 ALSA 音频设备交换音频数据时,通过 mmap 获得的内核音频数据缓冲区; - PA_MEMBLOCK_IMPORTED:
pa_memblock
结构本身在堆上分配,它所管理的音频数据内存缓冲区来自于通过共享内存获得的其它进程。
内存池 是 PulseAudio 音频服务中使用的基本的音频数据内存管理机制,它的基本思路是,预先分配一块巨大的内存,将这一大块内存分割为大小相等的小内存块,每次需要音频数据缓冲区时,则取一个小内存块来用。音频数据的传递和处理中,所需的音频数据缓冲区大小并不会经常剧烈变化,因而将内存池的内存划分为大小相等的小内存块对于兼顾音频数据缓冲区的分配/释放效率和内存利用率是值得的。
PulseAudio 音频服务中 内存池 由 pa_mempool
结构描述,这个结构定义 (位于 pulseaudio/src/pulsecore/memblock.c) 如下:
struct pa_mempool {/* Reference count the mempool** Any block allocation from the pool itself, or even just imported from* another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED),* shall increase the refcount.** This is done for per-client mempools: global references to blocks in* the pool, or just to attached ones, can still be lingering around when* the client connection dies and all per-client objects are to be freed.* That is, current PulseAudio design does not guarantee that the client* mempool blocks are referenced only by client-specific objects.** For further details, please check:* https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html*/PA_REFCNT_DECLARE;pa_semaphore *semaphore;pa_mutex *mutex;pa_shm memory;bool global;size_t block_size;unsigned n_blocks;bool is_remote_writable;pa_atomic_t n_init;PA_LLIST_HEAD(pa_memimport, imports);PA_LLIST_HEAD(pa_memexport, exports);/* A list of free slots that may be reused */pa_flist *free_slots;pa_mempool_stat stat;
};
内存池 的大内存块由 pa_shm
结构描述,这个结构定义 (位于 pulseaudio/src/pulsecore/shm.h) 如下:
typedef struct pa_shm {pa_mem_type_t type;unsigned id;void *ptr;size_t size;/* Only for type = PA_MEM_TYPE_SHARED_POSIX */bool do_unlink:1;/* Only for type = PA_MEM_TYPE_SHARED_MEMFD** To avoid fd leaks, we keep this fd open only until we pass it* to the other PA endpoint over unix domain socket.** When we don't have ownership for the memfd fd in question (e.g.* pa_shm_attach()), or the file descriptor has now been closed,* this is set to -1.** For the special case of a global mempool, we keep this fd* always open. Check comments on top of pa_mempool_new() for* rationale. */int fd;
} pa_shm;
根据大内存块的创建方式,大内存块分为 3 种类型,如:
typedef enum pa_mem_type {PA_MEM_TYPE_SHARED_POSIX, /* Data is shared and created using POSIX shm_open() */PA_MEM_TYPE_SHARED_MEMFD, /* Data is shared and created using Linux memfd_create() */PA_MEM_TYPE_PRIVATE, /* Data is private and created using classic memory allocation(posi