GNU Radio Scheduler

ccll2026-07-23 14:45:24
2 浏览 0 评论

1、 Scheduler 任务简述

GNU Radio中每个block的调度工作如下图所示,主要包含三个方面:

1 block的调度工作

(1)   维护block对应的buffer的读写指针;

blcok对应的buffer读写指针在buffer章节介绍中有描述,这里不再赘述。

(2)   处理各个block的约束要求

Block的约束需求主要是四部分设置:Forecast函数的功能是告诉调度器对于每次输出的item需要多少个输入的itemsOutput Multiple则是通过函数set_output_multiple(int multiple)设置每次输出items的大小,如果没有足够的数据则不输出。Alignment表明数据对齐,通过函数set_alignment(int multiple)进行设置,保证输入的数据为multiple倍数大小;History主要用来设置buffer读指针的history items,一方面保证我们有足够有效的数据history items超过输出的output_items大小,另一方面它经常用于有因果关系的信号间进行工作。具体使用方式如下图:

2 history使用

(3)   完成信息的传输

GNU Radio采用消息传递(Message)和流标签(Stream tag)两种机制在block 之间传输信息。其中Stream tag是同步标签,只能单向传输,Message是异步消息,可以向任何方向传输。在GNU Radio流图中,Stream tag实线表示,Message虚线表示。

【注意】Stream tag与数据流是并行传输模式,不是插入到原始数据流,也不会改变原始数据流,而是绑定到数据流的某一个样点,只能在block之间传递消息,不能通过天线发送出去。

2、 流图调度

2.1 模块调度初始化

流图建立后,运行流图,创建调度器。对流图的模块进行拓扑排列,获取各个blockvector信息,设置流图中所有模块的done标志,然后循环为各个block创建线程并启动线程body进行信息处理。

3 流图的block初始化流程

2.2 模块线程整体处理

      block线程主体处理的流程如下图所示。先前的GNU Radio框架介绍提及到,模块的默认调度机制是TPB模式,即多线程调度。每个block的线程处理主体函数的实现均包含在tpb_thread_body.cc文件中,进入线程body,首先获取blockdetail()信息以及当前线程ID,接着绑定线程、设置线程优先级,然后while循环进行消息、数据流的run_one_iteration操作以及返回状态处理。具体介绍代码分析部分再描述。

4 block线程的主体处理流程

2.3 模块线程核心处理

run_one_iteration()函数是线程主体运行的核心,包含了模块的主要功能实现。

4 block线程核心处理流程

总的来说,run_one_iteration()函数按照模块类型(信源、信宿以及中间模块三种)进行逐一处理。针对block的每个输出端口,检查各自对应的buffer的剩余空间,从而获得block可以输出的items数目。如果没有用于输出的足够的空间,则返回BLKD_OUT(输出阻塞);同时,根据前面计算出的输出items数目,计算出所需要的输入items数目(通过forecast函数),综合检查输入的items数目是否满足要求,不满足则返回BLKD_IN;如果输入的items足够,也有足够的空间存放输出的items,则执行block::general_work()完成模块的功能。

2.3.1 try again流程

   该流程主要从以下几个方面进行逻辑的判断和处理:

1、如果是固定速率的block,有一些专门的限制条件,单独分析;

2、未设置Output Multiple,针对Alignment是否设置进行判断;

3、调用forecast计算输入items,判断数据是否满足要求,回调work函数进行处理;

4、其他的异常情况判断,返回相应状态处理。

具体的实现流程如下图所示:

5 try again处理流程

2.3.2 work流程

       work流程通过获取buffer指针,调用各个blockgeneral_work()函数进行数据处理,如果启用了work处理函数的性能计数器,则统计性能,接着根据返回值进行相应处理并返回结果状态值。

6 work处理流程

2.3.3 DONE流程

       该流程代表当前数据信息处理完成,然后设置blockdone标志,通知上下游模块可以进行数据信息的输入和输出。

7 done处理流程

3、 代码走读

3.1 调度器初始化

流图运行的时候,会根据系统选择的调度器模式(TPB)进行调度器的创建

和初始化(对流图的模块进行拓扑排列,获取各个blockvector信息(模块的上下游关系),设置流图中所有模块的done标志,循环为各个block创建线程)。

3.2 线程处理主体函数

线程主体函数处理部分,首先是获取线程ID,绑定线程,然后设置各个block的线程优先级,清除blockfinish标志。接下就是信息的处理部分了,先遍历消息队列,如果有当前消息注册的句柄,则从队列中POP,然后调用该blockhandler分发消息;否则将该消息塞进队列,判断队列里的最大消息数,超过容量则将最开始进队列的消息删除掉。如果不是消息而是流模块,则判断输入输出数据信息调用run_one_iteration()处理,返回处理的状态结果值。然后就针对各返回状态值进行处理:

READY:通知上下游模块有足够的buffer空间和数据输出;

READY_NO_OUTPUT:通知上游模块有buffer空间可以使用;

DONE:告知上下游模块信息处理完成;

BLKD_IN:等待数据的输入或者新的消息处理;

BLKD_OUT:等待数据的缓存空间释放或者新的消息处理。

3.3 流模块数据处理

      代码在block_executor.cc文件中,主要根据block类型(信源、信宿以及中间模块)进行分别处理。

3.3.1信源模块

该模块只有下游模块,综合计算输出buffer空间,如果足够则调用work处理,否则等待buffer的释放。

3.3.2信宿模块

该模块只有上游模块,如果输入数据完成且数据不满足blockoutput multiple,直接返回DONE;如果没有输出则表明没有输入数据,返回BLKD_IN,否则直接进入try again流程处理。

3.3.3中间模块

中间模块从上游模块接收数据,然后输出到下游模块,在处理时先确认输入的数据,然后计算输出buffer的空间,如果noutput_items值为0,则阻塞中,等待buffer,值为-1则表明已经完成,否则进入try again进行进一步的判断和分析。

3.3.4 try again处理

      该部分针对block的限制需求进行相关判断分析和处理,主要包含fixed_rateoutput_multiple_setalignmentforecast以及inputcheck,并返回对应的处理值。如果一切都正常,则获取bufferr_ptr指针读取输入数据,调用work进行数据的处理。

3.3.5 work处理

      获取输出buffer的写指针,将输入数据调用general_work处理输出写入buffer

3.3.6其他处理

       包括调整剩余待处理的未对齐数据的unaligned属性;如果是标签流,按照新的速率进行数据传递;更新buffer的指针位置;设置新的传输速率等等。

4、 LOG日志

       block的调度时,消息模块主要是由框架进行队列的管理及分发,这部分日志没什么好查看,如果想看消息具体的实现细节,则需在各个block的实现文件中增加相应日志信息;流模块则是由调度器循环不断的处理,主要的细节处理在block_executor.cc文件中,将#define ENABLE_LOGGING 0修改为#define ENABLE_LOGGING 1,即打开日志的使能开关,就可以看到各个block在运行中的各步骤打印信息,同样如果要看模块的具体功能实现细节,则需在blcokwork实现中增加相应的日志记录。

全部回复 (0)

0 / 1000
请遵守社区规范
暂无评论,快来抢沙发吧~
c

cll

社区创作者

帖子信息

所属板块GNU Radio
发布时间2026-07-23 14:45:24
2浏览
0点赞
0评论