vxWorks中延后执行任务队列(workQ)

来源:互联网 发布:网络舆情分析师待遇 编辑:程序博客网 时间:2024/05/29 18:35
在多核系统中为每一个cpu设置了一个如下的结构体,分别记录了当前核的运行的任务ID,延后执行任务
队列,延后任务队列是否为空等重要信息。
#define  VX_MAX_SMP_CPUS 4WIND_VARS  _WRS_VX_KERNEL_VARS_SECTION_ATTR vxKernelVars[VX_MAX_SMP_CPUS];

typedef struct _windVars    {    WIND_TCB *cpu_taskIdCurrent;/* 0x00: current task ID */    ISR_IDcpu_isrIdCurrent;/* 0x04: current ISR ID */    intcpu_errno;/* 0x08: errno location (for ISRs) */    intcpu_intCnt;/* 0x0c: interrupt nesting count */    intcpu_windPwrOffState;/* 0x10: power management state */    char *cpu_vxIntStackBase;/* 0x14: interrupt stack base */    char *cpu_vxIntStackEnd;/* 0x18: interrupt stack end */    WORK_Q_IX   cpu_workQIx;            /* 0x1c: read, 0x20: bitmask, */                                        /* 0x24: write */    volatile BOOL   cpu_workQIsEmpty;   /* 0x28: each workQ status */    atomic_t    cpu_spinLockIsrCnt;     /* 0x2c: counting help spinlocks */    atomic_t    cpu_spinLockTaskCnt;    /* 0x30: counting help spinlocks */    WIND_TCB *  cpu_idleTaskId;         /* 0x34: idle task ID */    BOOLcpu_kernelIsIdle;/* 0x38: kernel idle state */    int         cpu_reschedMode;        /* 0x3c: call "reschedule()" or not */    atomicVal_t cpu_my_tkt;/* 0x40: ticket for kernel lock */    char *cpu_reserved1;/* 0x44: Reserved field one    */    char *cpu_reserved2;/* 0x48: Reserved field two   */#ifdef _WRS_WIND_VARS_ARCH    /*     * _WRS_WIND_VARS_ARCH macro-definition can be used for specifying     * architecture an specific fields for WIND_VARS.     */    _WRS_WIND_VARS_ARCH;/* 0x4c: architecture specific */#endif /* _WRS_WIND_VARS_ARCH */    } _WIND_VARS _WRS_DATA_ALIGN_BYTES (_WRS_WIND_VARS_ALIGN);
typedef struct windVars    {    _WIND_VARS vars;    } WIND_VARS;

下面这个结构体构成了对延后任务队列的记录工作:具体原理后面会说到

typedef struct    {    volatile    UINT32 read;/* workQ read index */    UINT32             bitmask; /* workQ index bitmask */    volatile    UINT32 write;   /* workQ write index */    } WORK_Q_IX;
不出所料,这个任务池时全局的,这是一个2维数组,可以分别为每个核记录64个延后执行的任务。
系统每次获取具体的延后执行任务时,就是从这里进行获取的。
#define WIND_JOBS_MAX     64#define VX_MAX_SMP_CPUS   4JOB     pJobPool [WIND_JOBS_MAX][VX_MAX_SMP_CPUS];

typedef struct/* JOB */    {    FUNCPTR function;/* 00: function to invoke */    intarg1;/* 04: argument 1 */    int arg2;/* 08: argument 2 */    UINT isrTag;/* 12: tag of ISR that queued up work */    } JOB;
好,到这里就知道延迟后的任务其实都存储在pJobPool这个任务池之中,但是要获取时的ID号呢就存储在vxKernelVars-->cpu_workQIx之中,就可以了,这样就和每个cpu对应起来了。

再看一下对于这个任务延后执行队列的初始化:
函数调用:
usrInit-->usrKernelInit-->workQInit
初始化都做了什么呢?
1,设置了这个环形队列( WORK_Q_IX)的大小为0x0b
2,设置 WORK_Q_IX->read 为0
3,设置 WORK_Q_IX->write 为0
void workQInit    (    UINT queueSize/* size of the work queue in jobs */    )    {    UINT8 msb;/* most significant bit set in queueSize */    UINT32 bitMask;#ifdef _WRS_CONFIG_SMP    int ix = 0;#endif /* _WRS_CONFIG_SMP *//*这里的两行是计算这个mask值,最后得到的值为0x0b,这里了解这个环形区大小为mask就行,不需要知道具体过程*/    msb = (UINT8) (ffsMsb((UINT32) min (0x10000, queueSize)) - 1);    bitMask = (1 << msb) - 1;    workQWorkInProgress = WORKQ_WORK_NOT_IN_PROGRESS;/*如果是多核,就每个核都进行初始化*/#ifdef _WRS_CONFIG_SMP    for (; ix < _WRS_CPU_CONFIGURED (); ix++)        {#endif /* _WRS_CONFIG_SMP */        _WRS_KERNEL_CPU_GLOBAL_SET (ix, workQIsEmpty, TRUE);WORKQ_IX_SET (ix, bitmask, bitMask);        WORKQ_IX_SET (ix, write, 0);        WORKQ_IX_SET (ix, read, 0);#ifdef _WRS_CONFIG_SMP        }#endif /* _WRS_CONFIG_SMP */    }

OK,初始化完成了下面在看看怎么把任务存放进去,以及怎么获取到。
先展示一下对这个修改这个结构体的宏操作:

/*获取任务*/#define WORKQ_JOB_GET(cpuid, index)\    (*(pJobPool + index) + cpuid)/*设置wirteIx,记录有任务延后执行*/#define WORKQ_IX_SET(cpuid, globMemb, var)\    _WRS_KERNEL_CPU_GLOBAL_SET(cpuid, workQIx.globMemb, var)# define _WRS_KERNEL_CPU_GLOBAL_SET(cpuid, glob, var)\    vxKernelVars[cpuid].vars.cpu_##glob = var/*读取readIx,通过这个得到对应的任务*/#define WORKQ_IX_GET(cpuid, globMemb)\    _WRS_KERNEL_CPU_GLOBAL_GET(cpuid, workQIx.globMemb)# define _WRS_KERNEL_CPU_GLOBAL_GET(cpuid, glob)\    vxKernelVars[cpuid].vars.cpu_##glob
添加任务函数:

void workQAdd0    (    FUNCPTR func        /* function to invoke */    )    {    int     level   = KERNEL_INT_CPU_LOCK();    int    cpuid = _WRS_CPU_INDEX_GET ();    UINT32  writeIx = WORKQ_IX_GET (cpuid, write);    JOB *   pJob    = WORKQ_JOB_GET (cpuid, writeIx);    UINT32  bitMask;    bitMask = WORKQ_IX_GET (cpuid, bitmask);/*这里核mask进行与操作,就是严格按照初始化时的大小来进行设置,以防越过大小*/    writeIx = (writeIx + 1) & bitMask;/*设置对应cpu结构体中的write,就是延后一位,也就是指向下一个为空的位,记录有一个任务延后执行了*/    WORKQ_IX_SET (cpuid, write, writeIx);/*如果write==read说明以及满了*/    if (writeIx == WORKQ_IX_GET(cpuid, read))workQPanic ();                  /* panic while intLocked *//*设置vxKernelVars->workQIsEmpty为false,告诉cpu有任务延后执行*/    _WRS_KERNEL_CPU_GLOBAL_SET (cpuid, workQIsEmpty, FALSE);/*记录任务*/    pJob->function = func;/* fill in function */    WORKQ_ISR_TAG_SET (cpuid, pJob);    }

执行延后任务队列中任务的函数:

/*执行延后执行的任务*/void workQDoWork (void)    {    JOB    *pJob;    int     oldErrno;    UINT32  readIx;    int     cpuid = _WRS_CPU_INDEX_GET ();    int     intCtx;    UINT32  bitMask  = WORKQ_IX_GET (cpuid, bitmask);    intCtx = _WRS_KERNEL_CPU_GLOBAL_GET (cpuid, intCnt);   /*判断是否时中断嵌套*/    if (intCtx){oldErrno = _WRS_KERNEL_CPU_GLOBAL_GET (cpuid, errno);}    else{workQWorkInProgress = cpuid;}/*判断是否已经空了,当read=write时,环形区为空,如果不为空就获取任务,并使read指向下一个任务,并执行取出来的任务,最后设置vxKernelVars->workQIsEmpty为true,告诉cpu这个队列中的任务已经i执行完毕*/    while ((readIx = WORKQ_IX_GET(cpuid, read)) != WORKQ_IX_GET(cpuid, write)){pJob = WORKQ_JOB_GET(cpuid, readIx);         /* get job */readIx = (readIx + 1) & bitMask;WORKQ_IX_SET (cpuid, read, readIx);        (FUNCPTR *)(pJob->function) (pJob->arg1, pJob->arg2);_WRS_KERNEL_CPU_GLOBAL_SET(cpuid, workQIsEmpty, TRUE);}    }
说一说这个记录任务的环形区的使用。
typedef struct
    {
    volatile    UINT32 read;    /* workQ read index */
    UINT32             bitmask; /* workQ index bitmask */
    volatile    UINT32 write;   /* workQ write index */
    } WORK_Q_IX;

这个环形区由三个成员组成,read,write,bitmask,其中read表示要读取的任务的index,write记录写入的任务的index,bitmask就表示这个环形区的大小。
每次插入一个任务时,write都增加1,但是这个值的 大小不会超过bitmask的值,也就是这个write再一个环形区中循环而当write再写入时等于read拉,就说明这个环形区满了;而read呢,每次读取一个任务时,read加1,最后当read==wirte时,也就执行完所有的任务了。
如下面这图所示:read和write都是从0开始,然后递增,大小由bitmask指定。





(时机总结可能不全面)
执行的时机:
1,再调度函数中,选择任务先执行延后执行任务队列中的任务
2,中断返回