Vxworks学习(一)_多任务、任务通信

VxWorks简介

学习Vxworks之前希望读者已经具备计算机网络、操作系统、C/C++、网络通信等基础知识。建议阅读CSAPP,打好基础。
Vxworks为美国风河公司推出的高性能实时操作系统,现已广泛应用于各类大型项目,同时Tplink等公司也应用Vxworks作为某些路由器的操作系统。
实时性在计算机中分为强实时软实时,vx所支持的为强实时,Linux普遍为软实时。
在一个实时操作系统之中,最关注的是每个任务在多长时间内可以完成。简单地说,实时和分时操作系统最大的不同在于 **时限(deadline)**这个概念。它不允许任何超出时限的错误。超时错误会带来损害甚至导致系统失败、或者导致系统不能实现它的预期目标。软实时系统的时限是一个柔性灵活的,它可以容忍偶然的超时错误。失败造成的后果并不严重,例如在网络中仅仅是轻微地降低了系统的吞吐量。
声明本学习记录为笔者的学习记录笔记,其中大量借鉴了多本已出版书籍与网络资料,并会在文章末尾注明出处,同时笔者无虚拟机镜像。

一、多任务

现代实时系统是在多任务和任务间通信的基础上建立起来的。一个多任务的环境允许将实时应用构造成一组独立的任务,每个任务拥有各自的线程和一套系统资源。为了协调任务间的行为,任务间的通信设备允许这些任务通过同步和通信操作协调各自的活动。在VxWorks 操作系统中,任务间通信设备包括信号量、消息队列、管道以及网络套接字等设备。
在实时系统中,处理中断是另一个主要功能,这是因为中断是将外部事件通知系统的重要方式。为了能得到较快的中断响应,VxWorks操作系统里中断服务程序(ISR)在一个专门的上下文中执行,是处于任务的上下文之外。
Vxworks实时内核Wind提供了基本的多任务环境,并提供了相应的调度算法。每个任务均具备自己的上下文,上下文均保存在任务控制块TCB中。其中TCB包括以下内容:TCB内容
注意Vxworks系统是支持虚拟内存的。

1.1 任务状态

Vxworks任务状态表如下:
任务状态表
状态转移

1.2 Wind任务调度

Wind内核的默认算法为基于优先级的抢占式调度算法,同时也支持RR调度算法,两种算法均依赖任务优先级。Wind内核中有256种优先级,0-255,0为最高级。函数如下:
任务调度控制函数
1.基于优先级的抢占式任务调度
字面理解,优先级高的任务会掠夺CPU资源进行执行,低的任务将被掠夺。
优点:紧急任务可以立刻执行。
缺点:当多个优先级相同的任务共享一个CPU时,若某个任务永不阻塞,它将独占CPU,其他任务无法执行。任务调度会产生上下文切换,频繁掠夺会导致上下文切换造成的资源浪费。
由此引出轮转调度算法
2.轮转调度算法(RR)
平均使用CPU,相同优先级任务获得相同CPU处理时间,当一个时间片用完,自愿放弃对CPU的使用,进行上下文切换。
==注意:==若在轮转调度中,某个任务被高优先级任务抢占,则保留该任务的时间片剩余时间,在高优先级任务执行完毕后,恢复低优先级任务执行并用完用于时间片。
3.抢占上锁
taskLock()、taskUnlock(),可以禁止内核调度。若某个任务开启了禁止调度,却在执行中被阻塞和挂起,内核有资格执行调度,选择高优先级任务执行,当该任务解除阻塞或挂起时,禁止抢占将再次生效。

注意: 应用程序的优先级应设置在 100-250之间,驱动程序优先级位于 51-99。

1.3 任务控制

1. 任务创建函数:
id = taskSpawn(name, priority, options, stacksize, main, arg1, arg10);
taskInit();
taskActive();
//id 为4个字节 int
//name: 任务名
//stacksize:任务堆栈
//options:任务选项
//main:入口函数地址
//arg10:传给入口函数的启动参数

//示例:
tid = taskSpawn("tMyTask", 90, VX_FP_TASK, 20000, myFunc, 2387, 0, 0
0, 0, 0, 0, 0, 0, 0);

在这里插入图片描述

2. 任务删除
exit();//终止任务调用,释放内存
taskDelete();//终止指定任务,释放内存
taskSafe();//保护调用任务免于删除
taskUnsafe();//解除任务删除保护

注意删除任务之前,应先释放该任务所占有的共享资源。也需要注意,若某个任务需要访问临界区,若该任务释放时,同时对其持有的信号量进行删除,这将导致其他任务无法对该临界区进行访问。此时应调用taskSafe()保护该任务被删除。

3. 任务控制

在这里插入图片描述

4. 任务拓展函数

在这里插入图片描述

5. 任务错误errno

操作系统中的潜在的全局变量errno是已被定义的,可直接与操作系统相连接的应用代码所应用,上下文切换时,errno同时被保存。

6. 任务异常处理

对于操作系统来说,CSAPP讲到异常分为中断、陷阱、异常,同时中断也分为硬件中断与软件中断,通常硬件中断是异步的,即不可预料的;软件中断是同步的,即是可预料的,Debug就是通过陷阱实现的。

7. 共享代码和重入
  1. 代码共享必须是可重入的,多个任务同时调用一个函数不发生冲突;
    2)Vx中的I/O与驱动程序为可重入的;
    Vx函数使用下列重入技术:
    动态堆栈变量:多任务调用一个函数时,每个任务都有自己堆栈;
    被信号保护的全局和静态变量:使用semMLib提供的mutex或互斥信号量;
    任务变量:多任务调用一个程序,但每个任务调用使用不同的全局变量或静态变量。

二、任务间通信

  1. 共享内存:可以说是最快的通信方式,也简单
  2. 信号量:同步与互斥
  3. Mutexe和条件变量
  4. 消息队列和管道:用于同一个CPU内任务间消息传递
  5. Sockets和远程程序调用:网络编程
  6. 信号:异常处理

2.1 互斥办法

中断上锁

int lock = intLock();
//禁止中断的代码临界区
intUnlock(lock);

抢占上锁

taskLock();
//禁止中断的代码临界区
taskUnlock();

2.2 信号量

在这里插入图片描述

#include "vxWorks.h"
#include "semLib.h"
SEM_ID semMutex;
semMutex = semBCreate(SEM_Q_PRIORITY, SEM_FULL);

semTake(semMutex, WAIT_FOREVER);
//临界区
semGive(semMutex);

信号量同时可用于同步。具体为任务1等待某信号量,任务2完成某个任务后,释放该信号量,此时任务1捕捉到该信号,解除阻塞,开始执行任务。

一致性
和信号量有关的一致性问题主要包括:(1)删除信号量时不会使阻塞其上的任务陷入无限等待;(2)持有信号量的任务不会因为意外停止运行而使其他任务陷入无限等待。一致性问题可能会使部分任务无法运行,严重时使整个系统得不到期望的结果甚至崩溃。其中,第(1)点由系统保证,系统在删除信号量时自动解除信号量阻塞队列上所有阻塞的任务

2.2.1 二进制信号量

可进行同步和互斥
在这里插入图片描述

2.2.2 互斥信号量

在这里插入图片描述

2.2.3 计数信号量

2.3 注意:优先级倒置

基于优先级调度的操作系统会存在一个问题。在这里插入图片描述
解决办法:优先级继承。
由此提出互斥信号量

semID = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE);

计数器信号量
在这里插入图片描述
CSAPP均赘述,不做介绍。
Wind标准信号量接口包括两个特定选项,不兼容POSIX。
(1)超时: NO_WAIT, WAIT_FOREVER;
(2)队列:
在这里插入图片描述

2.4 消息队列

单个CPU里任务间主要通信方式。

  1. 全双工通信需要两个消息队列
    函数:
    在这里插入图片描述
  2. 可选择任务优先级顺序FIFO顺序
  3. 接受超时紧急消息选项
    推荐模型:
    在这里插入图片描述
    为什么使用消息队列
    “信号量+共享缓冲区”不需要在用户缓冲区(即共享缓冲区)和系统内核缓冲区之间复制数据,因此效率很高,适合数据量非常大的场合;而使用消息队列时通信双方需要经过系统内核缓冲区交换数据,因此效率比“信号量+共享缓冲区”低
    优势在于:程序简单
    数据量不大的情况下,程序简化效果明显。

信号量的优先级问题只考虑一个阻塞任务队列的优先级排序。和信号量优先级不同,消息队列优先级问题从下面两个方面考虑。
(1)消息自身的优先级:决定消息队列中多条消息提交给接收任务的顺序;
(2)阻塞任务队列优先级:决定阻塞任务队列中多个发送者任务或者多个接收者任务谁先被执行。

消息优先级被分成两类,normal和urgent,urgent插在队头,normal队尾。
在这里插入图片描述
信号量与消息队列实验

/*
msgorder.c: test message processing order*/
#include "vxworks.h"
#include "msgQLib.h"
#include " semLib.h"
#define MAxMSG 3
#define MAX_MSG_LEN 50
#define TEST_NUM 8
MSG_Q_ID msgQueue = NULL;
SEM_ID semMutex = NULL;
SEM_ID semCounter = NULL;
char logBuf[ TEST_NUM*3 ][MAX_MSG_LEN+20];
int nLog = 0;
void sendMsg( int );
void rcvMsg ( void ) ;
void msgOrder(){
	/*定义:发送任务优先级消息自身优先级接收任务优先级*/
	const int senderPri[TEST_NUM]= {50, 51, 52, 49, 53, 56, 55, 54};
	const int msgPri[TEST_NUM]={ 011.10101 };
	const int rcvPri = 60;
	int i, j;
	if( msgQueue || semMutex || semcounter ) {
		printf ('last running not exit properly ! \n" );
		exit (-1);
	}
	/*创建:消息队列互斥信号量计数信号量*/
	msgQueue = msgQCreate(MAX_MSG,MAX_MSG_LEN,MSG_Q_FIFO);
	semMutex= semMCreate(SEM_Q_FIFO) ;
	semCounter = semccreate(SEM__Q_FIFO,0); .
	if( !msgQueue || !semMutex li || !semCounter ) 
		exit (-1) ;
	/生成发送任务(发送消息)*/
	for( i=0; i<TEST_NUM; i++ )
		taskSpawn( 0, senderPri(i]02000,sendMsg, msgPri[i], 0,0,0,0,0,00,0,0 ) ;
	/*生成接收任务(接收消息)`*/
	taskspawn(0, rcvPri, 0, 2000, rcvMsg, 0,0,0,0,0,0,0,0, 0,0 ) ;
	/★等待所有任务退出*/
	i = 0 ;
	while( i++ < TEST_NUM+1 )
		semTake(semCounter, WAIT_FOREVER);
	printf ( " \n**Logged result : ln" ) ; /*输出结果*/
	for( i=0; i<nLog; i++ ) {
		printf ( "<%2d> %sln" , i,logBuf[i] ) ;
	}
	msgQDelete(msgQueue);  msgQueue = 0;/*正常退出清理*/
	semDelete(semMutex);  semMutex=0;
	semDelete(semcounter);  semCounter= 0;
}

void sendMsg( int msgPri ) /*发送消息msgPri表示消息优先级*/
{
	int taskPri ;
	char msgBuf[MAx_MSG_LEN];
	/*生成消息内容msgBuf:任务优先级+消息优先级记录*/
	taskPriorityget(taskIdself(), &taskPri ) ;
	sprintf(msgBuf,"task pri : %d,msg pri: %s",taskPri, msgPri == MSG_PRI_NORMAL ? "LO" : "HI") ;
	semTake(semMutex, WAIT_FOREVER) ;
	sprintf( logBuf [nLog], "---s:[ %s ] ready" , msgBuf ) ;
	nLog++;
	semGive( semMutex);
	/*发送消息―记录*/
	msgQSend(msgQueue,msgBuf,MAX_MSG_LEN,WAIT_FOREVER,msgPri );
	semTake(semMutex,WAIT_FOREVER);
	sprintf(logBuf [nLog] , "---s: [ %s ] sent" , msgBuf );
	nLog++;
	semGive(semMutex);
	semGive( semcounter);
}
void rcvMsg(void) /*接收消息*/
	int i, j;
	char msgBuf [MAX_MSG LEN ] ;
	for( i=0; i<TEST_NUM; i++) {
		j = msgQReceive(msgQueue,msgBuf,MAX_MSG_LEN,WAIT_FOREVER );
		semTake(semMutex,WAIT_FOREVER) ;
			if{j)
				sprintf( logBuf [nLog], "---R:[ %s ] received" , msgBuf);
			else
				sprintf( logBuf [nLog], "---R: 0 bytes read" );
				nLog++;
		semGive(seMutex);
		}
		semGive(semCounter);
	}
)

在这里插入图片描述

2.5 管道

#include "pipeDrv.h"
status = pipeDevCreate("/pipe/name", max_msgs, max_length);
status = pipeDevDelete(char *name, BOOL force);

#include "ioLib.h"
int open(const char *name, int flags, int mode);

管道提供了一个消息队列不具备的重要特性,可使用select(),属于I/O复用范畴,请读者自行检索。
和直接的VxWorks消息队列相比,VxWorks管道具有如下一致的地方:

  • 当任务试图从一个空的管道中读取数据,或向一个满的管道中写入数据时,任务被阻塞:
  • 支持中断服务程序向管道中写入信息,驱动程序判断调用环境属于中断则不阻塞,即使没有可用的缓冲区,但是不能从管道中读取,如同中断服务程序不能对消息队列做接收操作一样;
  • 往管道中写入数据时,一次写入的数据不能超出消息队列最大允许消息长度
    由于管道具有上述消息队列的本质内容,本书有时称管道中的数据为消另外,VxWorks管道相比消息队列有如下不足:
  • 管道的效率不如消息队列的高。需要将应用程序中标准的IIO调用映射到pipeDrv驱动程序函数,并最终由消息队列函数实现;
  • 管道会稍微多占用一些内存资源和IO系统资源;
  • 不支持消息的优先级控制,消息队列支持两级消息优先级和阻塞任务队列优先级
  • 管道不支持超时控制;
  • 较老版本可能不支持管道删除,消息队列不再使用时可以删除以释放资源。

2.6 任务间网络通信

  1. 套接字
    TCP/UDP
    可以去看看图解TCP/IP,通俗易懂。
  2. 远程程序调用(RPC)(笔者:总看见RPC框架什么的,没有深入了解过,JAVA用户应该比较了解)

2.7 信号

“信号可以异步改变任务的控制流程”
信号也称为 软中断
Wind内核支持:UNIX BSD风格信号POSIX兼容信号
在这里插入图片描述
在这里插入图片描述

2.8 看门狗定时器

(懒得手打了 = =, 坐的好累)
在这里插入图片描述

参考书籍及文献:
[1]: VxWorks程序员指南 [美]Wind River著-清华大学出版社

  • 9
    点赞
  • 168
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: vxWorks 是由美国公司 Wind River Systems 开发的实时操作系统(RTOS),支持多种处理器体系结构和硬件平台。vxWorks_kernel_api_reference_vol1_6.6 是 vxWorks 官方文档中的一部分,其主要是关于 vxWorks 内核 API 的参考手册。vxWorks 虽然拥有很多特定的 API,但是它与其他 RTOS 相比,最大的优势在于其高度可定制的内核,使用者可以根据自己的需求进行定制和配置。 vxWorks_kernel_api_reference_vol1_6.6 包含了 vxWorks 内核 API 的全部内容,其中包括任务管理、信号量、事件标志、消息队列、定时器、收发器等方面的内容,在 vxWorks 系统开发中是必需的参考手册。用户可以通过这份参考手册了解 vxWorks 内核 API 的具体功能和使用方法,以便开发出更加高效、稳定的应用程序。 此外,vxWorks_kernel_api_reference_vol1_6.6 也提供了详细的示例程序,帮助用户更快地理解 API 的使用,也可作为快速入门手册。但是需要注意的是,由于 vxWorks 内核的高度定制性和灵活性,用户在实际开发中需要根据具体的应用需求进行改动和调整。 总之,vxWorks_kernel_api_reference_vol1_6.6 是 vxWorks 开发者必不可少的参考文档,并且可以提供基础开发和高级调试参考,应用程序从考虑到开发到维护都需要借助该文档。 ### 回答2: 《vxworks_kernel_api_reference_vol1_6.6》是一份VxWorks实时操作系统的内核API参考手册,是开发者使用VxWorks进行嵌入式系统开发的必备参考资料。 该手册共分为六个部分,包括任务管理、消息传递、系统管理、软件定时器、内存管理和中断处理六个方面。在任务管理部分,介绍了任务的创建、删除、挂起、唤醒以及任务之间的消息传递方式;在消息传递部分,介绍了消息队列、消息邮箱、信号量和事件等通信机制;在系统管理部分,介绍了系统调度、任务优先级调整、定时器和闹钟等系统管理功能;在软件定时器部分,介绍了基于定时器的软件实现方式;在内存管理部分,介绍了内存分配、清理等内存管理功能;最后,在中断处理部分,介绍了中断的注册、删除、处理等相关内容。 此外,该手册还提供了各个API函数的详细参数介绍和使用方法。开发者可以根据自己的需求和具体应用场景,灵活运用这些API函数,实现定制化的嵌入式系统开发。 总的来说,《vxworks_kernel_api_reference_vol1_6.6》是一份非常全面、详细的VxWorks内核API参考手册,对于进行嵌入式系统开发开发者来说,是必不可少的参考资料。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值