1、概要
EPICS提供了三个一般用途IOC回调任务。这些任务之间的唯一差别是它们的调度优先级;低,中或高。低优先级任务运行在仅高于通道访问的优先级;中等优先级大约等于周期扫描任务的平均值,而高优先级高于事件扫描任务。回调任务对任何需要一个在其下立即或者一段延时后运行某个作业的任务的软件组件可用。在作业延时过程中,它们也可以被取消。三个回调任务项任务看门狗注册它们自己。用大量栈空间创建它们,并且因而能够被用于调用记录processing。例如,I/O事件扫描程序使用这些一般用途的回调任务。
为了使用这些一般回调任务,必须采取以下步骤:
1)包含回调定义:
#include <callback.h>
2)为一个结构体提供存储区,它是用于回调任务的一个私有结构体:
CALLBACK mycallback;
允许这个结构体成为一个更大结构体的组成部分,例如:
struct {
...
CALLBACK mycallback;
...
} ..
3)进行初始化CALLBACK中字段的调用(在大多数情况中,这些实际上是宏):
allbackSetCallback(CALLBACKFUNC func, CALLBACK *pcb);
者定义要被执行的回调函数。第一个参数是一个函数的地址,将把被传给这个CALLBACK的地址,并且返回void。第二个参数是CALLBACK结构体的地址。
callbackSetPriority(int, CALLBACK *pcb);
第一个参数是优先级,其是以下值之一:priorityLow, priorityMedium或priorityHigh。这些值被定义在callback.h中。第二个参数再次为CALLBACK结构体的地址。
callbackSetUser(void *, CALLBACK *pcb);
这个调用用于保存一个指针值,使用以下宏能够再次获取这个指针值:
callbckGetUser(void *, CALLBACK * pcb);
如果你的回调函数存在以在dbScanLock/dbScanUnlock调用内运行单个记录,你可以使用这种快捷方式,其向你提供了这个回调例程并且同时设置了另外两个参数(这里使用用户参数是一个指向记录实例的指针):
callbackSetProcess(CALLBACK *pcb, int prio, void *prec)
4) 在需要一个回调请求时,只要调用以下之一:
callbackRequest(CALLBACK *pcb);
callbackRequestProcessCallback(CALLBACK *pcb, int prio, void *prec);
二者都能从中断级别代码被调用。回调例程被传递单个参数,它时被传递给callbackRequest的相同参数,即:CALLBACK结构体的地址。第二个例程是调用callbackSetProcess和callbackRequest的快捷方式。以下是在用于为执行相关任务排队回调例程前等待指定时间的延时版本。
callbackRequestDelayed(CALLBACK *pCallback, double seconds);
callbackRequestProcessCallbackDelayed(CALLBACK *pCallback,
int Priority, void *pRec, double seconds);
这些例程不能被从中断级别代码调用。
2、语法
void callbackInit(void);
void callbackSetCallback(void *pcallbackFunction,
CALLBACK *pcallback);
void callbackSetPriority(int priority, CALLBACK *pcallback);
void callbackSetUser(void *user, CALLBACK *pcallback);
void callbackGetUser(void *user, CALLBACK *pcallback);
void callbackSetProcess(CALLBACK *pcallback, int Priority, void *prec);
void callbackRequest(CALLBACK *);
void callbackRequestProcessCallback(CALLBACK *pCallback,
int Priority, void *prec);
void callbackRequestDelayed(CALLBACK *pCallback, double seconds);
void callbackRequestProcessCallbackDelayed(
CALLBACK *pCallback, int Priority, void *prec, double seconds);
void callbackCancelDelayed(CALLBACK *pcallback);
int callbackSetQueueSize(int size);
- 在IOC初始化中自动执行callbackInit,因而用户代码从不调用这个函数。
- callbackSetCallback, callbackSetPriority, callbackSetUser和callbackGetUser实际上是宏。
- 可以从中断上下文调用callbackRequest和callbackRequestProcessCallback。
- callbackRequest的延时版本在排队回调前等待执行时间。
- callbackCancelDelayed可以用于取消一个延时的回调。
- callbackReqeustProcessCallback发出以下调用:
callbackSetCallback(ProcessCallback, pCallback);
callbackSetPriority(Priority, pCallback);
callbackSetUser(pRec, pCallback);
callbackRequest(pCallback);
例程ProcessCallback设计用于异步设备结束并且被定义为:
static void ProcessCallback(CALLBACK *pCallback)
{
dbCommon *pRec;
struct rset *prset;
callbackGetUser(pRec, pCallback); // 在回调函数中获取记录实例
prset = (struct rset *)pRec->rset; // 获取记录支持模块
dbScanLock(pRec);
(*prset->process)(pRec); // 调用基类实例的process例程
dbScanUnlock(pRec);
}
3、示例
以下使用回调的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "callback.h"
typedef struct CallbackPvt{
char begid[80];
CALLBACK callback;
char endid[80];
}CallbackPvt;
static CallbackPvt cbpvt;
// 定义一个回调函数
void myCallback(CALLBACK *pcallback)
{
CallbackPvt * pcbpvt;
// 从传给回调函数的参数中获取一个CallbackPvt结构体的地址
callbackGetUser(pcbpvt, pcallback);
// 在回调函数中打印CallbackPvt结构体中begid和endid成员
printf(”begid=%s endid=%s\n”,&pcbpvt->begid[0],
&pcbpvt->endid[0]);
}
void example(char *pbegid, char*pendid)
{
// 将传入内容复制到一个CallbackPvt结构体中相应成员变量中
strcpy(&cbpvt.begid[0],pbegid);
strcpy(&cbpvt.endid[0],pendid);
// 为回调设置回调函数
callbackSetCallback(myCallback,&cbpvt.callback);
// 为回调设置优先级
callbackSetPriority(priorityLow,&cbpvt.callback);
// 为回调设置回调参数
callbackSetUser(&cbpvt,&cbpvt.callback);
// 请求回调
callbackRequest(&cbpvt.callback);
}
int main(int argc, char** argv)
{
char * str1 = "Hello before CALLBACK Structure";
char * str2 = "Hello after CALLBACK Structure";
example(str1, str2);
return 0;
}
编译以上代码,测试结果如下:
orangepi@orangepi4-lts:~/host_program/host/hostApp$ O.linux-aarch64/cbtest
begid=Hello before CALLBACK Structure
endid=Hello after CALLBACK Structure
这个简单示例展示了如何用你自己包含CALLBACK结构体的结构体在任意位置使用回调任务。
4、回调队列
回调请求把每个回调优先级的请求放入一个单独的环形缓存。这些缓存默认最多保存2000个请求。通过在启动文件中iocInit前调用callbackQueueSize修改这个限制。
int callbackSetQueueSize(int size);