C compilation system

Minimize the library's data segment

As noted, only the text segment of a dynamically linked library is shared by all processes that use it; its data segment typically is not. Every process that uses a dynamically linked library 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:

Generally speaking, these make library interfaces and code easier to maintain. Moreover, defining functional interfaces often eliminates global variables entirely, which in turn eliminates global copy of data. The following illustrates the differences between the function interface approach (strerror(S)) and the global variable approach (sys_errlist[X]).

In some other implementations, system error messages are 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 non-negative 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 dynamically linked library:

  1. The compilation system sets aside memory for the table in the address space of each executable that uses it, even though it does not know yet where the table will be loaded.

  2. After the table is loaded, the dynamic linker copies it into the space that has been set aside.

  3. Each process that uses the table gets a private copy of the library's data segment, including the table, and an additional copy of the table in its own data segment.

  4. Each process pays a performance penalty for the overhead of copying the table at run time.

  5. Because the space for the table is allocated when the executable is built, the application will not have enough room to hold any new messages you might want to add in the future. A functional interface overcomes these difficulties.

strerror() might be implemented as follows:
   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 0; 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 the data segment of a dynamically linked library, read-only data can be stored in its text segment. For more information on const, see ``Type qualifiers'' in ``C language compiler'', and the -Krodata option to cc(CP).

In a similar way, try to allocate buffers dynamically -- at run time -- instead of defining them at link time. This saves memory because only the processes that need the buffers get them. It also allows the size of the buffers to change from one release of the library to the next without affecting compatibility as shown in the following:

   char *
   	static char *buf = 0;

if (buf == 0) { if ((buf = malloc(BUFSIZE)) == 0) return 0; } ... return buf; }

Next topic: Minimize paging activity
Previous topic: Guidelines for building dynamically linked libraries

© 2003 Caldera International, Inc. All rights reserved.
SCO OpenServer Release 5.0.7 -- 11 February 2003