VxWorks动态加载

使用动态加载目标模块的方式有很多好处,比如可以在不破坏原来的环境下增加调试定位功能,相当于给系统打“补丁”,不需要编译原来的代码(甚至可以不用原来的代码)而只需要关注正在调试的代码,这样能减少编译时间和减少映像的加载量。

实现目标模块的动态加载有很多种方法,如在主机环境的界面上通过在目标模块上单击鼠标右键,选择“Download 文件名”;也可以通过wShell和GDB命令行窗口实现。本文通过tshell下使用ld()、loadModule()、loadModuleAt()中一个函数来实现,当然在代码中也可以自如地调用它们。

ld命令是由用户接口子程序库usrLib提供的一个加载命令。使用ld的前提是在config.h中定义INCLUDE_LOADER。这样,在usrRoot()函数中就会自动调用加载模块初始化函数moduleLibInit();同时,根据CPU类型,自动决定目标模块的格式。如果CPU是MIPS、PPC、ARM、I80X86、COLDFIRE、SIMSPARCSOLARIS、SH等,加载的目标模块格式是elf类型,就会调用loadElfInit();如果CPU是I960、AM29XXX等,加载的目标模块的格式则是coff类型,就会调用loadCoffInit()函数。

在ARM和PPC下,Tornado编译器生成的.o或.out都是elf类型,打开目标文件都会看到文件头有ELF(45,4C,46)标记。这时可以通过ftp工具把它加载到文件系统(如使用copy命令加载到RAM盘)中,再调用ld()或loadModule()函数加载到内存中运行。

ld的函数原型是:MODULE_ID ld( int syms, BOOL noAbort, char *name )。参数syms决定目标文件的符号怎么处理:0,添加全局符号到系统符号表中;1,添加全局和局部符号到系统符号表中;-1,符号不添加到系统符号表中。一般选1,便于在shell下使用其中的符号。参数noAbort表明是否可以忽略加载期间出现的错误,为TRUE则忽略,FALSE则不忽略。name则为加载的文件名,包含文件路径。注意:ld是一条shell命令,也就是它是为在shell下调用而设计的一个函数,所以尽量不要用在代码内部,因为在之后的vxworks版本中直接调用ld可能会不支持。

loadModule的函数原型是:MODULE_ID loadModule( int fd, int loadFlag )。fd为文件描述符号,需要先打开文件获取fd;参数loadFlag含义有LOAD_NO_SYMBOLS(2)、LOAD_LOCAL_SYMBOLS(4)、LOAD_GLOBAL_SYMBOLS(8)、LOAD_ALL_SYMBOLS(0xC)三种。

在VxWorks下如何动态加载.out文件,下面是实际的代码,供参考:

 
 
//Device.cpp 
#include "other.h" 
#ifdef __cplusplus 
extern "C" { 
#endif 
 int initDevice(char *arg); 
#ifdef __cplusplus 
} 
#endif 
int initDevice(char *arg) 
{ 
 printf("%s\n", arg); 
} 
生成的.out文件需对其使用如下命令 
 chmod.exe a+rx Device.out 
int dynLoadOut( ) 
{ 
 char szDeviceOutPath[128] = "/ata0a/App/Device.out"; 
 for (int j = 0; j < 3; j++) 
 { 
    int fdX = open (szDeviceOutPath, O_RDONLY, 0644); 
     
    if (fdX == ERROR ) 
    { 
    printf("openfile error:%s\n", szDeviceOutPath); 
    taskDelay(1000); 
    continue; 
    } 
    else 
    {  
    MODULE_ID modID = loadModule (fdX, LOAD_ALL_SYMBOLS); 
    close (fdX); 
    if (modID == 0) 
    { 
    printf("loadModule error\n"); 
    return 1; 
    } 
    break; 
    } 
 } 
 printf("loadModule ok\n"); 
 extern SYMTAB_ID sysSymTbl; 
 FUNCPTR deviceEntry = 0; 
 SYM_TYPE type; 
 STATUS stus = symFindByName(sysSymTbl, "initDevice", (char**)&deviceEntry, &type); 
 if (stus == ERROR) 
 { 
    printf("symFindByName error\n"); 
    return 1; 
 } 
 else 
 { 
    printf("deviceEntry = 0x%x, type = %d\n", (int)deviceEntry, (int)type); 
    char szPara[128] = "have a test!" 
    (*deviceEntry)(szPara); 
 } 
} 
 

1.任务队列

vxworks维护4个队列:

  • tick队列
  • ready队列
  • active队列
  • pend队列

一,tick队列

当执行taskDelay时,任务会被延长一段时间执行,此时,任务就会被加入到tick队列中,任务处于Delay状态,无权竞争CPU;

二,ready队列

有资格竞争CPU的队列,按照任务的优先级进行排队,队列头部是优先级最高的任务;

三,active队列

所有任务无论状态如何都会在这个队列中,这个队列维护着系统当前所有的任务,我们通过“i”可以看到所有的任务就是通过遍历active队列来实现的;

四,pend队列

当多个任务竞争一个资源时,如果资源不可得,任务就会被设置为pend状态,进入pend队列中;

函数taskSpawn创建任务,新建任务会放到active队列,此时还不能竞争CPU,taskSpawn最后一步就是把任务放到ready队列中,就可以竞争CPU了。

任务间通信机制是多任务间相互同步和通信以协调各自活动的主要手段。VxWorks提供的任务间通信手段按其速度由快到慢包括信号量、消息队列、管道到网络透明的套接字。
任务间通信机制
  • 共享内存,数据的简单共享
  • 信号量,基本的互斥和同步
  • 消息队列和管道,同一CPU内多任务间消息传递
  • Sockets,远程调用,任务间透明的网络通信
  • Signals,用于异常处理
共享数据结构

任务共存于单一的线性地址空间。任一程序中定义的全局变量,都可以被所用任务直接访问。为了方便编程,自身定义几种数据类型:线性缓冲、环形缓冲、连接链等。可以被运行在不同上下文的代码引用。

连接链是一种双向连接的数据结构,定义在\target\h\lstLib.h中。

环形缓冲定义在\target\h\rngLib.h中。环形缓冲在用于任务和中断服务程序间传送字符非常有用。环形缓冲大小固定,以先进先出方式工作。

需要考虑互斥问题。仅仅服务于一个读任务和写任务时,不需要用信号量来控制对环形缓冲的访问。当读取任务读取下一个节点时,该操作将使该节点变为空,使得节点对新数据可用。如果读任务不能跟上写任务的速度,缓冲区将会溢出,应用数据可能丢失。这种情形需要较大的环形缓冲。

互斥

当一个共享地址空间简单地用于交换数据时,为避免竞争,需要对该内存的访问上锁。互斥方法包括:禁止中断,禁止抢占,和使用信号量对资源上锁。

中断上锁:intLock(); intUnlock(lock); 之间是不能被中断的临界区。

抢占上锁:taskLock(); taskUnlock(); 不能被中断的临界区;

中断上锁时间与控制抢占禁止时间尽可能短;

一种更好的机制是信号量。

VxWorks信号量是提供任务间通信、同步和互斥的最优选择,它提供任务间的最快通信,也是提供任务间同步和互斥的主要手段。

对于互斥,信号量可以上锁对共享资源的访问,并且比禁止中断或禁止抢占提供更精确的互斥粒度。

三种类型的信号量:

关于串口编程,在各大操作系统下的流程基本是一致的,只是针对不同的操作系统,函数接口可能有所差异而已,下面讲述VxWorks操作系统下对于串口编程步骤和代码:

  1. 打开串口
  2. 设置串口raw模式,清空输入输出的缓冲区
  3. 设置波特率,数据位,停止位,校验方式
  4. 读和写
打开串口:

fd = open("/tyCo/0", O_RDWR, 0);

"/tyCo/0" 串口1的设备名,O_RDWR:open for reading and writing

设置串口raw模式,清空输入输出的缓冲区

ioctl(fd,FIOSETOPTIONS,OPT_RAW);
ioctl(fd,FIOFLUSH,0);
ioctl(int fd,int function,int arg);

ioctl这个函数解释如下:

function这个参数有如下:(tty)

FIOBAUDRATE 设置波特率,arg为一整数,表示要设定的波特率
FIOGETOPTIONS 取得设备控制字,arg表示读出的内容存放的位置
FIOSETOPTIONS 设置设备控制字,arg表示要设置的选项
FIOGETNAME 取得文件描述符对应的文件名,arg存放文件名的缓冲区
FIOREAD 取得输入缓冲区内未读取的字符数,arg用于接收结果的整型指针
FIOWRITE 取得输出缓冲区内的字符个数,arg用于接收结果的整型指针
FIOFLUSH 清空输入输出缓冲区的字符
FIOCANCEL 取消读和写

FIOSETOPTIONS对应的arg有OPT_LINE,OPT_RAW,OPT_ECHO等等

关于这些的定义,可以在sioLib.h,ioLib.h里寻找。

设置波特率,数据位,停止位,校验方式
  1. STOPB:两位停止位,默认是1位停止位
  2. PARENB:使能校验,未使能则是无校验
  3. PARODD:奇校验,使能后默认是偶校验
  4. CS5,CS6,CS7,CS8: 5,6,7,8位数据位