嵌入式操作系统

VxWorks笔记整理

一、开篇

1、实时操作系统:

​ 硬实时系统:能够在指定期限内完成任务(即使在最坏的处理条件下也能如此)的操作系统;

​ 软实时系统:统计意义上的实时。

2、优先级反转:

​ 一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,而这个低优先级任务在访问共享资源时可能又被其它一些中等优先级任务抢先,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。

任务优先级反转解决方法:

  • 优先级天花板:当任务申请某资源时, 把该任务的优先级提升到可访问这个资源的所有任务中的最高优先级。

  • 优先级继承:任务 t1 申请共享资源 S 时, 如果 S 正在被任务 t3 使用,通过比较任务 t3 与 t1的优先级,如发现任务 t3 的优先级小于 t1 的优先级, 则将任务 t3 的优先级提升到 t1 的优先级,等 t3 释放资源 S 后,再恢复任务 t3 的原优先级。

二、VxWorks实时任务调度

​ 每个任务都有一个TCB(Task Control Block)任务控制块,内核用来管理任务调度功能。

任务状态:Pend、Delay、Suspended、Ready;

1、任务上下文:

​ 任务上下文的切换发生在一个任务停止,另一个任务开始的时候。保存当前任务的内容到TCB中。恢复要执行任务的TCB。上下文切换一定要快。

2、任务调度策略:
  1. 基于优先级的抢占式调度:不同优先级的任务;创建任务时指定优先级 taskSpawn、任务创建后设置优先级 taskPrioritySet。
  2. 罗宾调度(时间轮转调度):相同优先级的任务;KernelTimeSlice设置时间片的长度,以tick为单位。
3、创建任务:
taskSpawn("sendTest", 100, 0, 5*1024, (FUNCPTR)sendTestTask, portNo,0,0,0,0,0,0,0,0,0);
  //      (任务名称,优先级,任务属性,栈大小,  入口函数指针,          参数一)
4、堆栈:
  1. 堆:是为动态分配预留的内存空间;由开发人员分配和释放。

  2. 栈:栈是为执行线程留出的内存空间;栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。

三、VxWorks信号量

1、信号量的分类:
序号名称特点
1二进制信号量满足任务间同步与互斥,开销最小
2互斥信号量优化后的二进制信号量,用来解决互斥固有问题(优先级翻转)
3计数信号量记录有效的资源数目,监视同一资源上的多个实例
2、同步和互斥:
  • 同步即任务按照一定顺序先后执行,如任务A执行完成之后才能执行任务B;
  • 互斥是指多任务在访问临界资源时具有排他性。为使多个任务互斥访问临界资源,只需要为该资源设置一个信号量,相当于一个令牌,哪个任务拿到这个令牌即有权使用该资源。
3、信号量相关的函数:
序号函数功能
1semBCreate创建并初始化一个二进制信号量
2semMCreate创建并初始化一个互斥信号量
3semCCreate创建并初始化一个计数信号量
4semTake等待信号量
5semGive释放信号量
6semDelete删除一个信号量
7semFlush唤醒阻塞在某信号量上的所有任务
4、二进制信号量实行同步和互斥:
1、同步:
  • 初始状态设为不可用;semIdMu = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
  • 在不同任务中分别单独调用semTakesemGive

典型用法:将中断服务程序从冗长的事件处理中解放出来以缩短中断响应时间。

SEM_ID semIdSyn = NULL;
void TaskA()
{
	semGive(semIdSyn); // 释放信号量
}
void TaskB()
{
	semTake(semIdSyn, WAIT_FOREVER); 
}
void TaskSyn()
{
	semIdSyn = semBCreate(SEM_Q_FIFO, SEM_EMPTY); // 创建二进制信号量,初始状态为 empty;
    taskSpawn("TaskA",120,0,5*1024,(FUNCPTR)TaskA,0,0,0,0,0,0,0,0,0,0);
    taskSpawn("TaskB",120,0,5*1024,(FUNCPTR)TaskB,0,0,0,0,0,0,0,0,0,0);
}

2、互斥:
  • 初始状态设为可用;semIdMu = semBCreate(SEM_Q_FIFO, SEM_FULL);
  • 在同一个任务中,成对调用semTakesemGive

典型用法:对多个任务共享内存缓冲区或同一个I/O设备之类的资源进行保护。

void TaskA()
{
    semTake(semIdMu, WAIT_FOREVER); // 等待信号量被释放
    semGive(semIdMu); // 释放信号量
}
void TaskB()
{
    semTake(semIdMu, WAIT_FOREVER);
    semGive(semIdMu); // 释放信号量
}
void TaskMu()
{
	semIdMu = semBCreate(SEM_Q_FIFO, SEM_FULL); // 创建二进制信号量,初始状态为 FULL
	//semIdMu = semMCreate(SEM_Q_PRIORITY);//采用互斥信号量实现
    taskSpawn("TaskA",110,0,5*1024,(FUNCPTR)TaskA,0,0,0,0,0,0,0,0,0,0);
    taskSpawn("TaskB",120,0,5*1024,(FUNCPTR)TaskB,0,0,0,0,0,0,0,0,0,0);
}
3、信号量属性:
信号量属性作用
SEM_DELETE_SAFE保护拥有该信号量的任务不被意外删除。此选项为每个semtake启用隐式任务Safe,并为每个semGive启用隐式任务taskUnsafe
SEM_INVERSION_SAFE使用此选项,拥有该信号量的任务将以挂起在该信号量上的所有任务的最高优先级执行。该选项必须伴随着SEM_Q_PRIORITY队列模式

在由信号量保护的关键区域内,通常需要保护正在执行的任务被意外删除。删除在关键区域执行的任务可能是灾难性的

内在互斥问题: 优先级继承、删除安全、对资源的递归访问

优先级继承拥有该信号量的任务将以挂起在该信号量上的所有任务的最高优先级执行。该选项必须伴随着SEM_Q_PRIORITY队列模式
删除安全保护拥有该信号量的任务不被意外删除。此选项为每个semtake启用隐式任务Safe,并为每个semGive启用隐式任务taskUnsafe
对资源的递归访问一个互斥信号量可以被拥有它的任务反复进行semTake而不引起该任务的阻塞。一个占有互斥信号量的任务,若要释放掉互斥信号量,则必须执行与semTake同样次数的semGive操作

四、VxWorks共享内存、消息队列和管道

1、任务间通信:
多任务系统的任务间有通信需求。
1、任务间通信由三个部分组成:
  • 共享的信息或者数据;

  • 数据可用时的任务通知机制;

  • 防止任务间相互干扰机制;

2、两个通用的方法:
  • 共享内存:共享内存通常与信号量一起使用。

    tTaskA通过调用fooSet()设置全局结构fooBuffer

    tTaskB通过调用fooGet()获得全局结构fooBuffer的值。

    共享内存通常与信号量一起使用。

  • 消息传递:vxWorks使用管道(pipe)与消息队列(message queue)进行任务间消息传递。

    管道(pipe)与消息队列(message queue)都提供:消息的FIFO缓存、同步、互斥。

2、创建消息队列:
MSG_Q_ID msgQCreate (maxMsgs, maxMsgLength, options)    //返回引用消息队列的ID或者NULL
    
//maxMsgs:最大消息数   maxMsgLength :单个消息的最大字节数。
//options : 对挂起任务的消息类型。MSG_Q_FIFO 或者MSG_Q_PRIORITY 
3、发送消息:
STATUS msgQSend (msgQId, buffer, nBytes,timeout,priority)
msgQIdmsgQCreate返回的IDbuffer要放入队列的消息地址
nBytes消息大小timeout消息队列是满的时候最大等待时间(ticks),及WAIT_FOREVER,NO_WAIT
priority消息优先级。 MSG_PRI_URGENT将放队前, MSG_PRI_NORMAL将放队尾
  • 当队列满时,发送任务将挂起;
  • 使用NO_WAIT超时从中断级别发送;
  • 成功返回 OK,无效msgId等错误返回 ERROR 。
4、接收消息:
msgQReceive (msgQId, buffer,maxNBytes, timeout) //WAIT_FOREVER,NO_WAIT
  • 成功返回读取的字节数,失败返回 ERROR;
  • 消息未被读取部分将被丢弃;
  • 没有消息,读取任务将被挂起,直到超时;
  • 挂起任务将按优先级或者 FIFO 等待;
  • msgQReceive至少返回第一个消息的字节数;
5、删除消息队列:
STATUS msgQDelete (msgQId)
  • 删除消息队列;
  • 挂起在消息队列的任务将进入READYmsgQReveive()msgQsend()将返回ERROR

使用消息队列获取数据:

  • 用一个轮询任务或者 ISR 从设备取数据放入消息队列;
  • 低优先级任务从队列取数据处理。

五、事件与信号

1、事件介绍:
  • 事件提供了任务与任务,ISR与任务,信号量与任务,消息队列与任务之间同步的方法;
  • 事件可以由ISR、任务、消息队列发送、释放信号量操作发送事件;
  • VXWORKS提供32个事件,event25-event31为windriver使用;
  • 事件提供了一种无需分配额外系统资源即可协调复杂活动矩阵的方法。
2、时间标志与事件寄存器:
  • 每个任务控制块都有一个事件寄存器,用于存储来发到任务的事件;
  • 事件寄存器不能访问,需要由eventReceive拷贝进去;
  • 没有机制记录收到过事件的次数;
  • 事件标志是一个32位编码字,前24bit可以用户自定义;
  • 每个flag没有明确意思,由任务自行决定如何响应。
3、消息队列事件:
  • 当消息队列为空且没任务阻塞在队列上时,可以发送事件;
  • 消息队列事件没有机制计算队列中消息数量
  • 一个任务可以注册到多个消息队列上;
  • 一个消息队列一次只能被一个任务注册。

六、VxWorks中断、异常与定时

中断可以抢占优先级最高的任务。

1、ISR限制:
  1. ISR中不能使用vxWorks部分功能,特别是部分不能阻塞的功能:
    • 不能调用semTake()
    • 不能调用malloc(),因为内部使用了信号量;
    • 不能调用系统IO,如printf
  2. 可以在ISR中调用的一个操作是向管道中write()
2、典型ISR:
  • 读写内存映射的寄存器;
  • 与任务进行通信:
    • 写内存;
    • 以非阻塞的方式写消息到消息队列;
    • 释放二进制信号量。
3、调试ISR:
  • 将信息输出到控制台:logMsg (“foo = %d\n”, foo, 0, 0, 0, 0, 0);

    向任务tLogTask发送请求,由该任务执行printf

  • 使用系统级调试。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值