DSP

QNX操作系统信息传递-qnx任务之间的消息传递信息传递

2019-07-13 20:48发布

如果你认为本系列文章对你有所帮助,请大家有钱的捧个钱场,点击此处赞助,赞助额1元起步,多少随意


锋影e-mail:174176320@qq.com

一、QNX消息概述
QNX消息可以分为同步消息和异步消息。异步(脉冲)消息主要体现的是一种通知机制,同步消息主要是说消息在传递过程需要双方相互配合的过程。
二、QNX消息传递几个基本概念
1、频道与链接
消息传递是基于服务器与客户端的模式来进行的,QNX6抽象出了”频道“(Channel)这个概念。一个频道,就是一个服务的入口;至于这个频道到底具体有多少线程为其服务,那都是服务器端自己的事情。一个服务器如果有多个服务,它也可以开多个频道。而客户端,在向“频道”发送消息前,需要先建立连接(Connection),然后将消息在连接上发出去。这样同一个客户端,如果需要,可以与同一个频道建立多个连接。
2、名字空间(name_space)
在客户端与服务器通信中,为了让客户端方便的知道服务器的nd/pid/chid, 服务器进程可以注册一个路径名,与服务频道的nd, pid, chid关联起来。客户端就只要请求连接版务器路径名就可以了。

三、QNX同步信号下客户端服务器通信

1、客户端模型
1)调用name_open(PATH)连接到服务器频道获得一个连接ID
3)调用API发送消息等待服务器应答
4)收到应答完毕调用name_close()关闭连接

2、服务器模型
1)调用name_attach注册一个名字空间并且创建一个频道
2)接收并处理消息
3)应答消息处理结果
4)name_detach从名字空间删除该名称

3、客户端服务器消息传递过程
1)服务器注册一个名字空间等待客户端连接
2)客户端连接到名字空间
3)客户端调用MsgSend接口给服务器发送消息,客户端处于发送阻塞
4)服务器调用MsgReceive接口接收数据,客户端处于应答阻塞
5)服务器处理完毕消息,调用MsgReply函数发送应答消息
6)客户端从MsgSend函数返回,解除阻塞

4、利用多路消息传递提高效率
用iov_t来“汇集”数据。也就是说,可以一次传送几块据。Header同databuf是两块不相邻的内存,但传递到服务器端的ReceiveBuffer里,就是连续的了。
SETIOV(&iov[0], &header, sizeof(header));
SETIOV(&iov[1], databuf, datalen);
MsgSendvs(ConnectionId, iov, 2, Replybf, ReplyLen);

5、消息传递的方向与MsgDeliverEvent()
客户端给服务器发送消息,服务器不能立刻应答而客户端不想等待。遇到这种情况,正确的做法是,告诉客户端,请求一段时间后会得到处理,客户端得以继续运行,一旦服务器完成任务,服务器需要一些方式告诉客户端请求完成。

客户端: 准备一个“通知事件”(Notification Event),并把这个事件用MsgSend()发给服务器端,意思是:“如果xxx情况发生的话,请用这个事件通知我”。
  服务器: 收到这个消息后,记录下当时的rcvid,和传过来的事件,然后应答“好的,知道了”。 
  客户端: 因为有了服务器的应答,客户端不再阻塞,可以去做别的事 
  ...... 
  服务器: 在某个时刻,客户端所要求的“xxx情况”满足了,服务器调用 MsgDeliverEvent(rcvid, event);以通知客户端 
  客户端: 收到通知,再用MsgSend()发关“xxx 情况的数据在哪里?” 
  服务器: 用MsgReply()把数据返回给客户端

  int MsgDeliverEvent (int rcvid,const struct sigevent *event);
event服务器不需要作修改 rcvid是服务器从客户端接收的。当服务器给客户端应答后,这个id将失去意义。另外MsgDeliverEvent是非阻塞函数。

6、常用API
ChannelCreate(), ChannelDestroy()
    ConnectAttach(), ConnectDetach()
    MsgDeliverEvent()
    MsgError()
    MsgRead(), MsgReadv()
    MsgReceive(), MsgReceivePulse(), MsgReceivev()
    MsgReply(), MsgReplyv()
    MsgSend(), MsgSendnc(), MsgSendsv(), MsgSendsvnc(), MsgSendv(), 
MsgSendvnc(), MsgSendvs(), MsgSendvsnc()
  MsgWrite(), MsgWritev()

name_attach(), name_close(), name_detach(), and name_open()


7、总结
1)客户端在给服务发送消息的时候指定服务器应答缓冲区
2)服务器给客户端应答消息(MsgReply)的时候传递参数作为MsgSend返回值
3)服务器在MsgReceive的时候可以传递参数用于获取客户端发送的消息的一些信息
4)客户端在打开服务名字空间的时候系统会发送一个_IO_CONNECT 消息给服务器
5)客户端断开连接或者关闭名字空间的时候服务器会收到_PULSE_CODE_DISCONNECT脉冲
6)客户端视图从REPLY BLOCK解除阻塞的时候服务器会收到_PULSE_CODE_UNBLOCK脉冲
7)创建名字空间的时候自动设置了_NTO_CHF_DISCONNECT_NTO_CHF_COID_DISCONNECT 、_NTO_CHF_UNBLOCK 标准,故服务器可以收到对应系统脉冲



四、通知信号-脉冲
1、基本概念
脉冲其实更像一个短消息,也是在“连接”上发送的。脉冲最大的特点是它是异步的。发送方不必要等接收方应答,直接可以继续执行。但是,这种异步性也给脉冲带来了限制。脉冲能携带的数据量有限,只有一个8位的"code"域用来区分不同的脉冲,和一个32位“value"域来携带数据。脉冲最主要的用途就是用来进行“通知”(Notification)。不仅是用户程序,内核也会生成发送特殊的“系统脉冲”到用户程序,以通知某一特殊情况的发生。

2、接收脉冲
知道频道上不会有别的消息,只有脉冲的话,可以用MsgReceivePulse()来只接收脉冲;如果频道既可以接收消息,也可以接收脉冲时,就直接用MsgReceive(),只要确保接收缓冲(ReveiveBuf)至少可以容下一个脉冲(sizeof struct _pulse)就可以了。如果MsgReceive()返回的rcvid是0,就代表接收到了一个脉冲,反之,则收到了一个消息。

3、脉冲信号特点
1)有效传递40位数据(8位脉冲码,32位数据)
2)对发送者而言是非阻塞的
3)可以像其他消息一样被接受
4)脉冲会排队,只要接受者不是阻塞等待脉冲。
5)若没有脉冲,则接收者会阻塞等待脉冲

4、相关数据结构与API
struct _pulse {
      uint16_t type;
      uint16_t subtype;
      int8_t code;
      uint8_t zero [3];
      union sigval value;
      int32_t scoid;
  };
value是一个联合体
      union sigval {
      int sival_int;
      void *sival_ptr;
     };

 int MsgReceivePulse (int chid,
        void *rmsg,
        int rbytes,
        struct _msg_info *info);


五、异步消息

1、异步信号常用API
asyncmsg_channel_create()
asyncmsg_channel_destroy()
asyncmsg_connect_attach()
asyncmsg_connect_detach()
asyncmsg_flush()
asyncmsg_put(),
asyncmsg_get()
asyncmsg_free()
asyncmsg_malloc()

2、API说明
int asyncmsg_channel_create (
unsigned flags,
mode_t mode,
size_t buffer_size,
unsigned max_num_buffer,
const struct sigevent *ev,
int (*recvbuf_callback) (
size_t bufsize,
unsigned num_bufs,
void *bufs[],
int flags,
void *handle ),
void *recvbuf_callback_handle );

Flags:描述频道属性,默认设置_NTO_CHF_ASYNC,如果设置_NTO_CHF_ASYNC_NONBLOCK 
那么程序在调用asyncmsg_get()的时候如果没有消息到来将不会阻塞。

Mode:设置频道属性

Buff_size:设置存放消息的缓冲的大小

Max_num_buffer:设置存放消息的缓冲区最大个数

Ev:NULL或者一个指向sigevent结构体的指针,用于指定一个事件,当有消息可以被接收的时候这个事件自动发送

Recvbuf_callback:NULL或者一个函数指针,用于分配空间存放接收的消息为NULL时候用Malloc

recvbuf_callback_handle:传递给Recvbuf_callback函数。

说明:
当Recvbuf_callback不为NULL的时候:
每当调用一次asyncmsg_get()的时候触发一次回调函数(flags设置为ASYNCMSG_RECVBUF_ALLOC,bufs指向消息...)当希望接收其他消息的时候返回1,返回0停止。
调用 asyncmsg_channel_destroy()的时候,回调函数被触发多次用于释放空间,flags设置为ASYNCMSG_RECVBUF_FREE。


int asyncmsg_channel_destroy( int chid );

chid :频道ID
说明:
当Recvbuf_callback不为NULL的时候,每次调用会触发释放空间,默认调用free。

返回值:
成功:EOK
失败:-1 ->errno

int asyncmsg_connect_attach (
uint32_t nd,
pid_t pid,
int chid,
unsigned index,
unsigned flags, 
const struct _asyncmsg_connection_attr * attr);

参数说明:
Nd/pid/chid:哪台电脑/进程ID/频道ID

index:最低可接受的连接ID(起始值)

Flags:
_NTO_COF_NOSHARE:应用程序使用自己的BUFFER,否则使用asyncmsg_malloc(),填充数据调用asyncmsg_put()的时候发送。

_NTO_COF_NONBLOCK:不用阻塞等待,当发送头部忙的时候
Attr:
指定连接属性
返回值:
成功:连接ID
失败:-1

int asyncmsg_connect_detach( int coid );


参数:
coid :连接ID
备注:
从指定连接断开,所有在发送放一侧的消息被丢弃,如果为了在这之前所有的消息被发送完毕,在这之前调用asyncmsg_flush()接口。

返回值:
成功:EOK
失败:-1

int asyncmsg_flush( int coid,
int mode );

参数说明:
Coid:连接ID

Mode:
0,如果不想该函数阻塞则设置ASYNCMSG_FLUSH_NONBLOCK 

返回值:
成功:EOK
失败:-1

int asyncmsg_connect_attr (
int coid,
struct _asyncmsg_connection_attr *old_attr, 
const struct _asyncmsg_connection_attr *new_attr);

参数说明:
coid:连接ID
Old_attr:当前属性
new_attr:新的属性

返回值:
成功:EOK
失败:-1


int asyncmsg_put( int coid,
const void *buff,
size_t size, 
unsigned handle),
int (*call_back) (
int err,
void* buf,
unsigned handle ));

int asyncmsg_putv( int coid,