Adding to what Tim had to say…
I’d be very skeptical about what Receive(0,…) might try to do. The modern QNX 6 version should look like this:
struct _msg_info info;
chid = ChannelCreate(_NTO_CHF_SENDER_LEN);
rcvid = MsgReceive( chid, msg, size, &info);
MsgReply( rcvid, status, rmsg, size );
It might help to know why message passing has become more complex in QNX 6 to understand the differences.
The main issue is that QNX 6 supports threads. It is in principle possible for a process to use message passing to send and receive messages to itself. This means that a process is no longer in SEND/RECEIVE/REPLY mode, but rather its threads are. It is also possible that one wants to create a thread pool. This is a bunch of threads which all are in RECEIVE mode that can process messages as they come in.
So, taking a look at the code above, we see that before you can receive a message, you have to create a channel. A channel is known by all threads in a process. A process can have more than one channel. This allows more than one thread to be receive blocked on the same channel, and it also allows different threads to be blocked on different channels. You might ask which thread will receive a message if more than one is receive blocked on the same channel. I believe the answer is undefined. If you create a thread pool, any thread should be able to process the message.
A few other things before we go on to the sender side. The channel id (chid) is not a process id (pid).
The receive id (rcvid) is not a pid either. You should be used to the latter as even with QNX 2 the return from Receive() could have been
a virtual id when passing message over a network. This is somewhat hidden in QNX 6, however it is possible to learn what the node and pid of the sender is, note the _msg_info structure.
On the sender side the code looks like this. The terminology is confusingly similar to the sending side, but keep them straight.
int coid; // Connection Id
coid = ConnectAttach( node, pid, chid, index, flags);
ret = MsgSend( coid, smsg, s_size, rmsg, r_size);
Here you get a connection id (coid) to the receiver and send using this coid, again not a pid.
What’s wrong here? To create the coid you seem to need the node/pid/chid of the receiver. But how to you get it?
There is more than one way. Originally with QNX 6, all receivers needed to be resource managers that grabbed a piece
of name space, eg. /dev/myprogram. The sender would open() this name space and get the node/pid/chid from the fd returned. Note here that fd’s and coid’s are very similar in QNX 6.
Name space could of course be anywhere down the tree structure. It is even possible to grab already allocated name space. So for example,
you could have part of the file system as /database/file1. A database resource manager could grab /database so that an open to file1 would go to it and not the file system. It could then issue its own open() on file1, which would be processed by the file system giving it file access.
Getting back to coid’s, after early releases, there were lots of complaints by users that having to implement the entire resource manager machinery was cumbersome. Reluctantly I think, QNX provided the name-attach/name-locate routines which do all the work. So the coid from a name attach is the same as from ConnectAttach().
So note that while rarely done, a sender could have multiple threads, each of which sends messages using different connections to a receiver with multiple threads where the receiver thread is controlled by having multiple channels.
Yes a lot more complex, but you can set things up simply, similar to QNX 4, which is often done. Also common is setting up a resource manager with a thread pool that can handle multiple requests at the same time. One nice thing about this is that with QNX 4 and 2 you alway had to either reply to a send or remember that the reply needed to be done in the future. With QNX 6 a thread can be allocated for each send, and that thread can be blocked without holding up other sends. A thread pool will allocate these threads dynamically. The can greatly simplify code. Of course you then need to implement synchronization of threads, usually with mutex’s to prevent these threads from interfering.