When a process opens a file, the POSIX-compliant open() library routine first sends the pathname to procnto, where the pathname is compared against the prefix tree to determine which resource managers should be sent the open() message.
The prefix tree may contain identical or partially overlapping regions of authority—multiple servers can register the same prefix. If the regions are identical, the order of resolution can be specified (see Ordering mountpoints, below). If the regions are overlapping, the responses from the path manager are ordered with the longest prefixes first; for prefixes of equal length, the same specified order of resolution applies as for identical regions.
For example, suppose we have these prefixes registered:
Prefix | Description |
---|---|
/ | Power-Safe filesystem (fs-qnx6.so) |
/dev/ser1 | Serial device manager (devc-ser*) |
/dev/ser2 | Serial device manager (devc-ser*) |
/dev/hd0 | Raw disk volume (devb-eide) |
The filesystem manager has registered a prefix for a mounted Power-Safe filesystem (i.e., /). The block device driver has registered a prefix for a block special file that represents an entire physical hard drive (i.e., /dev/hd0). The serial device manager has registered two prefixes for the two PC serial ports.
The following table illustrates the longest-match rule for pathname resolution:
This pathname: | matches: | and resolves to: |
---|---|---|
/dev/ser1 | /dev/ser1 | devc-ser* |
/dev/ser2 | /dev/ser2 | devc-ser* |
/dev/ser | / | fs-qnx6.so |
/dev/hd0 | /dev/hd0 | devb-eide.so |
/usr/jhsmith/test | / | fs-qnx6.so |
Generally the order of resolving a filename is the order in which you mounted the filesystems at the same mountpoint (i.e., new mounts go on top of or in front of any existing ones). You can specify the order of resolution when you mount the filesystem. For example, you can use:
You can also use the -o option to mount with these keywords:
If you specify the appropriate before option, the filesystem floats in front of any other filesystems mounted at the same mountpoint, except those that you later mount with before. If you specify after, the filesystem goes behind any other filesystems mounted at the same mountpoint, except those that are already mounted with after. So, the search order for these filesystems is:
with each list searched in order of mount requests. The first server to claim the name gets it. You would typically use after to have a filesystem wait at the back and pick up things the no one else is handling, and before to make sure a filesystem looks first at filenames.
Consider an example involving three servers:
At this point, the process manager's internal mount table would look like this:
Mountpoint | Server |
---|---|
/ | Server A (Power-Safe filesystem) |
/bin | Server B (flash filesystem) |
/dev/random | Server C (device) |
Of course, each Server name is actually an abbreviation for the nd, pid, chid for that particular server channel.
Now suppose a client wants to send a message to Server C. The client's code might look like this:
int fd; fd = open("/dev/random", ...); read(fd, ...); close(fd);
In this case, the C library will ask the process manager for the servers that could potentially handle the path /dev/random. The process manager would return a list of servers:
From this information, the library will then contact each server in turn and send it an open message, including the component of the path that the server should validate:
As soon as one server positively acknowledges the request, the library won't contact the remaining servers. This means Server A is contacted only if Server C denies the request.
This process is fairly straightforward with single device entries, where the first server is generally the server that will handle the request. Where it becomes interesting is in the case of unioned filesystem mountpoints.
Let's assume we have two servers set up as before:
Note that each server has a /bin directory, but with different contents.
Once both servers are mounted, you would see the following due to the union of the mountpoints:
What's happening here is that the resolution for the path /bin takes place as before, but rather than limit the return to just one connection ID, all the servers are contacted and asked about their handling for the path:
DIR *dirp; dirp = opendir("/bin", ...); closedir(dirp);
which results in:
The result now is that we have a collection of file descriptors to servers who handle the path /bin (in this case two servers); the actual directory name entries are read in turn when a readdir() is called. If any of the names in the directory are accessed with a regular open, then the normal resolution procedure takes place and only one server is accessed.
For a more detailed look at this, see Unioned filesystems in the Resource Managers chapter of Getting Started with QNX Neutrino.
This overlaying of mountpoints is a very handy feature when doing field updates, servicing, etc. It also makes for a more unified system, where pathnames result in connections to servers regardless of what services they're providing, thus resulting in a more unified API.