![]() |
|
||||
LynxOS User's Guide |
Shared Libraries
Users may have an application that relies on shared libraries. Or perhaps users are considering using shared libraries for an embedded application.
This chapter describes shared libraries and explains whether they should be used, how they are used, and how they are built.
Overview
A shared library is a collection of routines, organized so that code can be shared among processes; its primary goal is to reduce system and disk memory usage.
LynxOS supports tools to create and use C and C++ dynamic ELF shared libraries on all of its supported platforms.
Shared library development is supported by all native and cross development tools.
The term shared library is not entirely accurate when used with the ELF/SVR4 implementation. The correct term is shared object, because shared libraries are not archive (ar) libraries. They have more in common with partially linked (ld -r) object files. Shared objects can be identified by the .so suffix. This document, however, uses the term shared library when referring to shared objects.
The main difference in the treatment of LynxOS shared libraries and those on other systems at load-time, is that lazy linking is not supported for LynxOS shared libraries. The use of lazy linking makes application startup faster, but causes problems in real-time systems with deterministic behavior requirements.
Compatibility between different revisions of a library is easy to maintain, as long as the libraries are functionally compatible; applications linked with the library do not need to be relinked in order to use the newer version.
No source code changes are necessary when making a shared library. Object files that are to be part of the shared library must be compiled as Position Independent Code (PIC) using the -fPIC -mshared -shared compiler flags.
The LynxOS implementation also includes dlopen()/libdl.so support. This library provides functions that allow users to open, close, and find symbols in an arbitrary shared library, even if the application in use has not been linked with the library. For more details, see the dlopen() man page.
Creating Shared Libraries by Default
Some ELF-based systems, such as Linux, create shared objects by default, and make it necessary to provide a special option to the linker to produce statically linked objects. LynxOS takes the reverse approach. In LynxOS 4.0, the linker produces statically linked objects by default. To produce executables that link with shared objects at run-time, you must link with the option -mshared.
Single/Multithreaded Applications and Shared Libraries
When an application links with a shared object, it is important to note if the application uses single-threaded processes or mulithreaded processes. Processes that create multiple threads (using the system call pthread_create) should only call thread-safe functions. Thread-safe functions are coded in such a way that they work correctly even when they are concurrently executed by more than one thread.
On the other hand, processes that never create new threads can call non-thread safe functions without difficulty. Since non-thread safe functions may be faster than their thread-safe equivalents, users may prefer to use non-thread safe functions whenever possible. The usual practice is to keep thread-safe functions and non-thread safe functions in different libraries. LynxOS follows this practice with the libraries that it provides, and users should consider following this practice with their own libraries.
The libraries provided by LynxOS are kept in directories that distinguish thread-safe from non-thread safe libraries. The following table details the library types and locations:
Effects of Using Shared Libraries
In essence, when users modify the contents of a shared library, they modify all the programs that rely on it, unless they never deploy the new library, or only deploy it selectively. Using a shared library instead of an ordinary non-shared archive library may affect the following:
System Memory Usage
System memory usage is different because multiple processes can share the library code. The amount of system memory saved (or lost) by using a shared library depends on three factors:
- The contents of the shared library
- The set of programs that are typically run on the system
- The load on the system
(See "Determining the Use of Shared Libraries" for additional details and several examples).
The following relationships are true in general:
- A shared library consisting of commonly used routines saves more system memory than one that has many rarely used routines.
Routines and data items in the shared library that are rarely used waste system memory simply by their lack of use as follows:
- A rarely used routine wastes memory because the entire shared library resides in memory whenever it is used, regardless of which subset of routines from the library are actually linked.
- A rarely used data item wastes system memory equal to its size multiplied by the number of processes currently using the shared library. This is because each process has its own copy of the data.
- A shared library that is used by a varied and numerous assortment of programs saves more system memory than a shared library that is used by only one or two programs.
Multiple concurrent executions of the same program use the same amount of text memory as does a single execution. The operating system ensures that the executing text segments of the same program are shared.
Disk Space Usage
Disk space usage is typically lower because a program linked with a shared library is almost always smaller than the same program linked with an equivalent archive library. The shared library itself is not a factor in disk space usage because it is comparable in size to an equivalent archive library.
Consider this simple "hello world" program:
The printf() function is included in the C library. If the program is linked to the static C library, the resulting program executable file is close to 36 kilobytes:
If the same program is linked with the shared C library, then it is only about 5 kilobytes:
# gcc -mshared -o hellodynamic hello.c
# ls -l hellodynamic
-rwxr-xr-x 1 root 5133 Jan 24 01:02 hellodynamic*
In many cases, using shared libraries provides a considerable difference in size compared to static libraries.
Code Maintenance
Maintaining programs that use a shared library is somewhat easier than maintaining programs that use an archive library because if an error in the library is fixed, only the shared library needs to be remade. The programs do not need to be re-linked with the library because the operating system maps the shared library code into their address spaces at run time. In effect, modifying the contents of a shared library modifies all of the programs that use it.
Determining the Use of Shared Libraries
In order to decide whether or not to use shared libraries, users must take the following factors into consideration:
- The number of different applications that are to run concurrently on the target system
- The percentage of the library code that applications use
- The size of the library's data and bss sections
- The amount of RAM available on the target system
- The amount of disk, flash, or ROM space available on the target system
- The ease of updating and fixing bugs on shared libraries
- The possibility of performance degradation if using shared libraries
If a target system is running a single application, multiple instances of a single application, or multiple different applications (not concurrently), the using of shared libraries will probably increase memory and disk space usage. If, however, the target system is running many different applications at the same time (and they use large portions of the same shared libraries), there may be significant reductions in memory and disk space usage.
For comparison purposes, the space requirements of three sets of applications are explored in three pairs of tables that follow. All applications in a given set require a common library.
In the tables "Space Requirements for a 1 MB Library (X) Used by 6 Applications" "Space Requirements with a 1 MB Library (Y) Used by 6 Applications" and "Space Requirements with a 1 MB Library (Z) that Includes Data" space requirements are shown when the associated library is implemented as a non-shared library.
In the tables "Space Requirements for 1 MB Shared Library (X) Used by 6 Applications" "Space Requirements with a 1 MB Shared Library Used by 6 Applications" and "Space Requirements with a 1 MB Library (Z) that Includes Data" space requirements are shown when the associated library is implemented as a shared library.
Example 1
Each of the applications shown in the next two tables uses half of a 1 MB library, but uses a different mixture of the library's routines. Where these applications do not use a shared library, they require 3 MB of RAM and disk space. When they employ a shared library, they require only 1 MB of RAM and disk space.
Space Requirements for a 1 MB Library (X) Used by 6 Applications
Space Requirements for 1 MB Shared Library (X) Used by 6 Applications + library text used by application
Example 2
Each application shown in the next two tables uses 10% of a 1 MB shared library. Each application uses a different portion of the library, all mutually disjoint. Where these applications do not use a shared library, they require 0.6 MB of RAM and disk space (with all six applications running). When using a shared library, they require 1 MB of RAM and disk space.
This is a worst-case scenario; it is not possible to save any space by using a shared library for this mix of applications. This example is for illustration purposes only. It is unlikely that a group of applications would use completely disjoint sets of library routines.
Space Requirements with a 1 MB Library (Y) Used by 6
Applications
Space Requirements with a 1 MB Shared Library Used by 6 Applications + library text used by applicationThe preceding examples assume that the library contains no data or bss sections. While this is the best way to build a shared library, it is not always possible to do so.
Due to the inability of applications to share data, the data and bss sections of a shared library are not treated the same way as the text section: Every process gets a full copy of the data and bss sections whether it uses all of it or not.
Example 3
In the applications shown in the next two tables, the issues surrounding bss and data section utilization in a shared library are illustrated. Both shared and non-shared versions of the same library contain 0.5 MB of text, 0.3 MB of data, and 0.2 MB of bss. Each application uses forty percent of the text and forty percent of the data and bss sections.
In the case of the non-shared library (the table below), memory usage is 2.4 MB and disk usage is 1.9 MB. In the case of the shared library (the second table), the memory usage has increased to 3.5 MB, while the disk usage has dropped to 0.8 MB.
Space Requirements with a 1 MB Library (Z) that Includes Data 0 0 .4 MB .4 MB .4 MB .4 MB .4 MB .3 MB .4 MB .2 MB .4 MB .3 MB .4 MB .3 MB 2.4 MB 1.9 MBAs the preceding examples show, a well designed shared library may provide a significant savings in the memory space needed. In an embedded system with tight memory constraints, these savings could mean the following:
- A given application now fits in the space allowed on board.
- RAM can be decreased (cost savings).
- More space for new features
The preceding examples also show that a well designed shared library may provide a significant savings in the disk, flash, or ROM space needed. In an embedded system with tight memory constraints, these savings could mean the following:
- A given application now fits in the space allowed.
- The flash/ROM chip count can be decreased (cost savings).
- The flash/ROM chip size can be decreased (cost savings).
- A disk may not be needed (cost savings).
Choosing Shared Library Contents
Choosing the contents of a shared library involves more considerations than choosing the contents of an ordinary non-shared archive library. It is perhaps the most important factor determining whether or not the shared library decreases system memory usage.
When choosing routines for the shared library, use the following guidelines:
- It is generally a good idea to include routines such as printf that are used often by many different programs.
- It is generally a bad idea to include routines that are rarely used. Recall that when a shared library is used, it is loaded into memory, regardless of what routines have been linked.
- It is generally a bad idea to include routines that have large amounts of data. Recall that the data of a shared library is not shared, so each process that uses the library gets its own copy of the data. Even if a routine is commonly used, it can still waste space, because any program that does not happen to use it still gets a copy of its data.
How to Save Space
In order to minimize the space used by a shared library, global data should be minimized. One technique for doing this is to use local (stack) variables instead of global variables wherever possible. Another technique is to allocate buffers dynamically (for example, with malloc()) instead of statically. These are effectively identical methods of reducing the data section of the shared library. This is important because each process using the shared library gets its own separate copy of the data section.
The above space-saving techniques have the added benefit of making maintenance of the shared library easier by reducing the number of external symbols in the library. The number of external symbols can be further reduced by explicitly using static variables wherever possible.
Updating Shared Libraries
The way that shared libraries are designed allows for an application to be updated (by rebuilding and replacing the target shared library) without the need to relink the application. This means that bug-fixes or other library changes can be made in the field without replacing the entire application. This feature of shared libraries may require extra work on the part of the library designer to ensure that a new library is compatible with previous versions.
To support compatibility between newer and older versions of the library, shared libraries use a global offset table to access function calls. This adds one or more additional instructions to the normal function call process. These extra instructions produce slightly longer execution times for library calls. In general, the performance decrease is not measurable.
Libraries Provided
LynxOS provides the following system shared libraries. Single-threaded versions are located in the /lib/shlib directory; multi-threaded versions are located in the /lib/thread/shlib directory. The X and Motif shared libraries are located in /usr/lib/shlib.
Creating Shared Libraries
Shared libraries are easy to create. The following example shows how to create a shared library named myshared.so from the file myshared.c:
The option -shared is used to create a shared object.
The option -fPIC causes the compiler to produce position-independent code. In other words, it produces text that does not contain pointers in its read-only area. Instead, the text works with a jump table so that it can be dynamically linked to functions in the shared libraries, whose locations in memory are not known until run-time. At run-time, the addresses of those functions are determined and stored in the jump table. When the function is called, the call address is fetched from the jump table.
Linking to a Shared Library
In order to link with one of the shared libraries supplied by LynxOS, users must tell the compiler/linker to look for libraries in one of the shared library directories. There are two ways to do this: by specifying the -mshared flag; or by specifying the directory to search using the -L directory flag.
The following commands are for single-threaded applications. Either command can be used; both are equivalent:
The -mshared option links the executable with the dynamic shared libraries in /lib/shlib/.
The following commands are for multi-threaded applications (i.e., if the program calls pthread_create). Either command can be used; both are equivalent. The user needs to choose thread-safe binary functions. Use the option -mthreads to the gcc command.
$ gcc -o output_file source_files -mthreads -mshared
$ gcc -o output_file source_files -mthreads -L \ /lib/thread/shlib
The use of -mshared and -mthreads together selects the dynamic shared libraries that are thread-safe, found in /lib/thread/shlib.
![]() LynuxWorks, Inc. 855 Branham Lane East San Jose, CA 95138 http://www.lynuxworks.com 1.800.255.5969 |
![]() |
![]() |
![]() |
![]() |