关于VxWorks系统调用,有没有封装好的C++库

关于VxWorks系统调用,有没有封装好的C++库,第1张

VxWorks下采用C++构建Application可以使得程序更加利于维护,利用其提供的STL支持,可以省去大量的底层工作,大大加速软件的开发进度。

1    异常类VxError

首先封装异常类VxError,当程序出现异常时,向外层调用者抛出一个该类的对象,调用者采用try-catch clause捕获该异常对象,进行异常处理。

由于该类针对的是系统运行时产生的异常,故考虑由C++标准异常类中的runtime_error类派生;VxWorks内核采用设置全局变量errno的方式记录系统运行中产生的错误,所以将int  errNum 作为该类的成员变量,用以记录异常发生时的errno值。源代码如下:

#include <stdexcept>

#include "errnoLib.h"

 

class VxRunTimeError : public runtime_error {

protected:

        int errNum

public:

        VxRunTimeError(const string msg = "") : runtime_error(msg), errNum(errnoGet ())

        {

        }

        int getErrNum(void)

        {

              return errNum

        }

}

2    任务类VxTask

任务类VxTask用以封装VxWorks的task,本来考虑将任务的入口函数作为该类的纯虚成员函数,用户只要在该类的派生类中重载该纯虚函数就能实现自己的VxWorks task,但由于taskSpawn()的入口函数的参数类型是FUNCPTR(typedef  int            (*FUNCPTR) (...)),而指向VxTask类成员函数的指针类型为int (VxTask:: *ptr)(…),编译器不支持这两种类型之间的强制类型转换,所以只能换种思路——用一个名为Runnable的类专门封装task的入口函数,同时提供一个静态成员函数完成对该入口函数的调用,而该静态成员函数地址可以转换成FUNCPTR类型,供taskSpawn()调用。在VxTask类中实现关于task的系统调用。部分源代码如下:

class Runnable {

protected:

        virtual void run() = 0

public:

        static void entry(Runnable * Run)

        {

                Run->run()

        }

        virtual ~Runnable()

        {

        }

}

 

class VxTask {

protected:

        char *name

        int tid

public:

        VxTask(char* Name, int Arg1 , FUNCPTR Entry = (FUNCPTR)Runnable::entry, int Pri = 150, int Opt = VX_FP_TASK, int StackSize = 2000000,

               int Arg2 = 0, int Arg3 = 0, int Arg4 = 0, int Arg5 = 0, int Arg6 = 0, int Arg7 = 0, int Arg8 = 0, int Arg9 = 0, int Arg10 = 0) : name(Name)

        {

                if(Entry == NULL) {

                        throw(VxRunTimeError("Task Creat Fail: Entry Can't be NULL!"))

                }

               

tid=taskSpawn(Name,Pri,Opt,StackSize,Entry,Arg1,Arg2, Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10)

                if(tid == ERROR) {

                        throw(VxRunTimeError("Task Spawn Fail!"))

                }

        }

        ~VxTask()

        {      

                if(taskDelete(tid) == ERROR) {

                        throw(VxRunTimeError("Task Delete Error: Task delete fail!"))

                }

        }

        void suspend()

        {

                if(taskSuspend(tid) == ERROR) {

                        throw(VxRunTimeError("Task Suspend Error: Task suspend fail!"))

                }

 

        }

        void resume()

        {

                if(taskResume(tid) == ERROR) {

                        throw(VxRunTimeError("Task Resume Error: Task resume fail!"))

                }

        }

        int getTid()

        {

                return tid

        }

}

使用时首先派生Runnable的子类,重载其run()成员函数,然后将该子类的对象指针赋给VxTask的构造函数,用户task就跑起来了:

class MyRunnable : public Runnable

{

        void run() {

                while(1) {

                        cout<<"Hello VxWorks Task World!"<<endl

                        taskDelay(sysClkRateGet())

                }

        }

}

 

void myMain()

{

              MyRunnable myRun

VxTask task(“tMyRun”, (int)&myRun)

While(1) {

              taskDelay(sysClkRateGet())

}

}

在shell中sp myMain可以看到预期效果,但如果myMain()中去掉最后的while(1)循环,就只会在输出窗口中看一次Hello VxWorks Task World!输出。Why?(提示:VxTask的析构函数!)

3    中断类VxInt

中断类VxInt与VxTask类似,同样用Runnable的派生类封装入口函数,VxInt类实现中断系统调用:

typedef  void (**VOIDFUNCPTRPTR) (...)

 

class VxInt {

protected:

        int intNum

public:

VxInt(int IntNum, int Arg = 0, VOIDFUNCPTR Entry = (VOIDFUNCPTR)Runnable::entry) : intNum(IntNum)

        {

          if(intConnect((VOIDFUNCPTRPTR)INUM_TO_IVEC(intNum), Entry, Arg) == ERROR) {

                        throw(VxRunTimeError("Interrupt Connect Fail!"))

                }

        }

}

与task不同,中断服务程序(ISR)中不能调用可能被阻塞的函数,这点需要在重载Runnable派生类中的run()成员函数时引起注意。

4    看门狗类 VxWatchDog

VxWorks中的看门狗实际上是利用系统时钟中断来定时执行某个函数的,所以被看门狗执行的函数是运行在中断的上下文(Context)中,而不是任务的上下文中,故该函数中也不能调用带有阻塞功能的函数。所以VxWatchDog的实现与中断类VxInt类似:

class VxWatchDog {

        WDOG_ID id

        int delay

public:

        VxWatchDog(int Delay, Runnable *EntryObj) : delay(Delay)

        {

                id = wdCreate()

                if(id == NULL) {

                        throw(VxRunTimeError("Watch Dog Creat Fail!"))

                }

 

                if(wdStart(id, delay, (FUNCPTR)Runnable::entry,  (int)EntryObj) != OK) {

                        throw(VxRunTimeError("Watch Dog Start Fail!"))

                }

        }

        void cancel()

        {

                if(wdCancel(id) != OK) {

                        throw(VxRunTimeError("Watch Dog Cancel Fail!"))

                }

        }

 

        WDOG_ID getId()

        {

                return(id)

        }

 

        ~VxWatchDog()

        {

                if(wdDelete(id) != OK) {

                        throw(VxRunTimeError("Watch Dog Delete Fail!"))

                }

        }

}

wdStart(WDOG_ID Id, int Delay, FUNCPTR Ptr, int Para )只会让函数Ptr在延时Delay ticks后执行一次,要周期性地执行Ptr,需要在Ptr中递归调用wdStart()。那能否这样实现呢:

class WdRun : public Runnable {

protected:

        void run() {

                logMsg("Hello Watch Dog World!",0,0,0,0,0,0)

                VxWatchDog wtDog(sysClkRateGet(), this)

        }

}

上述程序试图在入口函数中产生一个VxWatchDog类的对象,并用this指针初始化该对象,以期达到每秒钟执行一次入口函数的目的。但是不要忘了该入口函数是运行在中断的上下文中的,不允许动态地产生或删除对象,所以采用这种方法实现周期性执行作业并不可行。

为了在入口函数中调用wdStart(),且要避免动态地生成VxWatchDog对象,需要在Runnable派生类的成员变量中包含一个VxWatchDog指针,通过该指针调用所指对象的wdStart()。为此,需要在VxWatchDog类中增加成员函数:

VxWatchDog ::VxWatchDog(int Delay) : id(wdCreate()), delay(Delay)

{

           if(id == NULL) {

                   throw(VxRunTimeError("Watch Dog Creat Fail!"))

            }

 }

 void VxWatchDog ::start(Runnable *EntryObj)

 {

           if(wdStart(id, delay, (FUNCPTR)Runnable::entry, (int)EntryObj) != OK) {

                        throw(VxRunTimeError("Watch Dog Start Fail!"))

                }

  }

class WdRun : public Runnable {

protected:

        VxWatchDog *dog

        virtual void run() {

                logMsg("Hello Watch Dog World!",0,0,0,0,0,0)

                dog->start(this)

        }

public:

        WdRun(VxWatchDog *Dog) : dog(Dog)

        {

        }

}

void myMain()

{

               VxWatchDog wtDog(sysClkRateGet())

        WdRun run(&wtDog)

        wtDog.start(&run)

        while(1) {

                taskDelay(sysClkRateGet())

                cout<<"In Main!"<<endl

        }

}

     在shell中输入sp myMain,可以看到预期输出。

5    信号量类 VxSem

VxWorks信号量包括互斥信号量、二进制信号量和计数信号量,这三种信号量除了创建时调用各自的创建函数,其它操作具有相同的接口,所以考虑采用VxSem类作为信号量基类,提供统一的信号量操作接口,VxSemM、VxSemB、VxSemC三个派生类分别封装了三种信号量的创建函数:

class VxSem {

protected:

        SEM_ID id

public:

        VxSem(SEM_ID Id) : id(Id)

        {

        }

 

        virtual ~VxSem()

        {

                if(semDelete(id) == ERROR) {

                        throw(VxRunTimeError("Semaphore Delete Fail!"))

                }

        }

 

        void take(int TimeOut = WAIT_FOREVER)

        {

                if(semTake(id,WAIT_FOREVER) == ERROR) {

                        throw(VxRunTimeError("Semaphore Take Fail!"))

                }

        }

        void give()

        {

                if(semGive(id) == ERROR) {

                        throw(VxRunTimeError("Semaphore Give Fail!"))

                }

        }

        void flush()

        {

                if(semFlush(id) == ERROR) {

                        throw(VxRunTimeError("Semaphore Flush Fail!"))

                }

        }

        SEM_ID getId()

        {

                return id

        }

}

 

class VxSemB : public VxSem {

public:

        VxSemB(int Opts = SEM_Q_FIFO, SEM_B_STATE State = SEM_EMPTY) : VxSem(semBCreate (Opts, State))

        {

                if(id == 0) {

                        throw(VxRunTimeError("Binary Semaphore Creat Fail!"))

                }

        }

}

 

class VxSemM : public VxSem {

public:

        VxSemM(int Opts = SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE) : VxSem(semMCreate (Opts))

        {

                if(id == 0) {

                        throw(VxRunTimeError("Mutual-exclusion Semaphore Creat Fail!"))

                }

        }

 

}

 

class VxSemC : public VxSem {

public:

        VxSemC(int Opts, int Cnt) : VxSem(semCCreate (Opts, Cnt))

        {

                if(id == 0) {

                        throw(VxRunTimeError("Counting Semaphore Creat Fail!"))

                }

        }

}

常用函数公式:

1、LEN函数:用于统计一个数据或者一个词出现的次数

使用公式:=LEN(数据),需要进行统计的关键词,主要作用就是计算关键词出现的次数

2、countif函数:统计一个区域的数据中符合一个条件的总数量

使用公式:=countif(区域,条件) 需要注意符号是英文状态,除了字母,都要加一下双引号。

3、vlookup函数:纵向查找

使用公式:=vlookup(G:G,A:B,2) G:G相同的一列,A:B查找范围,2是查找的第几列

如果没有相同值,可以自己创造一个相同值。

4、sumif函数:条件求和

使用公式:=sumif(C:C,”>20”,B:B)

C列条件大于20,b列的和,注意符号使用英文状态下,除了字母都要添加双引号

5、sumifs函数:多条件求和

使用公式:=sumifs(B:B,C:C,”>80”,D:D,”>80”)

注意先写要求和的区域,再写条件

常用的sem数据分析函数,各位小伙伴们学会了吗?可以多多操作,牢牢记住公式,让自己的工作更加轻松,事半功倍。

信号量是包含一个非负整数型的变量,并且带有两个原子操作wait和signal。Wait还可以被称为down、P或lock,signal还可以被称为up、V、unlock或post。在UNIX的API中(POSIX标准)用的是wait和post。

对于wait操作,如果信号量的非负整形变量S大于0,wait就将其减1,如果S等于0,wait就将调用线程阻塞;对于post操作,如果有线程在信号量上阻塞(此时S等于0),post就会解除对某个等待线程的阻塞,使其从wait中返回,如果没有线程阻塞在信号量上,post就将S加1.

由此可见,S可以被理解为一种资源的数量,信号量即是通过控制这种资源的分配来实现互斥和同步的。如果把S设为1,那么信号量即可使多线程并发运行。另外,信号量不仅允许使用者申请和释放资源,而且还允许使用者创造资源,这就赋予了信号量实现同步的功能。可见信号量的功能要比互斥量丰富许多。

POSIX信号量是一个sem_t类型的变量,但POSIX有两种信号量的实现机制: 无名信号量 命名信号量 。无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量;命名信号量通常用于不共享内存的情况下,比如进程间通信。

同时,在创建信号量时,根据信号量取值的不同,POSIX信号量还可以分为:

下面是POSIX信号量函数接口:

信号量的函数都以sem_开头,线程中使用的基本信号函数有4个,他们都声明在头文件semaphore.h中,该头文件定义了用于信号量操作的sem_t类型:

【sem_init函数】:

该函数用于创建信号量,原型如下:

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享,value为sem的初始值。

该函数调用成功返回0,失败返回-1。

【sem_destroy函数】:

该函数用于对用完的信号量进行清理,其原型如下:

成功返回0,失败返回-1。

【sem_wait函数】:

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。其原型如下:

sem指向的对象是sem_init调用初始化的信号量。调用成功返回0,失败返回-1。

sem_trywait()则是sem_wait()的非阻塞版本,当条件不满足时(信号量为0时),该函数直接返回EAGAIN错误而不会阻塞等待。

sem_timedwait()功能与sem_wait()类似,只是在指定的abs_timeout时间内等待,超过时间则直接返回ETIMEDOUT错误。

【sem_post函数】:

该函数用于以原子操作的方式将信号量的值加1,其原型如下:

与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1。

【sem_getvalue函数】:

该函数返回当前信号量的值,通过restrict输出参数返回。如果当前信号量已经上锁(即同步对象不可用),那么返回值为0,或为负数,其绝对值就是等待该信号量解锁的线程数。

【实例1】:

【实例2】:

之所以称为命名信号量,是因为它有一个名字、一个用户ID、一个组ID和权限。这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。

【sem_open函数】:

该函数用于创建或打开一个命名信号量,其原型如下:

参数name是一个标识信号量的字符串。参数oflag用来确定是创建信号量还是连接已有的信号量。

oflag的参数可以为0,O_CREAT或O_EXCL:如果为0,表示打开一个已存在的信号量;如果为O_CREAT,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回,此时mode和value都需要指定;如果为O_CREAT|O_EXCL,表示如果信号量存在则返回错误。

mode参数用于创建信号量时指定信号量的权限位,和open函数一样,包括:S_IRUSR、S_IWUSR、S_IRGRP、S_IWGRP、S_IROTH、S_IWOTH。

value表示创建信号量时,信号量的初始值。

【sem_close函数】:

该函数用于关闭命名信号量:

单个程序可以用sem_close函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序执行之外是具有持久性的。当进程调用_exit、exit、exec或从main返回时,进程打开的命名信号量同样会被关闭。

【sem_unlink函数】:

sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除:

【信号量操作函数】:

与无名信号量一样,操作信号量的函数如下:

命名信号量是随内核持续的。当命名信号量创建后,即使当前没有进程打开某个信号量,它的值依然保持,直到内核重新自举或调用sem_unlink()删除该信号量。

无名信号量的持续性要根据信号量在内存中的位置确定:

很多时候信号量、互斥量和条件变量都可以在某种应用中使用,那这三者的差异有哪些呢?下面列出了这三者之间的差异:


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/220659.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-07
下一篇2023-04-07

发表评论

登录后才能评论

评论列表(0条)

    保存