|
|
This topic gives coding guidelines and maintenance tips for shared library development. Before getting down to specifics, we should emphasize that if you plan to develop a commercial shared library, you ought to consider providing a compatible archive as well. As noted previously, some users may not find a shared library appropriate for their applications. Others may want their applications to run on UNIX® operating system releases without shared object support. Shared object code is completely compatible with archive library code. You can use the same source files to build archive and shared object versions of a library. Some of the reasons you might want to use shared objects are:
To enhance shared library performance, you should:
As noted, only a shared object's text segment is shared by all processes that use it; its data segment typically is not. Every process that uses a shared object usually gets a private memory copy of its entire data segment, regardless of how much data is needed. You can cut down the size of the data segment in several ways:
In previous implementations, system error messages were made available to applications only through two global variables:
extern int sys_nerr; extern char sys_errlist[];sys_errlist[X] gives a character string for the error X, if X is a nonnegative value less than sys_nerr. Now if the current list of messages were made available to applications only through a lookup table in an archive library, applications that used the table obviously would not be able to access new messages as they were added to the system unless they were relinked with the library. Errors might occur for which these applications could not produce useful diagnostics. Something similar happens when you use a global lookup table in a shared library:
static const char msg[] = { "Error 0", "Not owner", "No such file or directory", ... };char strerror(int err) { if (err < 0 || err >= sizeof(msg)/sizeof(msg[0])) return "Unknown error"; return (char )msg[err]; }
The message array is static, so no application space is allocated to hold a separate copy. Because no application copy exists, the dynamic linker does not waste time moving the table. New messages can be added, because only the library knows how many messages exist. Finally, note the use of the type qualifier const to identify data as read-only. Whereas writable data is stored in a shared object's data segment, read-only data is stored in its text segment. For more on const, see ``C language compilers''.
In a similar way, you should try to allocate buffers dynamically -- at run time -- instead of defining them at link time. That will save memory because only the processes that need the buffers will get them. It will also allow the size of the buffers to change from one release of the library to the next without affecting compatibility as shown below:
char buffer() { static char buf = 0;if (buf == 0) { if ((buf = malloc(BUFSIZE)) == 0) return 0; } ... return buf; }
Although processes that use shared libraries will not write to shared pages, they still may incur page faults. To the extent they do, their performance will degrade. You can minimize paging activity in the following ways: