GNU Radio buffer介绍
1、 GNU Radio buffer分配
1.1 buffer使用简述
GNU Radio数据buffer的管理机制是零拷贝循环缓存,这样能够保证来自源
的数据流高效在各个模块之间传输。具体的buffer分配方式如下图所示:

图1 buffer的使用方式
页管理存储方式,一开始就分配好,buffer的长度是每页的大小的倍数,同时,每个循环缓存映射成两部分,实际使用的只是其中的一半。Buffer的size由用户决定(最大、最小缓存大小设定)。
1.2 buffer创建方式
针对不同的操作系统,GNU Radio提供不同配置方式。由vmcircbuf classes类型控制实现buffer使用。
● MS Windows系统,采用Create File Mapping方式
● OSX系统,采用Memory-mapped Temp File方式
● Linux/POSIX系统,提供了System V Shared Memory和Memory-mapped Shared Memory两种方式
具体每种方式的建立步骤这里不再赘述,后面根据代码再简单介绍。
2、 GNURadio buffer的创建流程
GNU Rradio Flowgraph 运行时,每个模块的输出端口都绑定一个循环缓冲区(buffer),通过一个写指针 w_ptr 输出数据,它的下游模块通过一个读指针r_ptr 读取数据。
对于每个block的buffer如何进行分配,在block运行的设置连接函数setup_connections()里就对它进行了具体的描述,主要是对buffer的size以及地址进行设定,具体流程如下图所示:

图2 buffer分配流程
3、 代码分析
每个Block通过detail()可以得倒block_detail实例对象指针,而且block的输
出端口都绑定一个循环缓冲区(buffer),其分配起始于下面的代码:


上面这部分代码是buffer分配的具体实现,主要是对输出端口对应的缓冲区size大小及输出数据类型做判断,确保buffer分配的有效,然后通过make_buffer实现buffer的分配以及设定循环缓冲区的首地址。


Buffer的分配最终是调用VM circular buffer类实现缓存分配并返回基址。



前面提到过,针对不同的操作系统提供了不同的内存分配接口,系统默认的buffer分配配置文件路径是$HOME/.gnuradio/prefs/vmcircbuf_default_factory。通过读取文件获取到(如默认 gr::vmcircbuf_sysv_shm_factory),如果没有默认配置或者配置的不正确,则直接获取工厂列表中的第一个。
同样,根据获取到的factory,然后通过对应的make进行buffer分配。
具体的代码实现参考以下的.h和对应的cc文件(vmcircbuf_createfilemapping.h、
vmcircbuf_sysv_shm.h、vmcircbuf_mmap_shm_open.h、vmcircbuf_mmap_tmpfile.h)
1、MS Windows系统的实现
Windows系统采用createfilemapping 方式实现内存的分配及映射,具体代
码的实现逻辑如下:
① size大小是page size的整数倍,通过CreateFileMapping获取文件页的句柄;
② 通过VirtualAlloc分配2*size大小的内存并返回指针first_tmp;
③ 通过MapViewOfFileEx分别将文件页映射到分配的虚拟内存的前后两部分,指针分别对应为first_tmp和first_tmp+size;
④ 返回内存地址指针(保证分配的内存大小是2*size)。
具体的实现代码见vmcircbuf_createfilemapping::vmcircbuf_createfilemapping
::vmcircbuf_createfilemapping(int size): gr::vmcircbuf(size)函数,
2、OSX系统的实现
苹果系统,查看vmcircbuf_mmap_tmpfile::vmcircbuf_mmap_tmpfile (int size)
: gr::vmcircbuf (size)函数具体描述,这里不再详细介绍。
3、LINUX系统的Mermory-mapped Shared Memory实现
查看vmcircbuf_mmap_shm_open::vmcircbuf_mmap_shm_open(int size)
: gr::vmcircbuf(size)函数具体描述,通过shm_open分配内存,这里不再详细介绍。
4、GNU radio默认的buffer分配采用LINUX系统的另外一种实现方式,
即System V Shared Memory共享内存方式,主要由shmget、shmat、shmat、shmctl四个函数组成实现。代码的逻辑基本上如下描述:
① 获取page size,传入的size大小是page size的整数倍;
② 通过shmget分别创建shmid_guard(page size大小)、shmid1(size大
小)、shmid2(2*size大小)共享内存对象;
③ 通过shmat将各共享内存区对象映射到调用进程的地址空间,将shmid1
分别映射到shmid2前半部分和后半部分,并且保证shmid2的两部分内存映射到同样的虚拟空间,同时映射shmid_guard到首地址,且为只读模式;
④ 返回内存地址指针(保证分配的内存大小是2*size)。
具体的实现代码见文件vmcircbuf_sysv_shm.cc文件的vmcircbuf_sysv_shm::vmcircbuf_sysv_shm(int size): gr::vmcircbuf(size)函数。
到此为止,buffer的分配完成,后面通过block建立block之间的连接,对应每个buffer绑定reader,在block的运行中,通过维护r_ptr和w_ptr即可实现buffer的循环使用。
4、 LOG跟踪
在flat_flowgraph.cc文件中,将#define FLAT_FLOWGRAPH_DEBUG 0修改为#define FLAT_FLOWGRAPH_DEBUG 1,即打开日志的使能开关,就可以看到各block buffer分配的简单信息,如果需要详细了解分配的各步骤信息,可以在代码中增加打印信息。
全部回复 (0)
cll
社区创作者
