Литература / sys_arch
.pdf♥ 2008, QNX Software Systems GmbH & Co. KG. |
Pathname management |
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:
1Server B receives a null path, since the request came in on the same path as the mountpoint.
2Server A receives the path "bin", since its mountpoint was "/".
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.
Why overlay mountpoints?
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.
Symbolic prefixes
We’ve discussed prefixes that map to a resource manager. A second form of prefix, known as a symbolic prefix, is a simple string substitution for a matched prefix.
You create symbolic prefixes using the POSIX ln (link) command. This command is typically used to create hard or symbolic links on a filesystem by using the -s option. If you also specify the -P option, then a symbolic link is created in the in-memory prefix space of procnto.
Command |
Description |
ln -s existing_file symbolic_link |
Create a filesystem symbolic link. |
ln -Ps existing_file symbolic_link |
Create a prefix tree symbolic link. |
Note that a prefix tree symbolic link will always take precedence over a filesystem symbolic link.
For example, assume you’re running on a machine that doesn’t have a local filesystem. However, there’s a filesystem on another node (say neutron) that you wish to access as “/bin”. You accomplish this using the following symbolic prefix:
ln -Ps /net/neutron/bin /bin
October 16, 2008 |
Chapter 6 • Process Manager 123 |
Pathname management |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
This will cause /bin to be mapped into /net/neutron/bin. For example, /bin/ls will be replaced with the following:
/net/neutron/bin/ls
This new pathname will again be applied against the prefix tree, but this time the prefix matched will be /net, which will point to lsm-qnet. The lsm-qnet resource manager will then resolve the neutron component, and redirect further resolution requests to the node called neutron. On node neutron, the rest of the pathname (i.e. /bin/ls) will be resolved against the prefix space on that node. This will resolve to the filesystem manager on node neutron, where the open() request will be directed. With just a few characters, this symbolic prefix has allowed us to access a remote filesystem as though it were local.
It’s not necessary to run a local filesystem process to perform the redirection. A diskless workstation’s prefix tree might look something like this:
/
lsm-qnet.so
dev
console ser2 
ser1
devc-con devc-ser...
With this prefix tree, local devices such as /dev/ser1 or /dev/console will be routed to the local character device manager, while requests for other pathnames will be routed to the remote filesystem.
Creating special device names
You can also use symbolic prefixes to create special device names. For example, if your modem was on /dev/ser1, you could create a symbolic prefix of /dev/modem as follows:
ln -Ps /dev/ser1 /dev/modem
Any request to open /dev/modem will be replaced with /dev/ser1. This mapping would allow the modem to be changed to a different serial port simply by changing the symbolic prefix and without affecting any applications.
124 |
Chapter 6 • Process Manager |
October 16, 2008 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
Pathname management |
Relative pathnames
Pathnames need not start with slash. In such cases, the path is considered relative to the current working directory. The OS maintains the current working directory as a character string. Relative pathnames are always converted to full network pathnames by prepending the current working directory string to the relative pathname.
Note that different behaviors result when your current working directory starts with a slash versus starting with a network root.
A note about cd
In some traditional UNIX systems, the cd (change directory) command modifies the pathname given to it if that pathname contains symbolic links. As a result, the pathname of the new current working directory — which you can display with pwd — may differ from the one given to cd.
In QNX Neutrino, however, cd doesn’t modify the pathname — aside from collapsing
.. references. For example:
cd /usr/home/dan/test/../doc
would result in a current working directory of /usr/home/dan/doc, even if some of the elements in the pathname were symbolic links.
For more information about symbolic links and .. references, see “QNX 4 filesystem” in the Working with Filesystems chapter of the QNX Neutrino User’s Guide.
File descriptor namespace
Once an I/O resource has been opened, a different namespace comes into play. The open() returns an integer referred to as a file descriptor (FD), which is used to direct all further I/O requests to that resource manager.
Unlike the pathname space, the file descriptor namespace is completely local to each process. The resource manager uses the combination of a SCOID (server connection ID) and FD (file descriptor/connection ID) to identify the control structure associated with the previous open() call. This structure is referred to as an open control block (OCB) and is contained within the resource manager.
The following diagram shows an I/O manager taking some SCOID, FD pairs and mapping them to OCBs.
October 16, 2008 |
Chapter 6 • Process Manager 125 |
Pathname management |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
System space |
I/O Manager |
|
space |
FD
SCOID
Open control block
Open control block
Sparse array
The SCOID and FD map to an OCB of an I/O Manager.
Open control blocks
The open control block (OCB) contains active information about the open resource. For example, the filesystem keeps the current seek point within the file here. Each open() creates a new OCB. Therefore, if a process opens the same file twice, any calls to lseek() using one FD will not affect the seek point of the other FD. The same is true for different processes opening the same file.
The following diagram shows two processes, in which one opens the same file twice, and the other opens it once. There are no shared FDs.
126 |
Chapter 6 • Process Manager |
October 16, 2008 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
|
|
|
|
|
|
|
|
Pathname management |
||||
|
File |
|
Server process |
||||||||||
descriptors |
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|||
0 |
|
|
|
|
|
Open |
|||||||
|
|
|
|
|
|||||||||
Process A 1 |
|
|
|
|
|
||||||||
|
|
|
control blocks |
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/tmp/file
File descriptors
0
Process B
Two processes open the same file.
FDs are a process resource, not a thread resource.
Several file descriptors in one or more processes can refer to the same OCB. This is accomplished by two means:
•A process may use the dup(), dup2(), or fcntl() functions to create a duplicate file descriptor that refers to the same OCB.
•When a new process is created via vfork(), fork(), posix_spawn(), or spawn(), all open file descriptors are by default inherited by the new process; these inherited descriptors refer to the same OCBs as the corresponding file descriptors in the parent process.
When several FDs refer to the same OCB, then any change in the state of the OCB is immediately seen by all processes that have file descriptors linked to the same OCB.
For example, if one process uses the lseek() function to change the position of the seek point, then reading or writing takes place from the new position no matter which linked file descriptor is used.
The following diagram shows two processes in which one opens a file twice, then does a dup() to get a third FD. The process then creates a child that inherits all open files.
October 16, 2008 |
Chapter 6 • Process Manager 127 |
Pathname management |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
|
|
File |
Server process |
|||
|
descriptors |
|||||
|
|
|
|
|||
|
0 |
|
|
|
Open |
|
|
|
|
|
|||
Parent |
1 |
|
|
|
||
|
|
control blocks |
||||
process |
|
|
||||
2 |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
File |
/tmp/file |
descriptors
0
Child 1 process
2
A process opens a file twice.
You can prevent a file descriptor from being inherited when you posix_spawn(), spawn(), or exec*() by calling the fcntl() function and setting the FD_CLOEXEC flag.
128 |
Chapter 6 • Process Manager |
October 16, 2008 |
Chapter 7
Dynamic Linking
In this chapter. . .
Shared objects 131
How shared objects are used 132
October 16, 2008 |
Chapter 7 • Dynamic Linking 129 |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
Shared objects |
Shared objects
In a typical system, a number of programs will be running. Each program relies on a number of functions, some of which will be “standard” C library functions, like printf(), malloc(), write(), etc.
If every program uses the standard C library, it follows that each program would normally have a unique copy of this particular library present within it. Unfortunately, this results in wasted resources. Since the C library is common, it makes more sense to have each program reference the common instance of that library, instead of having each program contain a copy of the library. This approach yields several advantages, not the least of which is the savings in terms of total system memory required.
Statically linked
The term statically linked means that the program and the particular library that it’s linked against are combined together by the linker at linktime. This means that the binding between the program and the particular library is fixed and known at linktime
— well in advance of the program ever running. It also means that we can’t change this binding, unless we relink the program with a new version of the library.
You might consider linking a program statically in cases where you weren’t sure whether the correct version of a library will be available at runtime, or if you were testing a new version of a library that you don’t yet want to install as shared.
Programs that are linked statically are linked against archives of objects (libraries) that typically have the extension of .a. An example of such a collection of objects is the standard C library, libc.a.
Dynamically linked
The term dynamically linked means that the program and the particular library it references are not combined together by the linker at linktime. Instead, the linker places information into the executable that tells the loader which shared object module the code is in and which runtime linker should be used to find and bind the references. This means that the binding between the program and the shared object is done at runtime — before the program starts, the appropriate shared objects are found and bound.
This type of program is called a partially bound executable, because it isn’t fully resolved — the linker, at linktime, didn’t cause all the referenced symbols in the program to be associated with specific code from the library. Instead, the linker simply said: “This program calls some functions within a particular shared object, so I’ll just make a note of which shared object these functions are in, and continue on.” Effectively, this defers the binding until runtime.
Programs that are linked dynamically are linked against shared objects that have the extension .so. An example of such an object is the shared object version of the standard C library, libc.so.
October 16, 2008 |
Chapter 7 • Dynamic Linking 131 |
How shared objects are used |
♥ 2008, QNX Software Systems GmbH & Co. KG. |
You use a command-line option to the compiler driver qcc to tell the tool chain whether you’re linking statically or dynamically. This command-line option then determines the extension used (either .a or .so).
Augmenting code at runtime
Taking this one step further, a program may not know which functions it needs to call until it’s running. While this may seem a little strange initially (after all, how could a program not know what functions it’s going to call?), it really can be a very powerful feature. Here’s why.
Consider a “generic” disk driver. It starts, probes the hardware, and detects a hard disk. The driver would then dynamically load the io-blk code to handle the disk blocks, because it found a block-oriented device. Now that the driver has access to the disk at the block level, it finds two partitions present on the disk: a DOS partition and a QNX 4 partition. Rather than force the disk driver to contain filesystem drivers for all possible partition types it may encounter, we kept it simple: it doesn’t have any filesystem drivers! At runtime, it detects the two partitions and then knows that it should load the fs-dos.so and fs-qnx4.so filesystem code to handle those partitions.
By deferring the decision of which functions to call, we’ve enhanced the flexibility of the disk driver (and also reduced its size).
How shared objects are used
To understand how a program makes use of shared objects, let’s first see the format of an executable and then examine the steps that occur when the program starts.
ELF format
QNX Neutrino uses the ELF (Executable and Linking Format) binary format, which is currently used in SVR4 Unix systems. ELF not only simplifies the task of making shared libraries, but also enhances dynamic loading of modules at runtime.
In the following diagram, we show two views of an ELF file: the linking view and the execution view. The linking view, which is used when the program or library is linked, deals with sections within an object file. Sections contain the bulk of the object file information: data, instructions, relocation information, symbols, debugging information, etc. The execution view, which is used when the program runs, deals with segments.
At linktime, the program or library is built by merging together sections with similar attributes into segments. Typically, all the executable and read-only data sections are combined into a single “text” segment, while the data and “BSS”s are combined into the “data” segment. These segments are called load segments, because they need to be loaded in memory at process creation. Other sections such as symbol information and debugging sections are merged into other, nonload segments.
132 |
Chapter 7 • Dynamic Linking |
October 16, 2008 |
