Interesting query on IPC in QNX

HI,

For IPC, I understand that we need to create a channel and then other threads can connect to that channel and do a MsgSend/Receivce operations.

But I want to know that if a channel is created by one of the threads (say T1) in one process (say P1), how does it export the channel ID to the other thread (say T2) in other process (say P2). Because we need the channel ID in order to establish a connecction with the channel. Since Channel ID is a variable that is limited to the process (say P1 here), it is not visible in other process space.

I want to understand how actually does the IPC takes place between thread in P1 to P2 using the same channel.

Please help. Thanks.

You could use a resource manager, and by doing so map the channel ID’s to the file system. As such you can open() the FD of the resource manager and do you msgSend() stuff their.

Also, there are some function that allow for a name_atach() like approach. There might be other methods but, this is what I could think up atm.

Thanks.
Can you send me a sample code to do this?
I still want an description of how exactly QNX Kernel does this IPC between two processes?

Thank you for your reply.

Qnxdevelop,

This sample code should give you what you need to set up 2 processes.

struct Message
{
    // This structure contains timer events
    struct _pulse timerPulse;

     // This is user event message data
     char data[100];
};

In Process A you do:

name_attach_t *nameAttach;     // QNX global name struct
int channelId;
int connectId;
int processId = 0; // Ourselves

if ((nameAttach = name_attach(NULL, "Foo", 0)) == NULL)
{
    printf( "Error!");
    exit (-1);
}
channelId = nameAttach->chid;

// Connect to the channel to be able to receive messages
if ((connectId = ConnectAttach_r(ND_LOCAL_NODE, processId, channelId, _NTO_SIDE_CHANNEL, 0)) < 0)
{
    printf("Connection failed on channel %d due to %s", channelId, strerror(abs(connectId)));
    exit(-1);
}

// Receive a message or timer pulse
int recvId;
Message msg; // Incoming message or timerpulse
Message replyMsg;

if ((recvId = MsgReceive_r(channelId, &msg, sizeof(Message), NULL)) < 0)
{
    printf(Error %s while trying to receive a message over connection %d on channel %d", strerror(abs(recvId)), connectId, channelId);
    exit(-1);			
}
else
{
    // Determine if we received a message or timer pulse
    if (recvId == 0)
    {
        switch (msg.timerPulse.code)
        {
            case _PULSE_CODE_DISCONNECT:
                // Remote side disconnecting via name_close() from a 
                // name_open (this only occurs for a server process
                ConnectDetach_r(msg.timerPulse.scoid);
            break;
            case _PULSE_CODE_UNBLOCK:
	 // Sender hit by a signal so release them to process it
                 Message emptyReply;
                 reply(emptyReply);
            break;
            case _PULSE_CODE_THREADDEATH:
            case _PULSE_CODE_COIDDEATH:
                    // Remote side died/exited!
            break;
            default:
	 // Timer pulse received. Do timer processing...
             break;
         }
    }
    else
    {
        // Reply to a name_open() call for servers
        if (msg.timerPulse.type == _IO_CONNECT)
        {
	MsgReply_r(recvId, EOK, NULL, 0);
                prinf("Remote side %d connected to server", recvId);
         }
         else
         {
                // Process the message...
                printf ("Received a message of %s", msg.data);

                // Reply to unblock the other side.
                sprintf(replyMsg.data,"Bar");
	MsgReply_r(recvId, 0, replyMsg, 0);
         }
    }
}
	

In Process B you do:

int connectId;           // Connect Id returned by QNX O/S

if ((connectId = name_open("Foo", 0)) < 0)
{
    printf("Error!");
   exit(-1);
}

// Sends a message to the server
int ret;
Message msg;         // Our data to the server
Message replyMsg;  // Any reply data from the server

sprintf(msg.data,"Foo");
if ((ret = MsgSend_r(connectId, &msg, sizeof(Message), replyMsg, sizeof(Message))) < 0)
{
    printf("Error %s %d while trying to send message over connection %d",  strerror(abs(ret)), ret, connectId);
    exit(-1);
}

printf ("Server replied with %s", replyMsg.data);

Make sure to run gns, the global name server.

Tim

Your code example has given me quite some good insight into the IPC. Thanks again.

Not quite. You can’t receive on a connection, only on a channel (i.e. your statement implies that a thread within a process that does not own a channel may do a receive, this is not true).

There´s no need to call ConnectAttach_r() in processA in order to send msg from B. Attaching a channel is enought.

Other thing: I had problems with name_* set of functions. It´s ok sending msgs locally, but setting NAME_FLAG_ATTACH_GLOBAL (and running gns as docs says) I had strange behavior, with the additional disadvantage to depend on another process (gns).

It´s strongly recomended to create a full resource manager.

Juan Manuel

The behavoir isn’t strange, it’s well documented and work as advertised.

Not if you need network wide name resolution.

I can´t make it work fine… I tried with the same example that Tim post with no significant differences. It works fine locally. But even locally if I run “gns” it stop working. ?
It also work perfectly in remote way using ConnectAttach() but no with name_open()… and this is what we are talking about…

What could be wrong?

Thank you!

Gns needs to be running on each machine. One needs to be configure as server and the other as slave.

I think I`ve tried all variants as docs says… Running gns -s in node A, gns -c (or gns -c hostname_server) in node B. I run the processes server and client after running gns of course. The processA attaches to a portion of namespace depending on flag NAME_FLAG_ATTACH_GLOBAL in function name_attach() (/dev/name/local|globa/Foo)… everything seems normal…

I also tried make a symlink /dev/name/global from node A to node B, as docs says again, but nothing…

Even more, as I said. If I dont set the global flag (supouse I dont need this particular service to be global) it attaches to /dev/name/local/Foo. That`s fine and that works. But If I run “gns -s” in that node, it stop working.

Is that normal? I think It shouldn’t work this way…

Here’s my console output referring to the last part of my question (local mode).

./processA &

[1] 169287744

MsgReceive () on channel 1

./processB

connectId name_open() [1073741825]
recvId=3
(server) received a message of [Foo]
(client) Server replied with [Bar]
[1] + Done ./processA

THAT WAS OK

gns -s

./processA &

[1] 169308225

MsgReceive () on channel 1

./processB

recvId=3
(server) received a message of []
connectId name_open() [1073741825]
Error [9] - [Bad file descriptor] ret=-1 while trying to send message over connection 1073741825
[1] + Done ./processA

I can see there is some comunication betwen process A and B, because the server receives a msg (this happens in remote mode too), I supouse a _IO_CONNECT or IO_OPEN but the comunication isn`t succesful.

Any idea? Thank you!

Juan Manuel

Juan,

If you want to use gns with the -s (server) option, you’ll need to change the 4th argument to the name_attach() call from _NTO_SIDE_CHANNEL to NAME_FLAG_ATTACH_GLOBAL (see ‘locating services using GNS’ in the helpviewer).

In your example, I assume you are running on just 1 node (ie not across a network) since the code I posted only was set up to run on 1 node. And that’s the case that works for you.

For the ‘gns -s’ case, in addition to setting the 4th argument, I think (note, have not tested) you also need a 2nd ‘gns -c’ running (this is the default when you just do ‘gns’). This is the actual gns the code attaches to. The gns -s is simply the central repository of names on the network (in your case, the network consists of 1 machine).

Tim

Tim: thanks for the reply. Ok, name_attach() have only 3 parameters as I see, i supouse you wanted to say to set the flag in param #3 not #4. right?

Ok, Let´s separete problems (that I thing they are the same)

  1. Local mode: name_attach( NULL, “Foo”, 0).
    It register under /dev/name/local/Foo (even with no gns running)

  2. Remote mode: name_attach( NULL, “Foo”, NAME_FLAG_ATTACH_GLOBAL)
    It register under /dev/name/global/Foo (with no gns running i have a normal “function not implemented”). gns must be running.

The console output I post was for local mode: NAME_FLAG_ATTACH_GLOBAL unset (#3 param=0). With gns -s running (I’ve also tried with both gns running, the client too…) doesn’t work for me. “Bad File Descriptor” as you see in my post. With no gns running it works nice.

In remote mode the situation is similar or identical, the only exception is that param #3 is now set to NAME_FLAG_ATTACH_GLOBAL as we said ans both gns -s and gns -c are running before both processes were running. EBADF again.

Suggestions? Thank you!

Juan

Does your code properly handle the IO_CONNECT stuff?

Juan,

Ok, I spent 10 minutes doing a working example:

Common File

#include <errno.h>
#include <time.h>
#include <string>
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#include <sys/dispatch.h>

struct Message 
{ 
	// This structure contains timer events 
	struct _pulse timerPulse; 

	// This is user event message data 
	char data[100]; 
}; 

Server Code:

#include "test.h"

using namespace std;

int main() 
{
	name_attach_t *nameAttach; // QNX global name struct 
	int channelId; 
	int connectId; 
	int processId = 0; // Ourselves 
	
	if ((nameAttach = name_attach(NULL, "Foo", NAME_FLAG_ATTACH_GLOBAL)) == NULL) 
	{ 
		printf( "Error!"); 
		exit (-1); 
	} 
	channelId = nameAttach->chid; 
	
	// Connect to the channel to be able to receive messages 
	if ((connectId = ConnectAttach_r(ND_LOCAL_NODE, processId, channelId, _NTO_SIDE_CHANNEL, 0)) < 0) 
	{ 
		printf("Connection failed on channel %d due to %s", channelId, strerror(abs(connectId))); 
		exit(-1); 
	} 
	
	// Receive a message or timer pulse 
	int recvId; 
	Message msg; // Incoming message or timerpulse 
	Message replyMsg; 

	while (1)
	{
		if ((recvId = MsgReceive_r(channelId, &msg, sizeof(Message), NULL)) < 0) 
		{ 
			printf("Error %s while trying to receive a message over connection %d on channel %d", strerror(abs(recvId)), connectId, channelId); 
			exit(-1); 
		} 
		else 
		{ 
			// Determine if we received a message or timer pulse 
			if (recvId == 0) 
			{ 
				switch (msg.timerPulse.code) 
				{ 
				 case _PULSE_CODE_DISCONNECT: 
					// Remote side disconnecting via name_close() from a 
					// name_open (this only occurs for a server process 
					ConnectDetach_r(msg.timerPulse.scoid); 
					break; 
				 case _PULSE_CODE_UNBLOCK: 
					// Sender hit by a signal so release them to process it 
					Message emptyReply; 
					int ret;
					if ((ret = MsgReply_r(recvId, 0, &emptyReply, sizeof(Message))) < 0)
					{
						printf("Error %s while trying to reply to sender over connection %d on channel %d", strerror(abs(ret)), connectId, channelId);
					}
					break; 
				 case _PULSE_CODE_THREADDEATH: 
				 case _PULSE_CODE_COIDDEATH: 
					// Remote side died/exited! 
					break; 
				 default: 
					// Timer pulse received. Do timer processing... 
					break; 
				} 
			} 
			else 
			{ 
				// Reply to a name_open() call for servers 
				if (msg.timerPulse.type == _IO_CONNECT) 
				{ 
					MsgReply_r(recvId, EOK, NULL, 0); 
					printf("Remote side %d connected to server\n", recvId); 
				} 
				else 
				{ 
					// Process the message... 
					printf ("Received a message of %s\n", msg.data); 
					
					// Reply to unblock the other side. 
					sprintf(replyMsg.data,"Bar"); 
					MsgReply_r(recvId, 0, &replyMsg, sizeof(Message)); 
				} 
			} 
		} 
	}
}

Client Code


#include "test.h"

using namespace std;

int main() 
{
	int connectId; // Connect Id returned by QNX O/S 
	
	if ((connectId = name_open("Foo", NAME_FLAG_ATTACH_GLOBAL)) < 0) 
	{ 
		printf("Error!"); 
		exit(-1); 
	} 
	
	// Sends a message to the server 
	int ret; 
	Message msg; // Our data to the server 
	Message replyMsg; // Any reply data from the server 
	
	sprintf(msg.data,"Foo"); 
	if ((ret = MsgSend_r(connectId, &msg, sizeof(Message), &replyMsg, sizeof(Message))) < 0) 
	{ 
		printf("Error %s %d while trying to send message over connection %d", strerror(abs(ret)), ret, connectId); 
		exit(-1); 
	} 
	
	printf ("Server replied with %s\n", replyMsg.data); 
}

On the server node (172.27.12.103), I started qnet via:

mount -Tio-net npm-qnet.so

I verified qnet started by doing ‘ls /net’ and I saw my hostname there.

I started gns in server mode via:

gns -s

On the client node (172.27.12.3), I started qnet via:

mount -Tio-net npm-qnet.so

I verified qnet started by doing ‘ls /net’ and I saw my hostname there.

At this point doing an ‘ls /net’ on either the server or client should show the hostnames of BOTH machines. This indicates qnet is working.

I started gns in client mode via:

gns -c

On the server node I started the testServer program.

./testServer

On the client node I started the testClient program.

./testClient

And the Foo message appeared on the server and the Bar message on the client. So the name_attach worked fine across the network as expected.

Tim

Hey Tim, thank you very much for your time!!. Now I kow what was the problem… :slight_smile:

I used the first code you post as base, and there was no while ()… Then all is simple. With no while, I was receiving only the CONNECT msg and exit, and no the I/O msg.

With MsgReceive in a while, i capture all the secuence: connection, i/o transfer, and close? (3 msgs).

Thanks to all by help!

Juan Manuel