Some notes on NDs
The other funny
thing that we haven't yet talked about when it comes
to message passing is this whole business of a node descriptor
or just
ND
for short.
Recall that we used symbolic node names, like /net/wintermute in our examples.
Under QNX 4 (the previous version of the OS before QNX Neutrino), native networking was
based on the concept of a node ID, a small integer that was unique on the network.
Thus, we'd talk about node 61,
or node 1,
and this
was reflected in the function calls.
Under QNX Neutrino, all nodes are internally referred to by a 32-bit quantity,
but it's not network unique!
What I mean by that is that wintermute might think of spud
as node descriptor number 7,
while spud might think of
magenta as node descriptor number 7
as well.
Let me expand that to give you a better picture.
This table shows some sample node descriptors that might be used by three nodes,
wintermute, spud, and foobar:
| Node | wintermute | spud | foobar |
|---|---|---|---|
| wintermute | 0 | 7 | 4 |
| spud | 4 | 0 | 6 |
| foobar | 5 | 7 | 0 |
Notice how each node's node descriptor for itself is zero.
Also notice how wintermute's node descriptor for spud is
7,
as is foobar's node descriptor for spud.
But wintermute's node descriptor for foobar
is 4
while spud's node descriptor for foobar
is 6.
As I said, they're not unique across the network, although they are unique on each node.
You can effectively think of them as file descriptors—two processes might have the
same file descriptor if they access the same file, but they might not; it just depends on
who opened which file when.
Fortunately, you don't have to worry about node descriptors, for a number of reasons:
- Most of the off-node message passing you'll typically be doing will be through higher-level function calls (such as open(), as shown in the example above).
- Node descriptors are not to be cached—if you get one, you're supposed to use it immediately and then forget about it.
- There are library calls to convert a pathname (like /net/magenta) to a node descriptor.
To work with node descriptors, you'll want to include the file <sys/netmgr.h> because it includes a bunch of netmgr_*() functions.
You'd use the function
netmgr_strtond()
to convert a string into a node descriptor.
Once you have this node descriptor, you'd use it immediately in the ConnectAttach()
function call.
Specifically, you shouldn't ever cache it in a data structure!
The reason is that the native networking manager may decide to reuse it once all connections
to that particular node are disconnected.
So, if you got a node descriptor of 7
for /net/magenta, and you
connected to it, sent a message, and then disconnected, there's a possibility that the native
networking manager will return a node descriptor of 7
again for a different node.
Since node descriptors aren't unique per network, the question that arises is, How do
you pass these things around the network?
Obviously, magenta's view of what node descriptor 7
is will
be radically different from wintermute's.
There are two solutions here:
- Don't pass around node descriptors; use the symbolic names (e.g., /net/wintermute) instead.
- Use the netmgr_remote_nd() function.
The first is a good general-purpose solution. The second solution is reasonably simple to use:
int
netmgr_remote_nd (int remote_nd, int local_nd);
This function takes two parameters: the remote_nd is the node descriptor of the target machine, and local_nd is the node descriptor (from the local machine's point of view) to be translated to the remote machine's point of view. The result is the node descriptor that is valid from the remote machine's point of view.
For example, let's say wintermute is our local machine.
We have a node descriptor of 7
that is valid on our local machine
and points to magenta.
What we'd like to find out is what node descriptor magenta uses to
talk to us:
int remote_nd;
int magenta_nd;
magenta_nd = netmgr_strtond ("/net/magenta", NULL);
printf ("Magenta's ND is %d\n", magenta_nd);
remote_nd = netmgr_remote_nd (magenta_nd, ND_LOCAL_NODE);
printf ("From magenta's point of view, we're ND %d\n",
remote_nd);
This might print something similar to:
Magenta's ND is 7
From magenta's point of view, we're ND 4
This says that on magenta, the node descriptor 4
refers to our node.
(Notice the use of the special constant ND_LOCAL_NODE, which is really
zero, to indicate this node.
)
Now, recall that we said (in
Who sent the message?
)
that the struct _msg_info contains, among other
things, two node descriptors:
struct _msg_info
{
uint32_t nd;
uint32_t srcnd;
...
};
We stated in the description for those two fields that:
- nd is the receiving node's node descriptor for the transmitting node
- srcnd is the transmitting node's node descriptor for the receiving node
So, for our example above, where wintermute is the local node and magenta is the remote node, when magenta sends a message to us (wintermute), we'd expect that:
- nd would contain 7
- srcnd would contain 4
