操作理论
初始化
在初始化中,端口驱动注册每个通信端口以及所有支持的接口。
用户代码创建一个asynUser, 它是访问asynDriver功能的"句柄",通过调用:
pasynManager->createAsynUser(processCallback,timeoutCallback);
一个asynUser有以下特性:
- asynManager通过asynUser管理访问一个端口的多个请求。
- processCallback由queueRequest使用,如下描述,是一个用户提供的回调例程的地址。
- timeoutCallback是调用者提供的回调地址,如果一个queueRequest留在这个队列时间太长,它将被调用。
- 设备支持代码应该为对底层驱动的每个原子访问创建一个asynUser,即,一组调用一定不能与对底层驱动的其它调用交织在一起。例如,对EPICS记录支持的设备支持应该为每个记录实例创建一个asynUser。
- 设备支持代码不应该尝试在访问一个端口的多个请求来源之间共享一个asynUser。如果这了这件事,设备支持自身必须必须处理的竞争问题已经由asynManager处理了。
用户代码通过对以下调用连接一个底层驱动:
status = pasynManager->connectDevice(pasynUser,portName,addr);
这个调用指定了设备的端口名和地址。它接着调用findInterface来定位接口,它用这些接口调用这个驱动。例如:
pasynInterface = pasynManager->findInterface(pasynUser,asynOctetType,1);
请求访问一个端口
用户代码可以通过两种方法请求访问一个端口:
1)queueRequest:传递给createAsynUser的processCallback对这个端口接口进行调用。
2)lockPort/unlockPort, queueLockPort/queueUnlockPort:调用者在持有锁时对端口接口进行调用。这些调用以及对端口的调用可能阻塞,因而不应该被不应该阻塞的代码使用,例如,EPICS记录的同步设备支持。
queueReqeust-控制流
通过调用以下,用户代码请求访问一个端口:
status = pasynManager->queueRequest(pasynUser,priority,timeout);
此结果导致processCallback或timeoutCallback被调用。对一个端口的大多数请求时从processCallback进行的。queueRequest不阻塞。如果为一个可以阻塞的端口调用queueReqeust,这个请求将被排队到一个专用于这个端口的线程。如果为一个不阻塞的端口调用queueRequest,它只调用processCallback。仅在底层驱动通过调用queueRequest, lockPort/unlockPort和/或queueLockPort/queueUnlockPort被访问时,保证才是有效的。
以下示例是基于EPICS IOC记录/设备支持。
第一个示例展示了对一个可以阻塞的端口的访问。
发生的记录支持事件的顺序从一个应用程序线程起始,并且用以下步骤解释:
- 记录运行用PACT 0调用设备支持(运动没有活动)
- 设备支持调用queueRequest
- queueRequest在这个驱动的工作队列上放置这个请求。应用程序线程现在能够继续运行并且执行其它操作。这个I/O请求的后续操作时在端口驱动线程中处被理。
- portThread从工作队列移除这个I/O请求。
- portThread调用位于记录设备支持中的processCallback。
- portThread调用底层驱动。底层驱动读或写例程阻塞,直到这个I/O结束或者直到发生超时。底层驱动例程返回I/O操作的结果给processCallback。
- processCallback请求记录被运行。注意:运行请求将由其中一个标准回调请求而不是端口线程进行。
- 记录支持在此调用设备支持,此时用PACT 1(运行处于活动)。设备支持更新记录中字段并且返回到记录支持,这结束了记录运行。
第二个示例展示对一个不能阻塞端口的访问。
发生的记录设备支持事件的顺序从一个应用程序线程开始,在图2中,并且用以下步骤解释:
- 记录运行调用设备设置。
- 设备支持调用queueRequest。
- 由于端口是同步的,即不能阻塞,queueRequest锁定端口并且接着调用processCallback。
- processCallback调用底层读取读或写例程。底层驱动例程返回I/O操作结果给processCallback。
- processCallback返回到queueRequest,它解锁端口并且返回到设备支持,设备支持返回到记录支持,这结束了记录运行。
asynDriver结构体和接口
asynDriver.h描述以下:
- asynStatus:一个描述很多方法返回的状态的枚举。
- asynException:一个描述异常的枚举。
- asynQueuePriority:一个描述队列优先级的枚举。
- asynUser:一个包含通用信息的结构体,并且是用于调用大多数方法的"句柄"。
- asynInterface:一个描述一个接口的结构体。
- userCallback:一个用于以上描述的用户处理回调函数的typedef。
- exceptionCallback:一个用于用户回调的typedef,当异常发生时,被调用。
- timeStampCallback:一个用户回调函数的typedef,其将被updateTimeStamp调用。
- asynManager:一个用于与asynDriver通信的接口。
- asynCommon:一个提供方法的接口,这些方法必须被所有底层驱动实现。
- asynTrace:一个接口和相关函数和定义,它们实现了跟踪功能。
asynStatus
定义大多数方法返回的状态。如果一个方法返回除了asynSuccess外的一个状态,并且传给这个方法的其中一个参数是pasynUser,则这个方法预计写一条错误消息到pasynUser->errorMessage。
typedef enum {
asynSuccess,asynTimeout,asynOverflow,asynError,asynDisconnected,asynDisabled
}asynStatus;
asynStatus
asynSuccess | 请求成功 |
asynTimeout | 请求由超时出错 |
asynOverflow | 驱动已经丢失输入数据。如果内部缓存或者用户提供的缓存太小,这会发生。在可能时,应该写底层驱动,使得用户可以以小段读取输入。 |
asynError | 某个其它错误发生。 |
asynDisconnected | 请求因为端口没有连接,出错 |
asynDisabled | 请求因为端口或设备被禁用,出错 |
asynException
为方法execeptionOccurred定义异常。
typedef enum {
asynExceptionConnect,asynExceptionEnable,asynExceptionAutoConnect,
asynExceptionTraceMask,asynExceptionTraceIOMask,asynExceptionTraceInfoMask,
asynExceptionTraceFile,asynExceptionTraceIOTruncateSize
} asynException;
asynException
asynExceptionConnect | 端口或设备的连接状态发生变化 |
asynExceptionEnable | 端口或设备的使能状态发生变化 |
asynExceptionAutoConnect | 端口或设备的自动连接状态发生变化 |
asynExceptionTrackMask | 端口或设备的traceMask发生变化 |
asynExceptionTrackIOMask | 端口或设备的traceIOMask发生变化 |
asynExceptionTrackInfoMask | 端口或设备的traceInfoMask发生变化 |
asynExceptionTraceFile | 端口或设备的跟踪文件发生变化 |
asynExceptionTraceTruncateSize | 端口或设备的traceIOTruncateSize发生变化 |
asynQueuePriority
这定义了传递给queueRequest的优先级。
asynQueuePriority
typedef enum {
asynQueuePriorityLow,asynQueuePriorityMedium,asynQueuePriorityHigh,
asynQueuePriorityConnect
}asynQueuePriority;
asynUser
描述一个结构体,用户代码传递它到大多数asynManager和驱动方法。代码必须通过调用asynManager:createAsynUsser(或asynManager:duplicateAsynUser)和asynManager:freeAsynuser分配和释放要给asynUser。
typedef struct asynUser {
char *errorMessage;
int errorMessageSize;
/* 必须由用户设置的超时 */
double timeout; /* I/O操作的延时 */
void *userPvt;
void *userData;
/* 以下是由驱动使用的 */
void *drvUser;
/* 以下通常通过asynDrvUser->create()由驱动设置 */
int reason;
epicsTimeStamp timestamp;
/* 以下是用于来自方法调用的其它信息*/
int auxStatus; /* 用于辅助状态 */
int alarmStatus; /* 尤其对应EPICS记录警报状态 */
int alarmSeverity; /* 尤其对应EPICS记录警报严重性 */
}asynUser;
asynUser
errorMessage | 当一个方法返回asynError时,它应该通过调用以下放置一条错误消息到errorMessage: epicsSnprintf(pasynUser->errorMessage, pasynUsser->errorMessageSize, "<format>", ...); 错误消息不应该(不包含)以一个新行字符序列结束(例如,\n)。由用户代码决定是否以及如何显示错误消息。防止错误消息出现新行,使得用户代码嵌入错误消息在另一条消息或输出格式变得简单。 |
errorMessageSize | errorMessage的大小。用户不能更改这个值 |
timeout | 在I/O请求的超时前的秒数。这是由用户设置并且在对一个驱动调用期间被修改。如果对一个底层驱动调用导致驱动进行很多I/O请求,这是每个I/O请求的时间。 含义如下: >0.0:等待I/O结束,最多等待timeout秒 =0.0:执行没有阻塞可以进行的任意I/O。如果没有I/O可以无阻塞的进行,返回timeout错误。 <0.0:无限超时。一直等待结束I/O |
userPvt | 由用户使用。用户一你刚刚在调用pasynManager->createAsynUser后立即设置这个成员。如果在asynUser排队时,这被修改了,结果时未定义的,例如,它会引起崩溃 |
userData | 也由用户使用 |
drvUser | 一个驱动可以使用这个保存asynUser专用数据。asynDrvUser接口用于asynUser和驱动之间的通信。 |
reason | 驱动和asynUsers可以将这个成员用作通用字段。按惯例,它用作确定什么"命令"正在通过一个特定接口被发送。例如,一个实现了asynInt32的A/D驱动可能定义reason=0表示"返回A/D转换",而reason=1可能表示"返回放大器增益"。尤其驱动实现了asynDrvUser接口,并且使用这个成员从这个命令的描述字符串(例如:在本例中"DATA"或"GAIN")转换成枚举"reason"。正在调用一个中断的驱动,用户经常使用reason决定用户回调是否应该被调用。小于0的reason值被保留给了标准含义。例如,ASYN_REASON_SIGNAL用于表示"out of band"请求。devGpib支持使用这个成员报告SRQs。 |
timestamp | 提供它们自己时间戳的设备使用这个字段为其TSE字段设为"-2"的记录提供时间值。 |
auxStatus | 任何方法在auxStatus中提供其它返回信息。含义由这个方法确定。callbacks可以使用auxStatus在设备支持callback函数中设置记录警报状态。 |
alarmStatus | 任何方法在alarmStatus中提供其它返回信息。含义由这个方法确定。callbacks可以使用alarmStatus在设备支持callback函数中设置记录警报状态。 |
alarmSeveity | 任何方法在alarmSeverity中提供其它返回信息。含义由这个方法确定。callbacks可以使用alarmSeverity在设备支持callback函数中设置记录警报严重性。 |
asynInterface
这定义了向asynManager:registerPort或asynManager:interposeInterface注册的接口。
typedef struct asynInterface{
const char *interfaceType; /* 例如asynCommonType */
void *pinterface; /* 例如pasynCommon */
void *drvPvt;
}asynInterface;
asynInterface
interfaceType | 一个描述接口的字符串 |
pinterface | 指向这个接口的指针。用户必须转换它为正确类型。 |
drvPvt | 调用了registerPort或interposeInterface的代码专用 |
asynManager
这是用于与asynDriver通信的主要接口。
/*registerPort attributes*/
#define ASYN_MULTIDEVICE 0x0001
#define ASYN_CANBLOCK 0x0002
/*standard values for asynUser.reason*/
#define ASYN_REASON_SIGNAL -1
#define ASYN_REASON_RESERVED_LOW 0x70000000
#define ASYN_REASON_RESERVED_HIGH 0x7FFFFFFF
#define ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED ASYN_REASON_RESERVED_LOW
typedef void (*userCallback)(asynUser *pasynUser);
typedef void (*exceptionCallback)(asynUser *pasynUser,asynException exception);
typedef void (*timeStampCallback)(void *userPvt, epicsTimeStamp *pTimeStamp);
typedef struct interruptNode{
ELLNODE node;
void *drvPvt;
}interruptNode;
typedef struct asynManager {
void (*report)(FILE *fp,int details,const char*portName);
asynUser *(*createAsynUser)(userCallback process,userCallback timeout);
asynUser *(*duplicateAsynUser)(asynUser *pasynUser,
userCallback queue,userCallback timeout);
asynStatus (*freeAsynUser)(asynUser *pasynUser);
void *(*memMalloc)(size_t size);
void (*memFree)(void *pmem,size_t size);
asynStatus (*isMultiDevice)(asynUser *pasynUser,
const char *portName,int *yesNo);
/* addr = (-1,>=0) => connect to (port,device) */
asynStatus (*connectDevice)(asynUser *pasynUser,
const char *portName,int addr);
asynStatus (*disconnect)(asynUser *pasynUser);
asynStatus (*exceptionCallbackAdd)(asynUser *pasynUser,
exceptionCallback callback);
asynStatus (*exceptionCallbackRemove)(asynUser *pasynUser);
asynInterface *(*findInterface)(asynUser *pasynUser,
const char *interfaceType,int interposeInterfaceOK);
asynStatus (*queueRequest)(asynUser *pasynUser,
asynQueuePriority priority,double timeout);
asynStatus (*cancelRequest)(asynUser *pasynUser,int *wasQueued);
asynStatus (*blockProcessCallback)(asynUser *pasynUser, int allDevices);
asynStatus (*unblockProcessCallback)(asynUser *pasynUser, int allDevices);
asynStatus (*lockPort)(asynUser *pasynUser);
asynStatus (*unlockPort)(asynUser *pasynUser);
asynStatus (*queueLockPort)(asynUser *pasynUser);
asynStatus (*queueUnlockPort)(asynUser *pasynUser);
asynStatus (*setQueueLockPortTimeout)(asynUser *pasynUser, double timeout);
asynStatus (*canBlock)(asynUser *pasynUser,int *yesNo);
asynStatus (*getAddr)(asynUser *pasynUser,int *addr);
asynStatus (*getPortName)(asynUser *pasynUser,const char **pportName);
/* drivers call the following*/
asynStatus (*registerPort)(const char *portName,
int attributes,int autoConnect,
unsigned int priority,unsigned int stackSize);
asynStatus (*registerInterface)(const char *portName,
asynInterface *pasynInterface);
asynStatus (*exceptionConnect)(asynUser *pasynUser);
asynStatus (*exceptionDisconnect)(asynUser *pasynUser);
/*any code can call the following*/
asynStatus (*interposeInterface)(const char *portName, int addr,
asynInterface *pasynInterface,
asynInterface **ppPrev);
asynStatus (*enable)(asynUser *pasynUser,int yesNo);
asynStatus (*autoConnect)(asynUser *pasynUser,int yesNo);
asynStatus (*isConnected)(asynUser *pasynUser,int *yesNo);
asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);
asynStatus (*isAutoConnect)(asynUser *pasynUser,int *yesNo);
asynStatus (*setAutoConnectTimeout)(double timeout);
asynStatus (*waitConnect)(asynUser *pasynUser, double timeout);
/*The following are methods for interrupts*/
asynStatus (*registerInterruptSource)(const char *portName,
asynInterface *pasynInterface, void **pasynPvt);
asynStatus (*getInterruptPvt)(asynUser *pasynUser,
const char *interfaceType, void **pasynPvt);
interruptNode *(*createInterruptNode)(void *pasynPvt);
asynStatus (*freeInterruptNode)(asynUser *pasynUser,interruptNode *pnode);
asynStatus (*addInterruptUser)(asynUser *pasynUser,
interruptNode*pinterruptNode);
asynStatus (*removeInterruptUser)(asynUser *pasynUser,
interruptNode*pinterruptNode);
asynStatus (*interruptStart)(void *pasynPvt,ELLLIST **plist);
asynStatus (*interruptEnd)(void *pasynPvt);
/* Time stamp functions */
asynStatus (*registerTimeStampSource)(asynUser *pasynUser, void *userPvt, timeStampCallback callback);
asynStatus (*unregisterTimeStampSource)(asynUser *pasynUser);
asynStatus (*updateTimeStamp)(asynUser *pasynUser);
asynStatus (*getTimeStamp)(asynUser *pasynUser, epicsTimeStamp *pTimeStamp);
asynStatus (*setTimeStamp)(asynUser *pasynUser, const epicsTimeStamp *pTimeStamp);
const char *(*strStatus)(asynStatus status);
}asynManager;
epicsShareExtern asynManager *pasynManager;
asynManager
1) report:
void (*report)(FILE *fp,int details,const char*portName);
报告有关asynManager的状态。如果portName是非NULL,它报告一个特定的端口。如果portName是NULL,则它报告每个注册的端口。它也为每个被报告的端口调用asynCommon:report。
2)createAsynUser
asynUser *(*createAsynUser)(userCallback process,userCallback timeout);
创建一个asynUser。调用者指定两个回调,process和timeout。这些回调仅由queueRequest的结果被调用。timeout回调是可选的。为errorMessage分配errorMessageSize个字符。存储量不能被修改。如果它不能够分配分配这个存储区,此方法不返回。
3) duplicateAsynUser
asynUser *(*duplicateAsynUser)(asynUser *pasynUser, userCallback queue,userCallback timeout);
通过调用createAsynUser创建一个asynUser。它接着按如下初始化这个新的asynUser:用从pasynUser获取的值初始化字段timeout, userPvt, userData和drvUser。它的connectDevice状态与pasynUser的相同。
4) freeAsynUser
asynStatus (*freeAsynUser)(asynUser *pasynUser);
释放一个asynUser。用户必须通过这个调用释放一个asynUser。如果asynUser连接到一个端口,asynManager:disconnect被调用。如果断开出错,这个调用也将出错。asynUser的存储区被保存在一个空闲列表并且在之后调用createAsynUser或dupilicateAsynUser中将被再使用。因而持续调用createAsynUser(或duplicateAsynUser)和freeAsynUser是高效的。
5)memMalloc/memFree
void *(*memMalloc)(size_t size);
void (*memFree)(void *pmem,size_t size);
分配和释放内存。memMalloc/memFree维护一个不同大小的空闲列表的集合。因而,需要内存区一小段时间的任何应用程序可以使用memMalloc/memFree分配和释放这个存储区,不会使内存碎片化。传递给memFree的大小必须与再memMalloc调用中指定的值相同。
6) isMultiDevice
asynStatus (*isMultiDevice)(asynUser *pasynUser, const char *portName,int *yesNo);
回调问题"这个端口支持多设备吗?" 在调用connectDevice前,可以调用这个方法。
7) connectDevice
/* addr = (-1,>=0) => connect to (port,device) */
asynStatus (*connectDevice)(asynUser *pasynUser, const char *portName,int addr);
连接asynUser结构体到由portName, addr指定的设备。端口名与在调用registerPort中指定的侠女沟通。如果这个asynUser已经连接了一个设备,这个调用将出错。如果端口不支持多设备,则忽略addr。connectDevice仅连接这个asynUser到对应portName, addr的端口驱动。端口驱动可能有也有可能没有连接到实际设备。因而,connectDevice和asynCommon:connect完全不同。
单和多设备端口驱动之间的不同,见运行理论的描述。
8) disconnect
asynStatus (*disconnect)(asynUser *pasynUser);
从port,addr断开这个asynUser,这个连接是通过之前调用connectDevice连接这个asynUser。如果asynUser排队或被锁定,或者有一个通过exceptionCallback注册的回调,调用将出错。注意asynManager:disconnect和asynCommon:disconnect完全不同。
9)exceptionCallbackAdd
asynStatus (*exceptionCallbackAdd)(asynUser *pasynUser, exceptionCallback callback);
当由asynException定义的异常之一发生时,将调用callback。这个callback可以调用isConnected, isEnabled或isAutoConnect来查找连接状态。asynTrace提供了查明当前跟踪设置的方法。
10)exceptionCallbackRemove
asynStatus (*exceptionCallbackRemove)(asynUser *pasynUser);
callback被移除。必须在disconnect前,调用这个方法。
11)findInterface
asynInterface *(*findInterface)(asynUser *pasynUser, const char *interfaceType,int interposeInterfaceOK);
查找一个驱动接口。如果interposeInterfaceOK是真,则findInterface返回上个注册的或插入的接口。否则,返回由registerPort注册的接口。如果不支持这个interfaceType,它返回0。
用户需要驱动的接口和pdrvPvt的地址,因而可以从这个驱动进行调用。例如:
asynInterface *pasynInterface;
asynOctet *pasynOctet;
void *pasynOctetPvt;
...
pasynInterface = pasynManager->findInterface(
pasynUser,asynOctetType,1);
if(!pasynInterface) { /*error do something*/}
pasynOctet = (asynOctet *)pasynInterface->pinterface;
pasynOctetPvt = pasynInterface->pdrvPvt;
...
/* The following call must be made from a callback */
pasynOctet->read(pasynOctetPvt,pasynUser,...
12)queueRequest
asynStatus (*queueRequest)(asynUser *pasynUser,asynQueuePriority priority,double timeout);
当registerPort被调用时,调用者必须指定它是否可以阻塞,即,属性位ASYN_CANBLOCK设位置或清零。如果已经用ASYN_CANBLOCK true注册了这个端口,则请求被放在一个与此队列相关联的线程的队列上。如果用 ASYN_CANBLOCK false,则queueRequest锁定这个端口并且调用这个process回调。在两种情况中任何一种中,调用了在调用createAsynUser中指定的process回调。
如果asynUser已经在一个队列上,返回一个asynError。当请求排队时,超时启动。一个小于或等于0.0的值意味着没有超时。在调用这个回调前,从队列移除这个请求。回调被允许对asynManager进行请求,诸如queueRequest,blockProcessCallback等。甚至允许从一个回调调用freeAsynUser,但这个请求将被延时到在回调结束后。
优先级asynQueuePriorityConnect必须仅用于asynCommon:connect和asynCommon:disconnect调用,并且一定不能用于其它任何调用。
如果没有传递timeout回调给createAsynUser,并且一个请求了一个非0 timeout的请求,请求出错。
尝试排队一个连接请求外的请求到一个断开的端口将出错,除非reason是ASYN_REASON_QUEUE_EVEN_IF_NOT_CONNECTED。
13)cancelRequest
asynStatus (*cancelRequest)(asynUser *pasynUser,int *wasQueued);
如果一个asynUser排队,从队列移除它。如果在调用cancelRequest时,process或timeout回调是活动的,则cancelRequest在回调结束前将不返回。
14) blockProcessCallback/unlockProcessCallback
asynStatus (*blockProcessCallback)(asynUser *pasynUser, int allDevices);
asynStatus (*unblockProcessCallback)(asynUser *pasynUser, int allDevices);
blockProcessCallback是一个防止在queueRequests之间由其它asynUsers对设备或端口访问的请求。可以从processCallback或者当这个asynUser没有排队的请求时,可以调用blockProcessCallback。当从processCallback被调用时,阻塞立即开始,否则阻塞从下次调用processCallback开始。阻塞表示在调用unblockProcessCallback前,将不调用其它asynUser的processCallback。注意以下对blockProcessCallback的限制:
- blockProcessCallback仅对可以阻塞的驱动有作用,并且如果位非阻塞驱动调用它,返回一个错误。
- 指定asynQueuePriority优先级的queueRequests不能被阻塞。
同时阻塞所有设备和asynUser连接的设备是允许的。
15)lockPort/unlockPort
asynStatus (*lockPort)(asynUser *pasynUser);
asynStatus (*unlockPort)(asynUser *pasynUser);
锁定对一个端口驱动的访问。当对一个端口驱动进行调用时,这是由愿意阻塞的代码使用。代码可以调用lockPort,对这个端口驱动进行任意数目的调用,并且接着调用unlockPort。调用queueRequest和/或lockPort将lockPort和unlockPort调用之间被延时。
16)queueLockPort/queueUnlockPort
asynStatus (*queueLockPort)(asynUser *pasynUser);
asynStatus (*queueUnlockPort)(asynUser *pasynUser);
锁定对一个端口驱动的访问。这是由当对端口驱动进行调用时愿意阻塞的代码使用。代码可以调用queueLockPort,queueLockPort,对端口驱动进行任意数目调用,并且接着调用queueUnlockPort。调用queueRequest和/或lockPort的其它代码将在queueLockPort和queueUnlockPort调用之间被延时。lockPort和queueLockPort之间的差别是queueLockPort使用与queueRequest相同的队列排队一个锁定端口的请求。这表示一个重复调用queueLockPort而在调用之间没有睡眠的线程将仍然允许其它线程访问端口。对于lockPort不是这样的,lockPort将在端口空闲时立即获取mutex,并且会完全阻止其它线程访问这个端口。
18)setQueueLockPortTimeout
asynStatus (*setQueueLockPortTimeout)(asynUser *pasynUser, double timeout);
设置在queueLockPort()中传递给queueRequest()的超时。当创建端口时,设置默认值2.0秒。这个函数可以用于更改那个值。注意,如果传递给queueLockPort的pasynUser->timeout值大于当前值,则使用这个更大的timeout。
19)canBlock
asynStatus (*canBlock)(asynUser *pasynUser,int *yesNo);
yesNo设置位(0,1),即如果对底层驱动的调用可以阻塞(false, true)。这个值是由传递给registerPort的属性确定的。
20)getAddr
asynStatus (*getAddr)(asynUser *pasynUser,int *addr);
*addr设置为用户在调用connectDevice中指定的地址,或如果端口不支持多设备,-1。单和多设备端口驱动之间的差别,见运行伦理描述。
21)getPortName
asynStatus (*getPortName)(asynUser *pasynUser,const char **pportName);
*pportName设置为用户连接端口的名称。
22)registerPort
/* drivers call the following*/
asynStatus (*registerPort)(const char *portName, int attributes,int autoConnect, unsigned int priority,unsigned int stackSize);
这个方法由驱动调用。为每个端口实例进行一次调用。attributes是一个位集合。当前定义了两个位:ASYN_MULTIDEVICE和ASYN_CANBLOCK。驱动必须恰当地指定这些,其是(0,1)对应(no, yes),为端口和连接端口地所有设备提供初始值。priority和stacksize仅在ASYN_CANBLOCK=1时才有作用,在此种情况下,asynManager在用epicsThreadCreate()创建线程时,使用这些值。如果priority是0,则将分配默认值epicsThreadPriority。如果stackSize是0, 将分配默认值epicsThreadGetStackSize(epicsThreadStackMedium)。portName参数指定了名称,asyn代码的上层通过这个名称指向这个通信接口实例。registerPort方法对名称参数指向的字符串进行内部复制。
23)registerInterface
asynStatus (*registerInterface)(const char *portName, asynInterface *pasynInterface);
由端口驱动为每个受到支持的接口调用这个方法。这个方法不复制pasynInterface参数指向的asynInterface。调用者必须在一个位置存储这个接口,为这个端口生命周期保留这个位置。通常通过在'驱动私有'结构体种放置这个asynInterface结构体做这件事。
24)exceptionConnect
asynStatus (*exceptionConnect)(asynUser *pasynUser);
当且仅当驱动连接一个端口或设备时,这个方法必须被这个驱动调用。
25)exceptionDisconnect
asynStatus (*exceptionDisconnect)(asynUser *pasynUser);
当且仅当驱动从端口或设备断开时,这个方法必须被这个驱动调用。
26)interposeInterface
/*any code can call the following*/
asynStatus (*interposeInterface)(const char *portName, int addr, asynInterface *pasynInterface, asynInterface **ppPrev);
这个方法是由客户端代码和端口驱动之间的软件层调用。例如,如果一个设备回显写,则可以创建一个软件模块,它在每次写后发出一次读,并且为接口asynOctet调用InterposeInterface。
可以为一个port/addr/interface发出多个interposeInterface调用。*ppPrev被设置成前个asynInterface的地址。因而,上次调用interposeInterface的软件模块被用户代码调用。它接着可以调用倒数第二次调用interposeInterface的软件模块。这样继续,直到实际的驱动驱动被调用。
也可以用先前还未被注册或替代的asynInterface调用interposeInterface。在这种情况,*ppPrev将是null。因而,可以实现底层驱动未知的新接口。
27)enable
asynStatus (*enable)(asynUser *pasynUser,int yesNo);
如果设置enable设为yes,除非队列超时发生,否则queueRequests不出队。
28)autoConnect
asynStatus (*autoConnect)(asynUser *pasynUser,int yesNo);
当一个用户回调被调度要被调用时,如果autoConnect是true,并且端口或设备没有连接,asynManager调用pasynCommon->connect。详见以下控制流讨论。
29)isConnected
asynStatus (*isConnected)(asynUser *pasynUser,int *yesNo);
根据端口设备连接有无连接,*yesNo设为(0无,1有) 。
30)isEnabled
asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);
根据端口或设备是否使能,*yesNo设为(0否,1是) 。
31)isAutoConnect
asynStatus (*isAutoConnect)(asynUser *pasynUser,int *yesNo);
根据portThread是否将自动连接端口或设备,*yesNo设为(0否,1是)。
32)setAutoConnectTimeout
asynStatus (*setAutoConnectTimeout)(double timeout);
在等待来自端口驱动的初始连接回调时,更改timeout。这个回调发生响应asynManager排队一个连接请求,当驱动驱动注册它的asynCommon接口时这发生。默认timeout是0.5秒。
33)waitConnect
asynStatus (*waitConnect)(asynUser *pasynUser, double timeout);
等待port/device连接,最多timeout秒。
34)registerInterruptSource
/*The following are methods for interrupts*/
asynStatus (*registerInterruptSource)(const char *portName, asynInterface *pasynInterface, void **pasynPvt);
如果一个底层驱动支持中断,它必须为支持中断的每个接口调用这个方法。pasynPvt必须是一个void *的地址, 由registerInterruptSource给其赋一个值。这个参数被传递给interruptStart和interruptEnd。
35)getInterruptPvt
asynStatus (*getInterruptPvt)(asynUser *pasynUser, const char *interfaceType, void **pasynPvt);
想要调用createInterrputNode但不知道pasynPvt地址的任何代码通过这个方法可以找到它。调用者必须连接一个设备,即,必须已经调用connectDevice。如果调用者没有连接,getInterruptPvt返回asynError。
36)createInterruptNode/freeInterruptNode
interruptNode *(*createInterruptNode)(void *pasynPvt);
asynStatus (*freeInterruptNode)(asynUser *pasynUser,interruptNode *pnode);
这些方法是用户可以分配和释放一个interruptNode的唯一方法。pasynPvt是从getInterruptPvt获取的值。createInterruptNode/freeInterruptNode是单独的方法,而不是自动被addInterruptUser/removeInterruptUser完成,因而addInterruptUser/removeInterrupt可以是高效的。
38)addInterruptUser/removeInterruptUser
asynStatus (*addInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);
asynStatus (*removeInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);
实现了registerInterruptUser/cancelInterruptUser的代码必须调用addInterruptUser添加用户到列表或者removeInterruptUser从列表移除用户,否则对interruptStart/interruptEnd的调用将无效。这是一个高效操作,使得用户可以重复调用registerInterruptUser/cancelInterruptUser。如果在中断正在处理时,即在interruptStart/interruptEnd调用之间,调用了这两个方法之一,在调用interruptEnd前,这个调用将阻塞。用于在addInterruptUser调用种指定的asynUser的process回调一定不能调用removeInterruptUser,否则它将永远阻塞。
asynStatus (*removeInterruptUser)(asynUser *pasynUser, interruptNode*pinterruptNode);
37)interruptStart/interruptEnd
asynStatus (*interruptStart)(void *pasynPvt,ELLLIST **plist);
asynStatus (*interruptEnd)(void *pasynPvt);
实现中断的代码是依赖接口的。asynManager提供的唯一服务是用户列表的线程安全实现。当代码想要调用在registerInterruptUser调用种指定的回调,它调用interruptStart获取回调列表。当它结束时,它调用interruptEnd。如果在interruptStart和InterruptEnd调用之间对addInterruptUser/removeInterruptUser进行任何请求,asynManager延时这些请求到interruptEnd被调用。
38)registerTimeStampeSource
/* Time stamp functions */
asynStatus (*registerTimeStampSource)(asynUser *pasynUser, void *userPvt, timeStampCallback callback);
注册一个用户定义的时间戳回调函数。
39)unregisterTimeStampSource
asynStatus (*unregisterTimeStampSource)(asynUser *pasynUser);
注销任何用户定义的时间戳回调函数,并且恢复成asynManager种默认的时间戳来源函数,它仅调用epicsTimeGetCurrent()。
40)updateTimeStamp
asynStatus (*updateTimeStamp)(asynUser *pasynUser);
通过调用默认时间戳来源或者用registerTimeStampSource注册的用户定义的时间戳来源,为端口设置当前时间戳。
41)getTimeStamp
asynStatus (*getTimeStamp)(asynUser *pasynUser, epicsTimeStamp *pTimeStamp);
为这个端口获取当前时间戳,它是由最近一次调用updateTimeStamp返回。
42)setTimeStamp
asynStatus (*setTimeStamp)(asynUser *pasynUser, const epicsTimeStamp *pTimeStamp);
从传递给这个函数的时间戳值直接为这个端口设置当前时间戳。
43)strStatus
const char *(*strStatus)(asynStatus status);
返回相应asynStatus值的一个描述字符串。
asynCommon
asynCommon描述了必须由驱动实现的方法。
/* 所有asyn驱动都支持的设备接口 */
#define asynCommonType "asynCommon"
typedef struct asynCommon {
void (*report)(void *drvPvt,FILE *fp,int details);
/*以下是连接硬件/从硬件断开*/
asynStatus (*connect)(void *drvPvt,asynUser *pasynUser);
asynStatus (*disconnect)(void *drvPvt,asynUser *pasynUser);
}asynCommon;
report | 产生一个有关硬件设备的报告。这是asynCommon方法中唯一不是必须由queueRequest 回调调用或者在lockPort/unlockPort调用之间被调用的方法。 |
connect | 连接硬件设备或者通信路径。queueRequest必须指定优先级asynQueuePriorityConnect, |
disconnect | 从硬件设备或通信路径断开。queueRequest必须指定优先级asynQueuePriorityConnect, |
asynCommonSyncIO
asynCommonSyncIO为需要对一个aysn设备执行"同步"操作的软件提供了一个快捷接口,即等待断开可用以及操作结束时阻塞。代码不需要处理回调或者理解asynManager和asynCommon接口的细节。
typedef struct asynCommonSyncIO {
asynStatus (*connect)(const char *port, int addr,
asynUser **ppasynUser, const char *drvInfo);
asynStatus (*disconnect)(asynUser *pasynUser);
asynStatus (*connectDevice)(asynUser *pasynUser);
asynStatus (*disconnectDevice)(asynUser *pasynUser);
asynStatus (*report)(asynUser *pasynUser, FILE *fd, int details);
} asynCommonSyncIO;
epicsShareExtern asynCommonSyncIO *pasynCommonSyncIO;
注意:这个接口的connect*和disconnect*中存在混淆的可能。为了与其它SyncIO接口保持一致性,连接调用pasynManager->connectDevice,断开调用pasynManger->disconnect,connectDevice调用asynCommon->connect以及disconnectDevice调用asynCommon->disconnect。
asynDrvUser
asynDrvUser提供了允许一个asynUser和用户特性端口驱动之间传递用户特定信息的方法。
#define asynDrvUserType "asynDrvUser"
typedef struct asynDrvUser {
/*The following do not have to be called via queueRequest callback*/
asynStatus (*create)(void *drvPvt,asynUser *pasynUser,
const char *drvInfo, const char **pptypeName,size_t *psize);
asynStatus (*getType)(void *drvPvt,asynUser *pasynUser,
const char **pptypeName,size_t *psize);
asynStatus (*destroy)(void *drvPvt,asynUser *pasynUser);
}asynDrvUser;
create | 用户,即,设备支持调用create。驱动可以创建它需要的任何资源。它可以使用pasynUser->drvUser提供对这些资源的访问。它asynUser和驱动都知道如何访问这些资源,它们必须在资源名称和尺寸上达成一致。如果pptypeName非null,驱动可以传递一个值给*pptypeName。如果psize非null,驱动可以传递一个值给*psize。除非asynUser接收了一个它知道的typeName和尺寸,否则它一定不能访问asynUser.drvUser。 |
getType | 如果其它代码,例如,一个interposeInterface想要访问asynUser.drvUser,它必须调用这个方法,并且确认typeName和size是它所预计的。 |
destroy | 销毁由create创建的资源,并且设置asynUser.drvUer为null |
asynLockPortNotify
为其是另一个端口驱动的一个aysnUser的端口驱动提供了这个方法。例如,可以通过连接到一个标准串口来执行实际的I/O来实现一个串行总线驱动。当串行总线端口由于请求者调用lockPort或因为queueRequest出队被锁定,则串行总线驱动需要锁定相关联的串口。
串行总线驱动注册接口asynLockPortNotify。当串行总线端口被锁定时,asynManager调用pasynLockPortNotify.lock。串行总线驱动为其连接的串口调用asynManager.lockPort。对于unlockPort类似。因而当串行总线端口被锁定时,串行总线也被锁定。
asynLockPortNotify仅由asynManager使用。它未被放入对应这个端口的接口列表中。
asynLockPortNotify是:
#define asynLockPortNotifyType "asynLockPortNotify"
typedef struct asynLockPortNotify {
asynStatus (*lock)(void *drvPvt,asynUser *pasynUser);
asynStatus (*unlock)(void *drvPvt,asynUser *pasynUser);
}asynLockPortNotify;
asynLockPortNotify
lock | 当asynManager.lockPort被调用时,调用。驱动通常为其连接的端口调用asynManger.lockPort |
unlock | 当asynManager.unlockPort被调用时,调用。驱动通常为其连接的端口调用asynManger.unlockPort |
asynOption
asynOption提供设置驱动专用选项的通用方式。例如,串口驱动使用这个接口指定波特率,停止位等。
#define asynOptionType "asynOption"
/*The following are generic methods to set/get device options*/
typedef struct asynOption {
asynStatus (*setOption)(void *drvPvt, asynUser *pasynUser,
const char *key, const char *val);
asynStatus (*getOption)(void *drvPvt, asynUser *pasynUser,
const char *key, char *val, int sizeval);
}asynOption;
asynOption
setOption | 设置与key相关联的值 |
getOption | 获取与key相关联的值 |
Trace接口
/*asynTrace由asynManager实现*/
/*所有asynTrace方法可以被从任何线程调用*/
/* traceMask definitions*/
#define ASYN_TRACE_ERROR 0x0001
#define ASYN_TRACEIO_DEVICE 0x0002
#define ASYN_TRACEIO_FILTER 0x0004
#define ASYN_TRACEIO_DRIVER 0x0008
#define ASYN_TRACE_FLOW 0x0010
#define ASYN_TRACE_WARNING 0x0020
/* traceIO mask definitions*/
#define ASYN_TRACEIO_NODATA 0x0000
#define ASYN_TRACEIO_ASCII 0x0001
#define ASYN_TRACEIO_ESCAPE 0x0002
#define ASYN_TRACEIO_HEX 0x0004
/* traceInfo mask definitions*/
#define ASYN_TRACEINFO_TIME 0x0001
#define ASYN_TRACEINFO_PORT 0x0002
#define ASYN_TRACEINFO_SOURCE 0x0004
#define ASYN_TRACEINFO_THREAD 0x0008
/* asynPrint and asynPrintIO are macros that act like
int asynPrintSource(asynUser *pasynUser,int reason, __FILE__, __LINE__, const char *format, ... );
int asynPrintIOSource(asynUser *pasynUser,int reason,
const char *buffer, size_t len, __FILE__, __LINE__, const char *format, ... );
*/
typedef struct asynTrace {
/* lock/unlock are only necessary if caller performs I/O other than */
/* by calling asynTrace methods */
asynStatus (*lock)(asynUser *pasynUser);
asynStatus (*unlock)(asynUser *pasynUser);
asynStatus (*setTraceMask)(asynUser *pasynUser,int mask);
int (*getTraceMask)(asynUser *pasynUser);
asynStatus (*setTraceIOMask)(asynUser *pasynUser,int mask);
int (*getTraceIOMask)(asynUser *pasynUser);
asynStatus (*setTraceInfoMask)(asynUser *pasynUser,int mask);
int (*getTraceInfoMask)(asynUser *pasynUser);
asynStatus (*setTraceFile)(asynUser *pasynUser,FILE *fp);
FILE *(*getTraceFile)(asynUser *pasynUser);
asynStatus (*setTraceIOTruncateSize)(asynUser *pasynUser,size_t size);
size_t (*getTraceIOTruncateSize)(asynUser *pasynUser);
#if defined(__GNUC__) && (__GNUC__ < 3)
/* GCC 2.95 does not allow EPICS_PRINTF_STYLE on function pointers */
int (*print)(asynUser *pasynUser,int reason, const char *pformat, ...);
int (*printSource)(asynUser *pasynUser,int reason, const char *fileName, int line, const char *pformat, ...);
int (*vprint)(asynUser *pasynUser,int reason, const char *pformat, va_list pvar);
int (*vprintSource)(asynUser *pasynUser,int reason, const char *file, int line, const char *pformat, va_list pvar);
int (*printIO)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *pformat, ...);
int (*printIOSource)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *file, int line, const char *pformat, ...);
int (*vprintIO)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *pformat, va_list pvar);
int (*vprintIOSource)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *file, int line, const char *pformat, va_list pvar);
#else
int (*print)(asynUser *pasynUser,int reason, const char *pformat, ...) EPICS_PRINTF_STYLE(3,4);
int (*printSource)(asynUser *pasynUser,int reason, const char *fileName, int line, const char *pformat, ...) EPICS_PRINTF_STYLE(5,6);
int (*vprint)(asynUser *pasynUser,int reason, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(3,0);
int (*vprintSource)(asynUser *pasynUser,int reason, const char *file, int line, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(5,0);
int (*printIO)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *pformat, ...) EPICS_PRINTF_STYLE(5,6);
int (*printIOSource)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *file, int line, const char *pformat, ...) EPICS_PRINTF_STYLE(7,8);
int (*vprintIO)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(5,0);
int (*vprintIOSource)(asynUser *pasynUser,int reason,
const char *buffer, size_t len,const char *file, int line, const char *pformat, va_list pvar) EPICS_PRINTF_STYLE(7,0);
#endif
}asynTrace;
epicsShareExtern asynTrace *pasynTrace;
asynTrace
asynDriver提供了具有以下属性的跟踪功能:
1)对单独设备,即一个portName, addr启用和关闭跟踪。
2)跟踪有一个用于未连接port或port,addr的asynUser的全局跟踪掩码。
3)输出被发送给一个文件或者stdout或者到errlog。
4)掩码确定要被显示的信息类型。不同选项可以被位或在一起。在创建一个端口时,这个掩码的默认值是ASYN_TRACE_ERROR。
- ASYN_TRACE_ERROR:报告运行时错误,例如超时。
- ASYN_TRACEIO_DEVICE:设备支持报告I/O活动。
- ASYN_TRACEIO_FILTER:在设备支持和底层驱动之间的任何层报告它对I/O做的任何过滤。
- ASYN_TRACE_LOW:报告逻辑流。设备支持应该报告所有队列请求,进入的回调,以及对驱动的所有调用。设备支持和底层驱动之间的层应该它们对底层驱动做的所有调用。底层驱动报告它们对其它支持的调用。
- ASYN_TRACE_WARNING:报告警告,即,在ASYN_TRACE_ERROR和ASYN_TRACE_LOW之间的情况。
5)另一个掩码确定如何打印消息缓存。各种选项可以按位或到一起。当创建这个一个端口时,这个掩码的默认值是ASYN_TRACEIO_NODATA。
- ASYN_TRACEIO_NODATA:不要输出来自消息缓存的任何数据。
- ASYN_TRACEIO_ASCII:用"%s"风格格式打印。
- ASYN_TRACEIO_ESCAPE:调用epicsStrPrintEscaped
- ASYN_TRACEIO_HEX:用"%2.2x"打印每个字节。
6) 另一个掩码确定在每条消息开始输出什么信息。各种选项可以按位或到一起。当创建一个端口时,这个掩码的默认值是ASYN_TRACEINFO_TIME。
- ASYN_TRACEINFO_TIME:打印消息的日期和时间。
- ASYN_TRACEINFO_PORT:打印[port, add, reason],此处port是端口名,addr是asyn地址,而reason是pasynUser->reason。这些是asyn中3段"寻址"信息。
- ASYN_TRACEINFO_SOURCE:打印asynPrint或asynPrintIO语句出现位置的文件名和文件编号,即[__FILE__,__LINE__]。
- ASYN_TRACEINFO_THREAD:打印线程名,线程ID和线程优先级,即[epicsThreadGetNameSelf(), epicsThreadGetIdSelf(), epicsThreadGetPriortitySelf()]。
为了跟踪功能正常执行:设备支持和所有驱动必须使用这个跟踪功能。设备和驱动支持可以直接调用asynTrace方法。提供了asynPrint和asynPrintIO宏,使得设备/驱动支持可以有像这样的调用:
asynPrint(pasynUser,ASYN_TRACE_FLOW,"%s Calling queueRequest\n",someName);
为了发出读或写请求的设备支持或驱动设计了asynPrintIO调用。它们像这样进行调用:
asynPrintIO(pasynUser,ASYN_TRACEIO_DRIVER,data,nchars,"%s nchars %d", someName,nchars);
由asynManager实现了asynTrace方法。这些方法可以被已经创建一个asynUser并且连接了一个设备的任何代码使用。所有方法都可以被任何线程调用。即是,一个应用线程和/或一个portThread。如果一个线程通过调用print或printIO执行所有I/O,则它不需要调用lock或unlock。如果它想要进行它自己的I/O,在任何I/O前,它必须锁定,在之后,解锁。例如:
pasynTrace->lock(pasynUser);
fd = pasynTrace->getTraceFile(pasynUser);
/*perform I/O to fd */
pasynTrace->unlock(pasynUser);
如果asynUser未连接一个端口,即,还未调用pasynManger->connectDevice,则认为一个"全局"设备。当在connectDevice前调用asynPrint,这是有用的。
asynTrace
lock/unlock | 这是调用asynTrace.print或asynTrace.printIO而不是asynPrint和asynPrintIO的代码才需要的。print和printIO在执行它们操作时都锁定。get方法不锁定(除了getTraceFile)并且它们是安全的。除了setTraceFile外,set方法不阻塞,由于会发生的最坏是用户gets多了一点或输出少了一点。 |
。setTraceMask | 设置跟踪掩码。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。为一个端口设置跟踪掩码也为连接那个端口的所有设备设置跟踪掩码。 |
getTraceMask | 获取跟踪掩码。想要发送跟踪消息的设备支持调用这个方法查看已经请求了什么跟踪选项。 |
setTraceIOMask | 设置traceIO掩码。通过由用户设置,通过shell命令或者devTrace设备支持请求它。为一个端口设置traceIO掩码,也为连接那个端口的所有设备设置了traceIO掩码 |
getTraceIOMask | 获取traceIO掩码。想要发送其自己IO消息而不是调用asynPrintIO的支持应该调用这个方法并且遵守这个掩码设置。大部分代码需要它。 |
getTraeInfoMask | 设置traceInfo掩码。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。为一个端口设置traceInfo掩码,也为所有连接那个端口的设备设置了traceInfo掩码。 |
setTraceFile | 为输出设置要使用的流。一个NULL参数表示使用errlog。通常由用户设置,通过一个shell命令或者通过devTrace设备支持请求它。如果当前输出流不是(NULL, stdout, stderr)中一个,则在使用新流前,关闭当前的输出流。 |
getTraceInfo | 获取用于输出的文件描述符。想要发出它自己IO消息而不是调用asynPrintIO的设备支持调用这个方法并且遵守掩码设置。在这种情况下,lock必须首先被调用。大部分代码不需要它。如果返回值是0,则输出应该指向errlog。 |
setTraceIOTruncateSize | 确定printIO输出多少数据。在所有情况中,它确定了显示缓存的多少字节。输出的实际字节数取决于traceIO掩码。例如,ASYN_TRACE_IO_HEX导致为每个字节输出3个字符。通常由用户设置,通过一个shell命令或者devTrace设备支持请求它。 |
getTraceIOTruncateSize | 获取当前截断大小。由asynPrintIO调用。进行其自身I/O的代码页应该支持traceIO掩码。 |
如果reason与当前traceMask按位或到一起非0。为了后向兼容性提供了这个方法。asynPrint宏现在调用printSource()。 | |
printSource | 如果与当前traceMask按位或在一起不为0,则打印消息。大部分代码应该调用asynPrint来替代调用者而换个方法。这个方法与print()相同,但多了file和line参数。 |
vprint | 这与print相同,但使用va_list作为它的最终参数。 |
vprintSouce | 与pirntSource相同,但使用va_list作为其最终参数。 |
printIO | 如果reason与当前traceMask按位或是非0,输出消息。如果len>0,则使用traceIO掩码和getTraceIOTruncateSize打印缓存。为了后向兼容性提供了这个方法。asynPrintIO宏现在调用pirntIOSource() |
printIOSource | 如果reason与当前traceMask按位或是非0,输出消息。如果len>0,则使用traceIO掩码和getTraceTruncateSize输出缓存。大部分代码应该调用asynPrintIO而不是调用这个方法。这个方法与printIO相同,但多了file和line参数。 |
vprintIO | 这与printIO相同,但使用一个va_list作为其最终参数 |
vprintIOSource | 这与printIOSource相同,但使用一个va_list作为其最终参数。 |