 | Level: Introductory Chakarat Skawratananond (chakarat@us.ibm.com), Linux on POWER Technical Consultant, IBM Matt Davis (mattdavis@us.ibm.com), Linux on POWER Technical Consultant, IBM
15 Feb 2005 Accelerate your porting efforts by following this six-step guide. Learn the differences between Solaris and Linux™ on POWER™ that you commonly encounter during a port. Get an introduction to the development environment for Linux running on IBM® POWER processor-based systems, and see how SUN's compiler/linker switches compare with those of GNU GCC and the IBM native compiler. Finally, learn about tools for performance analysis and software packaging for Linux on POWER.
Introduction
Generally, porting Solaris-based applications to Linux is a simple task since both Solaris and Linux are based on UNIX®. Porting often requires only a recompile with minor
changes in some compiler and linker switches. Only when applications depend on system-specific implementations do they require major modifications. While both Solaris and Linux are designed to follow standards, differences in their implementations sometimes occur. This guide highlights these differences and recommends possible solutions, if an equivalent on the Linux side is not available.
This paper is organized as follows:
- Proposed steps for migration
- Endianness and 32- and 64-bit porting issues
- Development environment for Linux on POWER
- Differences between Solaris and Linux
- Performance tuning and software packaging tools available for Linux on
POWER
About this guide
This guide focuses on migration from Solaris running on 32-bit or 64-bit SPARC, Intel,
or AMD x86 architectures to Linux running on POWER architecture. On the Solaris side, we base our discussion on the version 9 and later. On the Linux side, we focus on the distributions available on IBM POWER processor-based servers: SUSE LINUX Enterprise Server 9 and Red Hat Enterprise Linux AS V3 update 4.
Porting roadmap
The following steps provide a roadmap for successful migration:
Step 1: Prepare
Research to understand the differences between the source and target platforms. For example, you need to know if the endianness on the source platform is different from the endianness on the target platform. If your source platform is Solaris/x86, then
endianness should be under your consideration since Linux on POWER is big-endian, while Solaris/x86 is little-endian.
Determine, also, if all the required third party packages (such as databases and
class libraries) are available on the target platform. For 32-bit applications, consider if it
is necessary to migrate to 64-bit. The next section, General Porting Notes, provides more information about endianness and 32- to 64-bit issues.
Also, decide which compiler to use on the target platform. If your application is performance sensitive, consider using the native compiler. The section Development Environment for Linux on POWER provides more information about the available compilers for Linux on POWER.
Step 2: Configure
This step involves setting up the development environment, setting environment
variables, making changes to makefiles, and so on. At the end of this stage, you should be ready
to start building your application.
Step 3: Build
This step involves fixing compiler errors, linker errors, and more. It is not unusual for code to go through several iterations of Steps 2 and 3 before a clean build is produced.
Step 4: Run-time test and debug
After the application is successfully built, test it for runtime errors.
Step 5: Performance tuning
Now the ported code is running on the target platform. Monitor performance to ensure the ported code performs as expected. If not, performance tuning must be completed. The Performance Tuning section of this document provides more information
about the performance tuning tools available for Linux on POWER.
Step 6: Packaging
Will you have to distribute the resulting code to others? If so, what packaging method
will you want to use? Linux provides several ways to package your application such as a
tarball, self-installing shell script, or RPM. The section Software Packaging provides more information about software packaging for Linux.
General porting notes
This section provides information about when to migrate applications from 32-bit to 64-bit, as well as considerations for applications crossing an endianness barrier. Applications
migrating from Solaris on x86 platforms will cross an endian barrier. Developers familiar
with 32-bit to 64-bit migration and byte ordering may wish to skip to the next section,
Development Environment for Linux on POWER.
32-bit to 64-bit
Once you decide to port to Linux on POWER, you may also consider porting the application to 64-bit, if it is currently 32-bit. However, 32-bit applications that do not require 64-bit capabilities should remain 32-bit. Any porting effort involving a migration to 64-bit should be taken in two steps: First, migrate to the target platform as 32-bit, and only then proceed to 64-bit. Otherwise, issues arising from the platform migration can be easily confused with issues arising from the migration to 64-bits. That said, an application should be ported to 64-bit, if it can:
- benefit from more than 4 GB of virtual address space
- benefit from more physical memory (greater than 4 GB), and if its users are likely to deploy it on a system with more than 4GB of physical memory
- benefit from 64-bit size integers
- benefit from full 64-bit registers to do efficient 64-bit arithmetic
- use files larger than 2 GB
The application can remain 32-bit and still run on the 64-bit Linux on POWER kernel
without requiring any code changes. IBM Linux on POWER processor-based servers
support both 32-bit and 64-bit applications running simultaneously on the 64-bit
architecture without any performance degradation between modes.
When converting 32-bit applications to 64-bit applications, two basic issues are likely to arise: Data type inconsistency and interoperation between applications using different
data models.
The 64-bit environment uses a different data type model than the 32-bit environment. For Linux on POWER, the ILP32 model is used in the 32-bit environment, while the LP64 is used in the 64-bit environment. The differences between these two models are the sizes of long and pointer. Therefore, when an application is converted from ILP32 to LP64, fundamental changes in data type occur. Longs and integers are no longer the same size. Pointers and integers are no longer the same size. Also, the size of objects like structs and unions is bigger in the 64-bit environment if they include pointer or long data types. Other issues commonly spawned by these differences are:
- Data truncation
- Pointer assignment
- Data alignment
- Data sharing
The topic of migrating applications from 32-bit to 64-bit environments has been covered extensively in other publications. We, therefore, will not go into detail about how to deal
with these porting issues in this article. Refer to Chapter 3 of the "AIX 5L Porting Guide," an IBM Redbook, for detailed information about porting from 32-bit to 64-bit.
Byte ordering (endianness)
Since both SPARC and POWER architectures are big-endian, there will be no endianness issues when porting applications between these platforms. However, if you are porting Solaris on x86 applications to Linux on POWER, you may need to deal with endianness portability issues since x86 is a little-endian architecture.
Endianness issues are often encountered during the process of migrating applications from one type of architecture to another. Endianness is the ordering of a data element
and its individual bytes as they are stored and addressed in memory. There are two types
of endianness: big-endian and little-endian.
For big-endian processors, such as POWER, PowerPC™, and SPARC, when a word is placed in memory, starting from the lowest address, the most significant byte will be
placed first. On the other hand, for little-endian processors, such as Intel and Alpha processors, the least significant byte will be placed first.
The following diagram shows how big-endian and little-endian processors place a
hexadecimal value, like 0x89ABCDEF, in its memory:
Figure 1. Byte ordering (endianness)
One source of endianness problems is non-uniform data reference. It is often featured by data type mismatches resulting from either data element casting, use of a union data
structure, or the use and manipulation of bit fields.
The following source code illustrates the endianness issue due to the non-uniform data reference using a pointer:
Listing 1. Non-uniform data reference
int main() {
int value;
char *p;
p = (char*) &value;
value = 0x89ABCDEF;
printf(“%X.%X.%X.%X\n" p[0], p[1], p[2], p[3]);
return 0;
} |
Development environment for Linux on POWER
This section describes the elements of a Linux on POWER development environment with which you should be familiar.
Linux distributions
Linux for POWER is offered in enterprise form by two leading Linux vendors: SUSE
LINUX and Red Hat. While both are highly respected by the Linux community, their offerings do differ. For most vendors, however, there is no problem deploying software from the same code base across either offering. Developers familiar with the specific levels of the development environment in SLES9, RHEL3, and Solaris 10 may wish to skip to the next section, Differences Between Solaris and Linux on POWER.
- SLES9
- SUSE LINUX Enterprise Server, version 9 (SLES9) is based on the Linux 2.6
kernel and supports all enterprise features of Linux on POWER hardware. The development environment provides GCC 3.3.3, glibc 2.3.3, and the GNU binutils 2.15.90. This is in contrast to GCC 3.3.2 and glibc 2.2.3 available on Solaris 10. The IBM JDK 1.4.2 is also provided with SLES9. Many other open source development libraries, such as perl, python, and gtk are also packaged with SLES9. Thus, environments for 32-and 64-bit development are available for C, C++, and Java™ technology.
- RHEL3
- Red Hat Enterprise Linux, version 3, update 4 is based on the Linux 2.4.21 kernel,
and, thanks to extensive backporting from the Linux 2.6 kernel, RHEL3 offers 2.6 kernel features, such as simultaneous multi-threading (SMT) and Native POSIX Thread Library (NPTL). The RHEL3 development environment, as with SLES9, provides tools for both 32- and 64-bit development of C, C++, and Java technology, as well as common open source libraries. This is accomplished with GCC 3.2.3, glibc 2.3.2, the GNU binutils 2.14.90, and the IBM JDK 1.4.2.
Compilers
Linux on POWER, like Solaris, offers a high performance compiler set in addition to the GNU Compiler Collection -- the IBM XL C/C++ compiler set. Details about its
licensing are available at the XL C/C++ Advanced Edition for Linux Web site. (See Resources.)
Here is a brief overview of each compiler set and their advantages:
- GCC
- For projects developed across multiple platforms where a GCC compiler is the
original compiler, the GCC compiler is often used to deploy applications for Linux on POWER. This is especially true for applications where performance is not critical; small utilities, for example. GCC also allows some code prototypes that are only understood by GCC, such as GCC specific macros. However, many of these “GCC-isms" are incorporated in the IBM XL C/C++ compilers.
- XL C/C++
- XL C/C++ V7.0 is the follow-on release to VisualAge V6.0 for Linux. XL C/C++
compilers offer a high performance alternative to GCC, as well as a number of additional features. Fortunately, the XL C/C++ compilers produce 32- and 64-bit GNU elf objects, which are fully compatible with the objects GCC compilers produce because the XL C/C++ compilers employ the same GNU libraries, linker, assembler, and binutils as GCC. In fact, functionality formerly exclusively offered by GCC compilers has been ported to XL C/C++ to facilitate source compatibility; some GCC macros and inline functions, for example. But aside from compatibility, XL C/C++ offers unparalleled performance.
Sophisticated XL C/C++ optimization routines have been developed for and tailored to the POWER architecture, and it shows. High performance computing applications, especially those heavily reliant on floating point operations, often benefit significantly from a simple recompile with XL C/C++.
In addition to algorithmic optimization routines, XL C/C++ provides some architecture specific support not yet available in GCC -- specifically, support for VMX vector instructions featured in the IBM eServer® BladeCenter™ JS20 product. These instructions are supported by GCC 3.3 (SLES9, but not RHEL3 version of GCC), but only XL C/C++ version 7 offers automatic vectorization of unvectorized code and optimizes it for POWER VMX instructions.
While XL C/C++ for Linux on POWER is not free, it is available to developers at a reduced rate and as a free trial. Details are available at the XL C/C++ Advanced Edition for Linux Web site. (See Resources.)
- Binutils
- On Solaris 10, you have the option of using the native build utilities or the utilities provided with GNU . With Linux on POWER, the GNU binutils are used to generate objects with both the XL C/C++ compiler set and GCC. They are reviewed in significant detail at the GNU Binutils site. (See Resources.) In addition to the standard binutils, developers should be aware of modified versions of elflint, nm, readelf, size, and strip; these are the elfutils tools packaged on RHEL3. These tools have been rewritten for Linux-specific functionality, but the regular GNU binutils versions of these programs are still available as well.
Java technology
Java technology allows developers to deploy across multiple platforms without
recompiling code, provided that the Java Runtime Environment is compatible with the
Java Developer Kit (JDK) for the platform on which the software was developed. Linux
on POWER Java development is supported on both RHEL3 and SLES9 by the IBM Developer Kit for Linux, Java Technology Edition version 1.4.2. (See Resources.) This developer kit is available in both 32- and 64-bit versions at no cost from IBM . The IBM developer kit is also packaged with both RHEL3 and SLES9 media, and updates are supported by the distributions maintenance packages.
- 64-bit Java and NPTL
- Threaded 32- and 64-bit development on RHEL3 can be done with NPTL or the
older Linux Threads libraries. However, only the NPTL 64-bit threading libraries
are supported for Java. LinuxThreads is not supported with the 64-bit version of
the IBM JDK 1.4.2 for Linux on POWER.
Differences between Solaris and Linux on POWER
In this section, learn about the differences between Solaris and Linux on POWER, including system calls, signals, data types, makefiles, compiler and linker options, and threading libraries.
System calls and library functions
System calls are interfaces used by user programs to have the kernel perform a specific function on behalf of the calling thread or process. To execute a system call, a context switch to the privileged kernel mode is required. Library functions are normal C functions. They do not have any context switching overhead since they are part of the address space for each process that uses them. Some Solaris system calls and library functions may not be available on Linux. When this happens, a wrapper-call may have to be implemented on the Linux side.
Linux on POWER currently provides 256 different system calls. A list of system calls
can be found in /usr/include/asm-ppc/unistd.h or /usr/include/asm-ppc64/unistd.h.
Here are a few examples of incompatibilities between Solaris and Linux:
-
regexp() and regcmp(): The routines regexp() and regcmp() on Solaris need to be replaced by regexp() and regcmp() on Linux.
- Dirent structure on Solaris is different from that on Linux:
Listing 2. Dirent structure on Linux
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino;
__off_t d_off;
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256];
};
|
Listing 3. Dirent structure on Solaris
typedef struct dirent {
ino_t d_ino; /* "inode number" of entry */
off_t d_off; /* offset of disk directory entry */
unsigned short d_reclen; /* length of this record */
char d_name[1]; /* name of file */
} dirent_t;
|
- File system interface routines: Solaris file system routines employ
vfstab structures and contain vfs in the function name, such as getvfsent. Linux provides equivalent interfaces, but the routines use fstab structures and contain fs in the routine name, such as getfsent. The vfstab structure on Solaris is defined in /usr/include/sys/vfstab.h. The definition of fstab on Linux is in /usr/include/fstab.h.
- Device Information Library Functions (libdevinfo): The libdevinfo library contains
a set of interfaces for accessing device configuration data, such as major and minor numbers. The standard Linux installation does not support this.
- CPU Affinity: By default, a process in a multiprocessor system bounces among
several CPUs. By explicitly binding a process to certain CPUs (assigning CPU affinity) may yield performance gain in some cases. System calls for CPU affinity on Solaris are different from those on Linux. The Linux 2.6 kernel provides
sched_setaffinity() and sched_getaffinity() for CPU affinity. Please consult the Linux man pages for more information.
The following table lists the Solaris system calls that either use a different name, signature, or are not available on Linux:
Table 1. Solaris system calls
|
Solaris
|
Linux
|
Notes
|
| acl, facl | N/A | get or set a files’s Access
Control List (ACL) | | adjtime | N/A | correct the time to allow synchronization of the system clock | | audit | N/A | write a record to the audit log | | auditon | N/A | manipulate auditing | | auditsvc | N/A | write audit log to specified file descriptor | | getacct, putacct, wracct | N/A | get, put, or write extended accounting data | | getaudit, setaudit,
getaudit_addr, setaudit_addr
| N/A | get and set process audit information | | getauid, setauid | N/A | get and set user audit identity | | getdents | getdents** | read directory entries and put in a file system independent
format. Dirent structure on Linux and Solaris are different. | | getmsg, getpmsg | N/A | get next message off a stream | | getpflags, setpflags | N/A | get or set process flags | | getppriv, setppriv | N/A | get or set a privilege set | | getustack, setustack | N/A | retrieve or change the address of per-LWP stack boundary information | | issetugid | N/A | determine if current executable is running setuid or setgid | | llseek | _llseek | move extended read/write file pointer | | _lwp_cond_signal, _lwp_cond_broadcast | N/A | signal a condition variable | | _lwp_cond_wait, _lwp_cond_timedwait, _lwp_cond_reltimedwaid | N/A | wait on a condition variable | | _lwp_info | N/A | return the time-accounting information of a single LWP | | _lwp_kill | N/A | send a signal to a LWP | | _lwp_mutex_lock, _lwp_mutex_unlock, _lwp_mutex_trylock | N/A | mutual exclusion | | _lwp_self | N/A | get LWP identifier | | _lwp_sema_wait, _lwp_sema_trywait, _lwp_sema_init, _lwp_sema_post | N/A | semaphore operations | | _lwp_suspend, _lwp_continue | N/A | continue or suspend LWP execution | | memcntl | N/A | memory management control | | meminfo | N/A | provide information about memory | | mount | mount* | mount a file system. The signatures of this system call on Solaris and Linux are different. | | msgids | N/A | discover all message queue identifiers | | msgrcv | msgrcv* | message receive operation. Linux uses struct msgbuf type in one of its argument. | | msgsnap | N/A | message queue snapshot operation | | msgsnd | msgsnd* | message send operation. Linux uses struct msgbuf type in one of its argument. | | ntp_adjtime | N/A | adjust local clock parameters | | ntp_gettime | N/A | get local clock values | | open, openat | open* | open a file. openat() is not available in Linux. | | pcsample | N/A | program execution time profile | | p_online | N/A | return or change processor operational status | | priocntl | N/A | process scheduler control | | priocntlset | N/A | generalized process scheduler control | | processor_bind | sched_setaffinity | bind LWPs to a processor | | processor_info | N/A | determine type and status of a processor | | pset_bind | N/A | bind LWPs to a set of processors | | pset_create, pset_destroy, pset_assign | N/A | manage sets of processors | | pset_info | N/A | get information about a processor set | | pset_list | N/A | get list of processor sets | | pset_setattr, pset_getattr | N/A | set or get processor set attributes | | putmsg, putpmsg | N/A | send a message on a stream | | rename, renameat | rename* | change the name of a file. Linux does not have renameat(). | | resolvepath | N/A | resolve all symbolic links of a path name | | semids | N/A | discover all semaphore identifiers | | setrctl, getrctl | N/A | set or get resource control values | | settaskid, gettaskid, getprojid | N/A | set or get task or project IDs | | shmids | N/A | discover all shared memory identifiers | | sigsend, sigsendset | N/A | send a signal to a process or a group of processes | | __sparc_utrap_install | N/A | install a SPARC V9 user trap handler | | fstatat | N/A | get file status | | swapctl | N/A | manage swap space | | uadmin | N/A | administrative control | | unlink, unlinkat | unlink* | remove directory entry. Linux does not have unlinkat(). | | futimesat | N/A | set file access and modification times. It resolves the path relative to
the fildes argument. | | waitid | N/A | wait for child process to change state | | yield | sched_yield | yield execution to another lightweight process |
Signals
Signals are used to notify a process or thread of a particular event. Linux supports both POSIX standard signals and POSIX real-time signals. Every signal has a unique name and a corresponding signal number. Several signal numbers are architecture dependent. For example, the signal number of SIGSTOP for Solaris is 23, but it is 19 for Linux on POWER. The definition of signal numbers on Linux can be found at /usr/include/bits/signum.
For all possible signals, the system defines a default action to take when a signal occurs:
- Terminate:
- Default action is to terminate the process.
- Ignore:
- Default action is to ignore the signal.
- Core:
- Default action is to terminate the process and dump core.
- Stop:
- Default action is to stop the process.
For a full list of Linux signals, including a short description of each and the default
behavior when the signal is delivered, consult the signal man page in Section 7 by
invoking the following command:
Note that for Linux:
- SIGABRT and SIGIOT are identical
- SIGCLD and SIGCHLD are identical
- SIGPOLL and SIGIO are identical
- Default action for SIGPWR for Linux is to terminate the process, but ignore on
Solaris.
The following signals are supported by Solaris, but not by Linux on POWER:
Table 2. Signals supported by Solaris
|
Name
|
Default action
|
Description
|
| SIGEMT | core | Emulation trap | | SIGWAITING | ignore | Concurrency signal used
by threads library | | SIGLWP | ignore | Inter-LWP signal used by
threads library | | SIGFREEZE | ignore | Checkpoint suspend | | SIGTHAW | ignore | Checkpoint resume | | SIGCANCEL | ignore | Cancellation signal used
by threads library | | SIGLOST | ignore | Resource lost (but
supported in Linux
running on Sparc) | | SIGXRES | ignore | Resource control exceeded |
sigset_t is defined differently on Solaris and Linux.
On Linux, it is defined in /usr/include/bits/sigset.h as:
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long
int)))
typedef struct
{
unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
#endif
|
On Solaris, it is defined in /usr/include/sys/signal.h as:
typedef struct { /* signal set type */
unsigned int __sigbits[4];
} sigset_t;
|
Base data type and alignment
There are two different classes of data types available on a system: base data types and derived data types.
Base data types are all data types defined by the C and C++ language specification. The following table compares base data types for Linux on POWER and Solaris:
Table 3. Base data types
|
|
Linux on POWER |
Solaris |
| Base type | ILP32 | LP64 | ILP32 | LP64 | | char | 8 | 8 | 8 | 8 | | short | 16 | 16 | 16 | 16 | | init | 32 | 32 | 32 | 32 | | float | 32 | 32 | 32 | 32 | | long | 32 | 64 | 32 | 64 | | pointer | 32 | 64 | 32 | 64 | | long long | 64 | 64 | 64 | 64 | | double | 64 | 64 | 64 | 64 | | long double | 64/128* | 64/128* | 128 | 128 |
*The default size for long double in Linux on POWER is 64 bits. They can be increased to 128 bits if the compiler option
–qlongdouble
in XL C/C++ compiler is used.
When porting applications between platforms or between 32-bit and 64-bit modes, you
need to take into account the differences between alignment settings available in the
different environments to avoid possible degradation in performance and data corruption.
For IBM XL C/C++ compiler, each data type within aggregates (C/C++ structures/unions
and C++ classes) will be aligned along byte boundaries according to the either linuxppc
or bit-packed rules, where linuxppc is the default. linuxppc is compatible with the default
GCC alignment rules.
The following table shows the alignment values in byte for linuxppc and bit-packed:
Table 4. Alignment values
|
Data type
|
linuxppc
|
bit-packed
|
| bool | 1 | 1 | | char | 1 | 1 | | wchar_t (32-bit) | 2 | 1 | | wchar_t (64-bit) | 4 | 1 | | intl | 4 | 1 | | short | 2 | 1 | | long (32-bit) | 4 | 1 | | long (64-bit) | 8 | 1 | | long long | 8 | 1 | | float | 4 | 1 | | double | 8 | 1 | | long double | 8 | 1 |
System derived data types
A derived data type is a derivative or structure of existing base types or
other derived types. System derived data types can have different byte sizes depending
on the employed data model (32-bit or 64-bit) and the hardware platforms. The following table shows some of the derived data types on Linux that are different from those on Solaris:
Table 5. Derived data types
| OS |
gid_t |
mode_t |
pid_t |
uid_t |
wint_t |
| Solaris ILP32 l | long | unsigned long | long | long | long | | Solaris LP64 | int | unsigned int | int | int | int | | Linux ILP32 | unsigned int | unsigned int | int | unsigned int | unsigned int | | Linux ILP64 | unsigned int | unsigned int | int | unsigned int | unsigned int |
GNU Make versus Solaris Make
If you use Solaris Make in the source platform, you need to modify your makefile in order to use GNU Make on Linux.
To obtain detailed information about the difference between Solaris Make and GNU
Make, please consult Chapter 6 of the "AIX 5L Porting Guide."
Compiler and linker options
As mentioned earlier, there are two compilers available for Linux on POWER: GNU GCC and IBM XL C/C++ V7.0. Here is a list of widely used compiler options for SUN Studio C/C++ compiler and the equivalent options for the GNU GCC and IBM’s XL C/C++ compilers:
Table 6. Compiler options
| SUN Studio option |
GNU GCC option |
XL C/C++ option |
Description |
| -# | -v | -v | Instructs the compiler to report information on the progress of the compilation. | | -### | -### | -# | Traces the compilation without invoking anything. | | -Bstatic | -static | -qstaticlink | Causes the link editor to look only for files named libx.a. | | -Bdynamic | (Default) | (Default) | Instructs the link editor to look for files name libx.so and then libx.a when given the –lx option. | | -G | -shared | -qmkshrobj | Produce a shared object rather than a dynamically linked executable. | | -xmemalign | -malign-natural,
-malign-power | -qalign | Specifies the maximum assumed memory alignment and the behavior of misaligned data accesses. | | -xO1, -xO2, -xO3, -xO4,-xO5 | -O, -O2, -O3 | -O,-O2,-O3,-O4,-O5 | Optimizes code at a choice of levels during compilation. | | -KPIC | -fPIC | -qpic=large | Generates position-independent code for use in shared libraries (large model). | | -Kpic | -fpic | -qpic=small | Generates position-independent code for use in shared libraries (small model). | | -mt | -pthread | Use _r invocation mode | Compile and link for multithreaded code. | | -R dirlist | -Wl, -rpath, dirlist | -Wl, -rpath, dirlist | Builds dynamic library search path into the executable file. | | -xarch | -mcpu, -march | -qarch, -qtune | Specifies the target architecture instruction set. |
Solaris thread and NPTL
This section addresses issues that developers face when migrating multithreaded
applications from Solaris to Linux.
Solaris supports two threading implementations: Solaris threads and POSIX threads
(pthreads). There are a few functions implemented by the Solaris threads API that are not implemented by the pthreads API, and vice versa. For those functions that do match, the associated arguments might not. The following features are exclusively supported by the Solaris
threads API:
- The ability to create daemon threads. Daemon threads do not affect the process exit status. A process can exit either by calling
exit(), or by having every non-daemon thread in the process call thr_exit().
- The ability to suspend or continue the execution of the thread using
thr_suspend() and thr_continue(). Note that thr_suspend() suspends the target thread with no regard to the locks that the thread might be holding. If the suspending thread calls a function that requires a lock held by the suspended target thread, deadlock occurs.
- The ability to allow a thread to wait for any undetached thread in the process to
terminate. This is achieved when the first argument of
thr_join() is set to 0. If you set the first argument of pthread_join() to 0, the program will be terminated with a segmentation fault.
The following table compares the key Solaris threads functions with the pthreads
functions. For other Solaris threads functions, please consult the "Multithreaded Porting Guide" from SUN. (See Resources.)
Table 7. Threads and pthreads functions
| Solaris threads API |
Linux POSIX threads API |
Description |
| thr_create() | pthread_create() | Creates a new thread of control. | | thr_exit() | pthread_exit() | Terminates the execution of the calling thread. | | thr_join() | pthread_join() | Suspends the calling thread until the target thread completes. | | thr_kill() | pthread_kill() | Sends a signal to another thread. | | thr_self() | pthread_self() | Returns the thread ID of
the calling process. | | thr_yield() | sched_yield() | Makes the current thread
yield to another thread. | | thr_getprio() | pthread_getschedparam() | Retrieves a thread's priority parameters. | | thr_setprio() | pthread_setschedparam() | Modifies a thread's priority parameters. | | thr_getspecific() | pthread_getspecific() | Binds a new thread-specific
value to the key. | | thr_setspecific() | pthread_setspecific() | Binds a new thread-specific
value to the key. | | thr_getconcurrency() | pthread_getconcurrency() | Gets thread concurrency
level. | | thr_setconcurrency() | pthread_setconcurrency() | Sets thread concurrency
level. | | thr_sigsetmask() | pthread_sigmask() | Changes or examines the
calling thread's signal
mask. | | thr_keycreate() | pthread_key_create() | Creates a key that locates
data specific to a thread. | | N/A | pthread_key_delete() | Deletes a key that locates
data specific to a thread | | thr_suspend() | N/A | Suspends the execution of
the specified thread. | | thr_continue() | N/A | Resumes the execution of a
suspended thread. | | fork1() | fork() | Regular fork | | forkall() | N/A | Replicate all fork |
The behavior of fork() in Solaris 9 and earlier releases is different from the behavior of fork() in POSIX threads. In POSIX threads, fork() creates a new process, duplicating the complete address space in the child. However, it duplicates only the calling thread in the child process. Solaris threads API also provides the replicate all fork semantics, forkall(). This function duplicates the address space and all the threads in the child. This feature is not supported by POSIX thread standard.
There are some POSIX thread routines implemented in Solaris, but not in Linux, and vice versa. The following table lists those routines:
Table 8. POSIX thread routines
| Routine |
Solaris |
Linux |
| pthread_cond_reltimedwait_np | y | n | | pthread_mutexattr_getprioceiling | y | n | | pthread_mutexattr_getprotocol | y | n | | pthread_mutexattr_getrobust_np | y | n | | pthread_mutexattr_setprioceiling | y | n | | pthread_mutexattr_setprotocol | y | n | | pthread_mutexattr_setrobust_np | y | n | | pthread_mutex_consistent_np | y | n | | pthread_mutex_getprioceiling | y | n | | pthread_mutex_reltimedlock_np | y | n | | pthread_mutex_setprioceiling | y | n | | pthread_rwlock_reltimedrdlock_np | y | n | | pthread_rwlock_reltimedwrlock_np | y | n | | pthread_setschedprio | y | n | | pthread_attr_getaffinity_np | n | y | | pthread_attr_setaffinity_np | n | y | | pthread_cleanup_pop_restore_np | n | y | | pthread_cleanup_push_defer_np | n | y | | pthread_getattr_np | n | y | | pthread_kill_other_threads_np | n | y | | pthread_rwlockattr_getkind_np | n | y | | pthread_rwlockattr_setkind_np | n | y | | pthread_timedjoin_np | n | y | | pthread_tryjoin_np | n | y |
The Linux threading library in all the released versions of Linux prior to 2.6 is known as LinuxThreads. This library has been supported by GNU C library since glibc 2.0 and is largely POSIX-compliant. Starting in the 2.6 kernel, a new and improved threading library known as the Native POSIX Threading Library (NPTL) is introduced. This implementation provides a significant performance improvement over LinuxThreads. NPTL is also far more compliant with the POSIX specification than the LinuxThreads package was. However, simply using the 2.6 kernel does not mean that the NPTL is being used. Issue the following command to see which POSIX implementation you are using:
$ getconf GNU_LIBPTHREAD_VERSION |
NPTL implements the one-on-one threading model where there is a one-to-one relationship between user threads and kernel threads. NPTL also implements the inter-process POSIX synchronization primitives. Specifically, the thread option
PTHREAD_PROCESS_SHARED is now supported. By default, each thread is created with the detachstate attribute set to PTHREAD_CREATE_JOINABLE, scheduling policy
set to SCHED_OTHER, and no user-provided stack.
If your Solaris application uses POSIX threads API, then porting them to Linux will be straightforward. Note that even when using GCC, Solaris does not support NPTL. The following table shows the default values of attributes for POSIX threads on Linux:
Table 9. Default values of POSIX threads attributes on Linux
| Attribute |
Default Values |
| scope | PTHREAD_SCOPE_SYSTEM | | detachstate | PTHREAD_CREATE_JOINABLE | | schedparam | 0 | | inhiritsched | PTHREAD_EXPLICIT_SCHED | | schedpolicy | SCHED_OTHER |
Though many applications will migrate from the 2.4 kernel to the 2.6 without recompilation, the addition of NPTL may require minor modifications in multithreaded applications. Instructions for migrating applications by using LinuxThreads to NPTL is beyond the scope of this article. Additional information can be found in the LinuxDevices.com article "Migrating to Linux kernel 2.6." (See Resources.)
Performance tuning
Once the code has been ported and successfully executed on Linux, complete performance monitoring and tuning to ensure that the ported code performs well on the target platform. This section provides you with a list of tools available for Linux on POWER to help accomplish that.
Performance Inspector
This suite of tools can be used to identify performance problems in your application
(C/C++/Java) and shows how your application interacts with the Linux kernel. It consists
of seven tools:
-
TProf is a timer profiler that identifies what code is running on the CPU during a
user-specified time interval. It is used to report hot-spots in applications as well as
the kernel. Basically, it records which code is running at each system-clock
interrupt (100 times per second per CPU).
-
PTT collects per-thread statistics, such as number of CPU cycles, number of interrupts, and number of times that the thread was dispatched.
-
AI displays CPU utilization statistics during a user-specified interval.
-
JLM provides statistics on locks based in the Java 2 technology.
-
JProf is a shared library that interfaces with Java jvmpi interface.
-
POST generates reports based on outputs from other tools.
-
A2N is used by POST to map code execution to the application that was being
traced.
At the time of this writing, Performance Inspector supports SUSE LINUX Enterprise
Server 9 (2.6.5-7.97) and Red Hat Enterprise Linux V3.0 Update 2.
The article "Five easy-to-use performance tools for Linux on PowerPC" (developerWorks, June 2004) provides information about installing and utilizing Performance Inspector.
To download Performance Inspector and get more information, please visit the Performance Inspector home page. (See Resources.)
Post-Link optimization
This tool optimizes the executable image of a program by collecting information on the behavior of the program while the program is used for some typical workload. It then re-analyzes the program (together with the collected profile), applies global optimizations
(including program restructuring), and creates a new version of the program that is
optimized for that workload. The new program generated by the optimizer typically runs
faster and uses less real memory than the original program.
At the time of this writing, Post-Link Optimization tool is supported on the following
Linux distributions: SUSE LINUX Enterprise Server 8 and above, Red Hat Enterprise
Linux 3.
To download and get more information, please visit the alphaWorks Post-Link Optimization for Linux on POWER site. (See Resources.)
Oprofile
Oprofile provides profiles of code based on hardware-related events such as cache misses
or CPU cycles. For example, Oprofile can help you determine which of the source
routines causes the most cache misses. Oprofile utilizes hardware performance counters
provided in many CPUs including IBM POWER4™, POWER5™ and PowerPC™ 970. Please visit the Oprofile Web site for more information. (See Resources.)
Oprofile for Linux on POWER is also available in the SUSE LINUX SDK 9. (See Resources.)
Quick reference of Oprofile on an installed system can be found at
/usr/share/doc/packages/oprofile.
Software packaging
Application software is delivered to end users in a unit called a package. A package is a collection of related files (binaries, libraries, documentation, and source code) and metadata.
Metadata is used by the package management system to coordinate all of the pieces in the
package. Solaris uses pkgadmin as its package manager. RPM is the package
management system widely used on Linux. For further information about RPM, please
visit www.rpm.org. (See Resources.)
Note that the format of the package specification template files used by pkgadmin in
Solaris is different from the spec file used by RPM, and translating packaging
information from template file into spec files requires a substantial effort.
Summary
The porting effort from Solaris to Linux on POWER in most cases involves just a
recompile or minor changes in compiler/linker switches. However, the design of Solaris and Linux is fundamentally different. Solaris is focused on performance, scalability, and reliability, while sacrificing portability. On the other hand, Linux is designed with portability in mind, and it is supported on almost all hardware platforms available today. The Linux 2.6 kernel, however, has significantly improved performance, scalability and reliability from the 2.4 kernel. As a result, there are some system-specific features available on Solaris that are not available on Linux.
Resources
- Participate in the discussion forum.
- The IBM Redbook "AIX 5L Porting Guide" (July 2001) details the types of problems you most likely encounter when porting applications from other UNIX-based platforms to the AIX 5L Operating System.
- Get the XL C/C++ V7.0 compiler.
-
GNU binutils are used to generate objects with both the XL C/C++ compiler set and GCC. They are reviewed in significant detail at the GNU Binutils site.
- Get the IBM Developer Kit for Linux, Java Technology Edition version 1.4.2.
- The article "Migrating to Linux kernel 2.6" (LinuxDevices.com, March 2004) discusses topics related to migrating existing applications to the 2.6 kernel and the Native POSIX Threading Library (NPTL).
- Download Performance Inspector and get more information about this suite of tools.
- The article "Five easy-to-use performance tools for Linux on PowerPC" (developerWorks, June 2004) provides information about installing and utilizing Performance Inspector, which you can use to analyze performance of your C/C++ and Java applications, as well as performance of your system as a whole.
-
Post-Link Optimization for Linux on POWER is a performance-tuning utility used to improve the execution time and the real memory utilization of user-level application programs, based on their run-time profiles.
- Visit the Oprofile Web site. Oprofile for Linux on POWER is also available in the SUSE LINUX SDK 9.
- Visit www.rpm.org for further information about RPM.
-
Port Your Solaris Application to Linux on POWER to assess whether Linux on POWER is the right choice for you. Learn about many no-charge education opportunities, set up your development environment, remotely access POWER hardware for testing your applications, and leverage IBM marketing and sales support.
- The developerWorks Migration Station assists developers in finding tools, technical articles, online tutorials, classes, forums, Webcasts, customized services, and other forms of assistance to help you get your Linux migration for a variety of hardware platforms on the fast track.
- To learn more about Linux on Power, visit the IBM developerWorks: Linux on Power Architecture developer's corner to find technical documentation, education, downloads, product information, and more.
- The Linux on POWER ISV Resource Center provides resource for Independent Software Vendors interested in enabling and promoting their applications for Linux on Power.
- Find more resources for Linux developers in the developerWorks Linux zone.
- For additional information about Linux on the PowerPC family of processors, visit penguinppc.org.
- The article "Migrating UNIX Applications to Linux on IBM pSeries and iSeries Servers" (IBM, 2003) provides information on migrating application to IBM eServer hardware.
- Read the "Technical guide for porting applications from Solaris to Linux, version 1.0" (developerWorks, February 2002) for more information about the differences between Solaris and Linux and porting Solaris applications to Linux.
- The "Solaris to Linux Porting Guide" (Red Hat, Inc., 2000) discusses porting the 7.x series of the Red Hat Linux distribution to Solaris.
About the authors  | |  | Chakarat Skawratananond is currently a technical consultant in the IBM eServer
Solutions Enablement organization, where he assists ISVs in enabling their applications
for Linux on POWER architecture. He has published a number of articles for IBM
developerWorks in the area of Linux migration. He can be contacted at
chakarat@us.ibm.com. |
 | |  | Matt Davis is a Linux on POWER consultant in Austin, Texas. He came to IBM in May, 2000 while still studying at the University of Texas at Austin, from which he earned two degrees with academic honors. He has been engaged with the Linux on POWER development community for nearly 5 years, and is the author of roughly two dozen papers and articles regarding Linux on Intel and POWER architectures. In addition to involvement in the Open Source Community, Matt spends his spare time researching life sciences at the University of Texas. |
Rate this page
|  |