邮箱是一个通过在系统(共享)内存空间传递消息来实现同步和通信的对象。uTenux中每个邮箱都包含一个用来发送消息的消息队列和一个用于等待接收消息的任务队列,其使用邮箱功能的消息内容放置在发送方和接收方共享的内存中,因此,实际发送和接收的只有位于这片共享空间的消息起始地址。消息的内容本身并不复制。

消息邮箱的API包括创建删除发送接收删除的功能。

1、使用tk_cre_mbx创建一个消息邮箱。属性设置很简单。

2、向邮箱发送消息

ER ercd= tk_snd_mbx(ID mbxid,T_MSG* pk_msg);

只需要将消息丢给邮箱即可,发送消息的任务不会进入等待状态。

3、接收邮箱中的消息

ERercd= tk_rcv_mbx(IDmbxid,T_MSG**ppk_msg,TMOtmout);

得到消息邮箱提供的消息的起始地址。如果消息邮箱是空的,任务进入等待。

这里一几个地方使用了void*的空类型指针,关于空类型指针在这里的用法,可参考后边的附录。

关于void*指针的详细介绍,可参考这里:http://bbs.chinaunix.net/thread-225624-1-1.html

必须要明确的地方:消息传递中,传递的只是地址

【实验描述】

参考示例代码。首先创建两个任务TaskA和TaskB优先级分别为24和25,两个邮箱Mbx_S和Mbx_R。之后启动TaskA。在TaskA中启动TaskB。然后向Mbx_S发送一个消息,然后开始等待Mbx_R中的消息。由于Mbx_R中没有消息,TaskA进入休眠。

此时TaskB开始执行,TaskB先从Mbx_S中取消息,然后输出消息。最后再向Mbx_R中发送消息。Mbx_R中有了消息,TaskA释放等待进入Ready就绪状态。TaskA优先级高于TaskB,抢占TaskB开始执行,继续完成以后的消息处理代码

【代码和输出】

#include "MailboxSample.h" 

typedef struct u_msg {
VP msgque[1]; /* Area for message queue,as same as T_MSG */
UB *usrmsg; /* Area for message pointer */
} U_MSG; void MbxSampleTaskA(W stacd,VP exinf);
void MbxSampleTaskB(W stacd,VP exinf);
static ID TaskID_A;
static ID TaskID_B;
static ID MbxID_S;
static ID MbxID_R; ER MbxSample( void)
{
T_CTSK ctsk;
T_CMBX cmbx;
ER ercd; //创建两个任务
ctsk.bufptr = NULL;
ctsk.exinf = NULL;
ctsk.itskpri = 24;
ctsk.stksz = 512;
ctsk.task = MbxSampleTaskA;
ctsk.tskatr = TA_HLNG | TA_RNG0;
TaskID_A = tk_cre_tsk(&ctsk); ctsk.task = MbxSampleTaskB;
ctsk.itskpri = 26;
TaskID_B = tk_cre_tsk(&ctsk); //创建两个mbx
cmbx.mbxatr = TA_TFIFO | TA_MFIFO;
cmbx.exinf = NULL;
MbxID_S = tk_cre_mbx(&cmbx);
MbxID_R = tk_cre_mbx(&cmbx); //启动任务A
ercd = tk_sta_tsk(TaskID_A,5); return TRUE;
} void MbxSampleTaskB(W stacd,VP exinf)
{
U_MSG *pk_rcvmsg;
U_MSG sndmsg;
while(1)
{
tm_putstring((UB*)"TaskB准备发送消息\n");
tk_rcv_mbx(MbxID_S,(T_MSG**)&pk_rcvmsg,-1);
tm_putstring((UB*)"这是TaskB接收到的消息\n");
tm_putstring((UB*)pk_rcvmsg->usrmsg); sndmsg.usrmsg = "TaskB send a message";
tm_putstring((UB*)"这是TaskB发送的消息\n");
tm_putstring((UB*)sndmsg.usrmsg);
tm_putstring((UB*)"\n");
tk_snd_mbx(MbxID_R,(T_MSG*)&sndmsg);
}
} void MbxSampleTaskA(W stacd,VP exinf)
{
U_MSG *pk_rcvmsg;
U_MSG sndmsg;
sndmsg.usrmsg = "TaskA Send a Message\n";
tk_sta_tsk(TaskID_B,0);
while(1)
{
tm_putstring((UB*)"这是TaskA发送的消息\n");
tm_putstring((UB*)sndmsg.usrmsg);
tm_putstring((UB*)"\n");
tk_snd_mbx(MbxID_S,(T_MSG*)&sndmsg); tk_rcv_mbx(MbxID_R,(T_MSG**)&pk_rcvmsg,-1);
tm_putstring((UB*)"这是TaskA接收到的消息\n");
tm_putstring((UB*)pk_rcvmsg->usrmsg);
tm_putstring((UB*)"\n");
}
}

【串口输出】

----------------------------------------------------

micro Tenux Version 1.6.00(build 0180)

Supported MCU is ST STM32F407VG

Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd.

----------------------------------------------------

这是TaskA发送的消息

TaskA Send a Message

TaskB准备发送消息

这是TaskB接收到的消息

TaskA Send a Message

这是TaskB发送的消息

TaskB send a message

这是TaskA接收到的消息

TaskB send a message

这是TaskA发送的消息

TaskA Send a Message

【PS】

邮箱是一个很好玩的东西。我想写一个用于控制的东西,控制部分的输入数据是不连续的。有控制数据来了,执行部分就要改变一下运行状态。

其中控制部分可以将控制字做成邮箱消息,然后在去等待用户输入。执行部分,每隔一段时间检查一下邮箱,看看有没有控制部分来的更新。如果有,就根据邮箱消息改变下运行状态,没有的话保持运行状态。

比如我的电机控制部分,检测用户输入的任务可以是一个控制源,系统周期性的自检测的任务也可以当做一个控制源。两个任务同时工作,同时向邮箱丢控制信息。

用户输入任务可以发送以下消息:改变电机的转速,让电机停转,让电机刹车等等,自检任务检测到故障之后,发送一下消息:让电机刹车,让电机停转等。

执行部分的任务,只需要根据消息来广州就可以了。不用考虑谁在控制。

关于void型指针 VP

创建邮箱时,需要提供一个邮箱消息的指针T_MSG。

这个指针指向存放消息的地址。查看定义可发现,他是这样定义的:

typedef struct t_msg { 

      VP msgque[1]; /* Area for message queue */ 

} T_MSG;

而VP的定义是:

typedef void *VP;

这里就用到了一个void型的指针。在示例程序中,将自己定义的消息类型强制转换成T_MSG型的指针。即void型的指针。

Void类型的指针是无类型的指针,可以指向任何结构。因为它就是一个指针!

在引用这个指针时候,需要预先知道这个指针指向地址的结构。不然编译器无法知道所指数据的类型而报错

这样的消息传递,使得mbx所传递的消息类型不再固定。需要使用者手动定义。

发送消息时,发送任务不会进入等待状态。即:只要把发送的消息往邮箱已仍就可以了,至于消息到哪里去了,交给OS处理了。当邮箱是空的时候,消息直接进邮箱。邮箱不空,消息进入该邮箱的消息队列。这些,发送任务都不关心。

接收消息时候,使用tm_rcv_mbx完成。提供邮箱ID和消息数据包起始地址,以及一个等待超时时间。如果要接收的邮箱没有消息,那么接收任务进入等待状态。

问题,关于邮箱发送消息和接收消息函数中使用的空类型指针void*

两个函数的定义是这样的:

ER ercd= tk_snd_mbx(ID mbxid,T_MSG* pk_msg); 

    ER ercd= tk_rcv_mbx(ID mbxid,T_MSG** ppk_msg,TMO tmout);

其中T_MSG的定义如下:

typedef struct t_msg {
VP msgque[1]; /* Area for message queue */
} T_MSG;

其中VP就是 typedef void *VP; 也就是说,T_MSG这个结构本身包含了一个空类型的指针。这是消息结构的消息头,有OS负责维护,用户不用管。但不能没有这东西,否则OS会报错。

下面是我对这里使用空类型指针的理解。

1、tk_snd_mbx使用T_MSG*

T_MSG* 是指向消息结构的指针,与msgque是相同的。这个指针是一个void*类型的指针。可以指向任意类型的数据。

所以tk_snd_mbx(IDmbxid,T_MSG*pk_msg);

这种用法,就是要向函数传递一个空类型的指针。

2、tk_rcv_mbx中的使用。

这里是一个指向指针的指针(T_MSG** ppk_msg)。关于ppk_msg内核规范中有这么个说明:ppk_msg是包含消息的数据包(包括消息头在内)的起始地址。

也就是说ppk_msg是一个地址(地址1)。* ppk_msg是就是地址中的数据,这个数据也是个地址(地址2)。于是T_MSG** ppk_msg 就是地址2中存储的数据。这个数据是T_MSG类型的。

在示例工程中,有这么个用法:

tk_rcv_mbx(MbxID_S,(T_MSG**)&pk_rcvmsg,-1);

其中pk_rcvmsg 的定义是这样的:U_MSG *pk_rcvmsg; 也是个指针。

那么&pk_rcvmsg,就是取这个指针的地址。(T_MSG**)&pk_rcvmsg就成了将地址pk_rcvmsg中存储的地址所指向的数据块,强制转换成T_MSG*类型。

刚好与函数定义部分相同。

void*类型更详细介绍,请移步:http://bbs.chinaunix.net/thread-225624-1-1.html

【uTenux实验】邮箱的更多相关文章

  1. 【uTenux实验】消息缓冲区

    uTenux的消息缓冲区是一个通过传递大小可变的消息来实现同步和通信的对象. 消息缓冲区由三部分组成:1.待发消息队列  2.等接收消息的任务队列  3.用来保存缓冲消息的空间. 和信号相比,消息队列 ...

  2. 【uTenux实验】写在开始实验之前

    1.使用的uTenux内核代码:http://www.uloong.cc/cn/download/uTenux_V1.6.00r180.zip 2.uTenux的特性: 1.微内核  2.开放源码.完 ...

  3. 【uTenux实验】任务管理

    任务就是一个无限循环.uTenux提供的任务管理功能是很强大的,包括建立和删除一个任务,启动或退出任务,取消一个任务的启动请求,改变任务的优先级和査询任务状态,使任务进人睡眠状态和唤醒状态,取消唤醒请 ...

  4. 【uTenux实验】信号量

    信号量(semaphore)是一个用来指示可用的资源并将可用资源的数量以数值的形式表示出来的对象.当使用一组资源时,信号量用来实现互斥控制和同步.uTenux提供了信号量出来的API,可以很方便地使用 ...

  5. 【uTenux实验】事件标志

    事件标志是一个用来实现同步的对象,由多个位组成,用作指示对应事件存在的标志.事件标志由用来指示对应事件存在的位模式(bitpattern)和一个等待事件标志的任务队列组成. uTenux提供了一组AP ...

  6. 【uTenux实验】互斥体

    互斥体,维基百科中交互斥锁.其定义是这样的:互斥锁(英语:英语:Mutual exclusion,缩写 Mutex)是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制 ...

  7. 【uTenux实验】集合点端口

    这个是头一次接触的概念.比较不好理解.内核规范中的说明就要20页! 看了王总写的uTenux内核规范之后,有那么一点明白了但理解不深. 集合点端口就像每次工作前的收集情况会.首长下达收集情况指令,各个 ...

  8. 【uTenux实验】内存池管理(固定内存池和可变内存池)

    1.固定内存池管理实验 内存管理是操作系统的一个基础功能.uTenux的内存池管理函数提供了基于软件的内存池管理和内存块分配管理.uTenux的内存池有固定大小的内存池和大小可变的内存池之分,它们被看 ...

  9. 【uTenux实验】时间管理(系统时间/周期性处理/警报处理)

    1.系统时间管理 系统时间管理函数用来对系统时间进行操作,是OS的一个基础性的东西.个人认为,设置系统时间和获取系统时间对OS来说基本是可有可无的. uTenux提供了三个系统时间相关API.分别用于 ...

随机推荐

  1. roscpp

    不服气不行,写ROS这伙就是比我知道的库函数多 inti函数:初始化节点是简单读些命令行参数和环境配置来配置节点名呀,命名空间呀和命名重映射之类东西. 其是在roscpp里面解释很清楚了,只是,我当时 ...

  2. visual studio 远程服务器返回了意外响应:(417)expectation failed

    解决方法: 修改devenv.exe.config文件,添加 <servicePointManager expect100Continue="false" /> C:\ ...

  3. apache下自定义404错误页面

    404页面的目的是:告诉浏览者其所请求的页面不存在或链接错误,同时引导用户使用网站其他页面而不是关闭窗口离开. 很多开源系统包括CMS系统.Blog系统等不提供404页面或提供的404页面并未达到SE ...

  4. UOJ Test Round 1

    第一题: 题目大意: 给出N个字符串,字符串的前面部分都是字母且都是一样的,后面部分是数字,按照后面的数字排序.N<=10000 解题过程: 1.第一题是真良心,一开始的做法是把后面的数字分离出 ...

  5. DownloadManager补漏

    原始完成于:2014-10-24 20:01:03 DownloadManager是一个处理HTTP下载请求的系统服务: 1. 基本用法 1 private void download() { 2 R ...

  6. net 的单元测试 初学

    1. 都要以一个方法 这样的去测试 2. 利用工具 Install-Package Moq -Version 4.0 (最高的是4.5  4.0适用于自己项目的版本)   Moq.dll 进行测试   ...

  7. iOS中FMDB的使用

    1在日常的开发中,我们需要用到离线缓存将数据信息存入数据库,在没有网络的时候进行加载,而我们IOS用的就是sqlite3数据库,用原生的sql我们也能实现,但是书写起来比较麻烦,尤其是其它语言转过来的 ...

  8. cas 在.net 下的单点登录实现及 ,Net Mvc的接入

    最近在研究单点登录,发现用的最广的就是cas了,查了下资料,发现有人写了详细的说明 地址:http://www.cnblogs.com/zhenyulu/archive/2013/01/22/2870 ...

  9. 团队开发——冲刺1.b

    冲刺阶段一(第二天) 1.昨天做了什么? 在了解C#的基础上,深入熟悉Windows窗体应用程序,熟练掌握基本功能 2.今天准备做什么? 在C#的Windows窗体应用程序中,设计简单的游戏开始主界面 ...

  10. 三部曲二(基本算法、动态规划、搜索)-1003-Lucky and Good Months by Gregorian Calendar

    模拟加阅读题......虽然很多事常识性的知识,但也有许多不知道的知识,关键是不读不知道那些是已经知道的那些不是,许多重要的信息零散的分布在一大坨英文里,读起来很痛苦......自己读了一遍,读的晕晕 ...