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 article highlights these differences and recommends possible solutions if an equivalent on the Linux side is not available.
The article is organized as follows:
- A porting roadmap
- Porting notes, including 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
We focus on migration from Solaris running on 32- or 64-bit SPARC, Intel®, or AMD x86 architectures to Linux running on POWER architecture. On the Solaris side, we base our discussion on version 9 and later. On the Linux side, we focus on the distributions available on IBM POWER processor-based servers: SUSE LINUX Enterprise Server (SLES) Version 11 and Version 10 service pack 2, and Red Hat Enterprise Linux (RHEL) Version 5 update 2.
You need 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 consider endianness since Linux on POWER is big-endian, while Solaris/x86 is little-endian.
Determine if all the required third-party packages (such as databases and class libraries) are available on the target platform. For 32-bit applications, consider whether you need to migrate to 64-bit. The next section on General porting notes includes 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 Development environment for Linux on POWER section gives more information on the available compilers for Linux on POWER.
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.
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: Runtime test and debug
After the application is successfully built, test it for runtime errors.
Now the ported code is running on the target platform. Monitor performance to ensure the ported code performs as expected. If not, you need to tune for performance. The Performance tuning section describes the performance tuning tools available for Linux on POWER.
Will you have to distribute the resulting code to others? If so, what packaging method do you want to use? Linux provides several ways to package your application such as a tarball, self-installing shell script, and RPM. The Software packaging section provides more information about software packaging for Linux.
This section helps you decide when to migrate applications from 32- to 64-bit, and covers considerations for applications crossing an endianness barrier. Applications migrating from Solaris on x86 platforms will cross an endian barrier. Developers familiar with 32- to 64-bit migration and byte ordering may wish to skip to the next section, "Development environment for Linux on POWER."
Once you decide to port to Linux on POWER, you may also consider porting the application to a 64-bit one if it is currently at a 32-bit level; however, 32-bit applications that do not require 64-bit capabilities should remain 32-bit ones. Any porting effort involving a migration to 64 bits should be taken in two steps:
- First, migrate to the target platform as a 32-bit entity.
- Then, and only then, proceed to the 64-bit level.
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 bits if possible for these reasons:
- Benefit from more than 4GB of virtual address space.
- Benefit from more physical memory (greater than 4GB) if 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 2GB.
The application can remain 32 bits 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 uses. For Linux on POWER, the ILP32 model is used in the 32-bit environment, while the LP64 model 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 include:
- Data truncation
- Pointer assignment
- Data alignment
- Data sharing
IBM XL compilers provide the -qwarn64 option to
help check for possible data conversion problems between 32- and 64-bit
modes.
The topic of migrating applications from 32- to 64-bit environments is covered extensively in other publications, so we won't go into detail about how to deal with these porting issues here. (Refer to "Porting Linux applications to 64-bit systems" for more information.)
Since both SPARC and POWER architectures are big-endian, there are 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 is placed first. For little-endian processors such as Intel and Alpha processors, the least significant byte is placed first.
Figure 1 shows how big-endian and little-endian processors place a hexadecimal value, like 0x89ABCDEF, in 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. Listing 1 shows 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 fundamental elements of a Linux on POWER development environment, including distributions, compilers, and how Java™ technology fits in.
Linux for POWER is offered in enterprise form by two leading Linux vendors, SUSE Linux and Red Hat. Both distributions are highly respected by the Linux community, and the differences between the distributions and versions are driven more by when each version is released. Most developers should have no problem deploying software from the same code base on either offering:
- SLES11
Announced in early 2009, Novell's SUSE Linux Enterprise Server version 11 provides significant new kernel improvements, enhancements in virtualization, management, hardware enablement, and interoperability. It includes features and updates designed to handle mission-critical workloads in the data center. SUSE Linux Enterprise Server offers an open, scalable, high-performance data center solution. It is based on the 2.6.27 kernel and provides GCC 4.3.2, glibc 2.9, binutils 2.19. - SLES10 SP2
SUSE Linux Enterprise Server version 10, Service Pack 2 is based on the Linux 2.6.16 kernel and supports all enterprise features of Linux on POWER hardware. The development environment provides GCC 4.1.2, glibc 2.4.31, and the GNU binutils 2.16.91. - RHEL5.3
Red Hat® Enterprise Linux® 5.3 is based on the Linux 2.6.18 kernel and provides GCC 4.1.2, glibc 2.5, and binutils 2.17. RHEL5 includes many enhancements over prior releases. These include per-queue, switchable on-the-fly I/O schedulers, IPv4/IPv6 performance enhancements, and kernel SMP locking enhancements for improved scalability and performance. - RHEL4.8
Red Hat Enterprise Linux 4.8 is based on the Linux 2.6.9 kernel and provides GCC 3.4.6, glibc 2.3.4, and binutils 2.15. The GCC4 package is, however, updated to version 4.1.2. (See Resources for a link to a comparison of the features between RHEL3, RHEL4, and RHEL5 across the supported platforms.)
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 for a link.) 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. - Advance Toolchain
For projects that can take advantage of the latest gcc and toolchain technologies, an Advance Toolchain package is available for customers who prefer to remain on a GCC compiler. The Advance Toolchain provides a newer gcc compiler version and tuned runtime libraries for Power 6 when using SLES10 and RHEL5. In many cases, gcc compiled applications that take advantage of the Advance Toolchain compiled at a -O3 compilation level will have performance close to the IBM C/C++ compilers compiled at -O3. The advantage of the IBM compilers is the additional headroom available for continued performance improvements and tuning, which can be significant in some cases. - XL C/C++
The IBM XL C/C++ compilers for Linux are available for Power systems. XL C/C++ compilers Version 10.1 are available for RHEL5, SLES10, and SLES11. Earlier versions of the IBM compilers like XL C/C++ Version 8 and Version 9 remain available if you want to build on older Linux distribution versions like SLES 9, SLES10 sp1, RHEL5, or RHEL 4. 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 that 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 offered exclusively by GCC compilers has been ported to XL C/C++ to facilitate source compatibility, including 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++.
While XL C/C++ for Linux on POWER is not free, it is available to developers for a 60-day trial. Details are available at the XL C/C++ for Linux Web site. (See Resources for a link.) - XL Fortran
The IBM XL Fortran for Linux compilers are also available for Power systems if Fortran is desired. XL Fortran Version 11.1 and XL Fortran Version 12.1 are available for RHEL5, SLES10, and SLES11. Details are available at the XL Fortran for Linux Web site. (See Resources for a link.)
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 for a link.)
Java technology allows developers to deploy across multiple platforms without recompiling code, provided that the Java Runtime Environment (JRE) is compatible with the Java Developer Kit (JDK) for the platform on which the software was developed. The most current Linux on POWER Java development kits are supported on RHEL4, RHEL5, and SLES9, SLES10, and SLES11 by the IBM Developer Kit for Linux, Java Technology Edition version 1.4.2, version 5.0, and version 6. 32- and 64-bit versions are available for no-charge download (Resources) from IBM.
Differences between Solaris and Linux
Now let's review the differences between Solaris and Linux, 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 more than 300 different system calls. See a list of system calls in /usr/include/asm-powerpc/unistd.h or /usr/include/asm/unistd.h, depending on which distribution you are using.
Here are a few examples of incompatibilities between Solaris and Linux:
-
regexp()andregcmp(): If you use the routinesregexp()andregcmp()on Solaris, you need to replace them withregexec()andregcomp()on Linux. - File system interface routines: Solaris file system routines employ
vfstabstructures and containvfsin the function name, such asgetvfsent. Linux provides equivalent interfaces, but the routines usefstabstructures and containfsin the routine name, such asgetfsent. Thevfstabstructure on Solaris is defined in /usr/include/sys/vfstab.h. The definition offstabon 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. 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()andsched_getaffinity() for CPU affinity. Please consult the Linux man pages for more information.
Table 1 lists the Solaris system calls that use a different name or signature, or are not available on Linux:
Table 1. Solaris system calls
| Solaris | Linux | Notes |
|---|---|---|
| acl, facl | N/A | Gets or sets a file's Access Control List (ACL) |
| adjtime | N/A | Corrects the time to allow synchronization of the system clock |
| audit | N/A | Writes a record to the audit log |
| auditon | N/A | Manipulates auditing |
| auditsvc | N/A | Writes audit log to specified file descriptor |
| getacct, putacct, wracct | N/A | Gets, puts, or writes extended accounting data |
| getaudit, setaudit, getaudit_addr, setaudit_addr | N/A | Gets and sets process audit information |
| getauid, setauid | N/A | Gets and sets user audit identity |
| getdents | getdents** | Reads directory entries and puts in a file system-independent format. The dirent structure is different on Linux and Solaris. |
| getmsg, getpmsg | N/A | Gets next message off a stream |
| getpflags, setpflags | N/A | Gets or sets process flags |
| getppriv, setppriv | N/A | Gets or sets a privilege set |
| getustack, setustack | N/A | Retrieves or changes the address of per-LWP stack boundary information |
| issetugid | N/A | Determines if the current executable is running setuid or setgid |
| llseek | _llseek | Moves extended read/write file pointer |
| _lwp_cond_signal, _lwp_cond_broadcast | N/A | Signals a condition variable |
| _lwp_cond_wait, _lwp_cond_timedwait, _lwp_cond_reltimedwaid | N/A | Waits on a condition variable |
| _lwp_info | N/A | Returns the time-accounting information of a single LWP |
| _lwp_kill | N/A | Sends a signal to a LWP |
| _lwp_mutex_lock, _lwp_mutex_unlock, _lwp_mutex_trylock | N/A | Mutual exclusion |
| _lwp_self | N/A | Gets LWP identifier |
| _lwp_sema_wait, _lwp_sema_trywait, _lwp_sema_init, _lwp_sema_post | N/A | Semaphore operations |
| _lwp_suspend, _lwp_continue | N/A | Continues or suspends LWP execution |
| memcntl | N/A | Memory management control |
| meminfo | N/A | Provides information about memory |
| mount | mount* | Mounts a file system. The signatures of this system call on Solaris and Linux are different. |
| msgids | N/A | Discovers all message queue identifiers |
| msgrcv | msgrcv* | Message receive operation. Linux uses struct msgbuf type in one of its arguments. |
| msgsnap | N/A | Message queue snapshot operation |
| msgsnd | msgsnd* | Message send operation. Linux uses struct msgbuf type in one of its arguments. |
| ntp_adjtime | N/A | Adjusts local clock parameters |
| ntp_gettime | N/A | Gets local clock values |
| open, openat | open* | Opens a file. openat() is not available in Linux. |
| pcsample | N/A | Program execution time profile |
| p_online | N/A | Returns or changes processor operational status |
| priocntl | N/A | Process scheduler control |
| priocntlset | N/A | Generalized process scheduler control |
| processor_bind | sched_setaffinity | Binds LWPs to a processor |
| processor_info | N/A | Determines type and status of a processor |
| pset_bind | N/A | Binds LWPs to a set of processors |
| pset_create, pset_destroy, pset_assign | N/A | Manages sets of processors |
| pset_info | N/A | Gets information about a processor set |
| pset_list | N/A | Gets a list of processor sets |
| pset_setattr, pset_getattr | N/A | Sets or gets processor set attributes |
| putmsg, putpmsg | N/A | Sends a message on a stream |
| rename, renameat | rename* | Changes the name of a file.
Linux does not have renameat(). |
| resolvepath | N/A | Resolves all symbolic links of a path name |
| semids | N/A | Discovers all semaphore identifiers |
| setrctl, getrctl | N/A | Sets or gets resource control values |
| settaskid, gettaskid, getprojid | N/A | Sets or gets task or project IDs |
| shmids | N/A | Discovers all shared memory identifiers |
| sigsend, sigsendset | N/A | Sends a signal to a process or a group of processes |
| __sparc_utrap_install | N/A | Installs a SPARC V9 user trap handler |
| fstatat | N/A | Gets file status |
| swapctl | N/A | Manages swap space |
| uadmin | N/A | Administrative control |
| unlink, unlinkat | unlink* | Removes directory entry.
Linux does not have unlinkat(). |
| futimesat | N/A | Sets file access and modification times. It resolves the path
relative to the
fildes argument. |
| waitid | N/A | Waits for child process to change state |
| yield | sched_yield | Yields execution to another lightweight process |
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's 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:
# man 7 signal. Note that for Linux:
SIGABRTandSIGIOTare identical.SIGCLDandSIGCHLDare identical.SIGPOLLandSIGIOare identical.- Default action for
SIGPWRfor Linux is to terminate the process, but ignore on Solaris.
Table 2 shows the signals that 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:
Listing 2. How
sigset_t is defined in Linux
# 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:
Listing 3. How
sigset_t
is defined in Solaris
typedef struct { /* signal set type */
unsigned int __sigbits[4];
} sigset_t;
|
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. Table 3 compares base data types for Linux on POWER and Solaris (values given in bytes):
Table 3. Base data types
| Data type for: | Linux on POWER, ILP32 | Linux on POWER, LP64 | Solaris, ILP32 | Solaris, LP64 |
|---|---|---|---|---|
| _Bool,bool | 1 | 1 | 1 | 1 |
| char | 1 | 1 | 1 | 1 |
| wchar_t | 2 | 4 | 4 | 4 |
| short | 2 | 2 | 2 | 2 |
| int | 4 | 4 | 4 | 4 |
| float | 4 | 4 | 4 | 4 |
| long | 4 | 8 | 4 | 8 |
| pointer | 4 | 8 | 4 | 8 |
| long long | 8 | 8 | 8 | 8 |
| double | 8 | 8 | 8 | 8 |
| long double | 16* | 16* | 16 | 16 |
*The default size for long double in Linux on POWER is 128 bits
starting from Red Hat Enterprise Linux 5 (RHEL5) and SUSE Linux
Enterprise Server 10 (SLES10). Before RHEL5 or SLES10, they can be
increased to 128 bits if the compiler option
-qldbl128 in XL compilers is used.
When porting applications between platforms or between 32-bit and 64-bit
modes, you need to consider the differences between alignment
settings available in the different environments to avoid possible
degradation in performance and data corruption. For the 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 either the
linuxppc or bit-packed rules (using -qalign
option) where linuxppc is the default. linuxppc uses GNU C/C++ alignment
rules to maintain binary compatibilities with GNU C/C++ objects.
Table 4 shows the alignment values for linuxppc and bit-packed rules:
Table 4. Alignment values
| Data type | linuxppc | bit-packed |
|---|---|---|
| _Bool,bool | 1 | 1 |
| char | 1 | 1 |
| wchar_t (32-bit) | 2 | 1 |
| wchar_t (64-bit) | 4 | 1 |
| int | 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 | 16 | 1 |
SPARC platforms generally require data alignment to equal data size: two-byte types must be on a two-byte boundary, and four-byte types must be on a four-byte boundary. So when you port from Solaris to Linux, you need to take special care with those data types of different size between Solaris and Linux.
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- or 64-bit) and the hardware platforms. Table 5 shows some of the derived data types on Linux that are different from those on Solaris:
Table 5. Derived data types
| Data type for: | Solaris ILP32 | Solaris LP64 | Linux on POWER ILP32 | Linux on POWER LP64 |
|---|---|---|---|---|
| gid_t | long | int | unsigned int | unsigned int |
| mode_t | unsigned long | unsigned int | unsigned int | unsigned int |
| pid_t | long | int | int | int |
| uid_t | long | int | unsigned int | unsigned int |
| wint_t | long | int | unsigned int | unsigned int |
If you use Solaris Make in the source platform, you need to modify your makefile in order to use GNU Make on Linux. For detailed information about the difference between Solaris Make and GNU Make, please consult Chapter 6 of the "AIX 5L Porting Guide."
Table 6 is a list of widely used compiler options for Sun Studio compiler and the equivalent options for the GNU Compiler Collection and IBM's XL 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. |
| -dn | -static | -qstaticlink | Specifies static linking. |
| -dy | (Default) | (Default) | Specifies dynamic linking. |
| -G | -shared | -qmkshrobj | Produces 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 | Compiles and links for multithreaded code. |
| -R dirlist | -Wl, -rpath, dirlist | -Wl, -rpath, dirlist | Builds a dynamic library search path into the executable file. |
| -xarch | -mcpu, -march | -qarch, -qtune | Specifies the target architecture instruction set. |
XL C/C++ compilers support a subset of GNU compiler options to facilitate
porting applications developed with gcc and g++ compilers. This support is
enabled when either the gxlc or
gxlc++ invocation command is used with select
GNU compiler options. These invocation commands use a plain-text
configuration file to control GNU-to-XL C/C++ option mappings and
defaults. You can modify this configuration file to better meet your
requirements.
See your XL compiler manual for more information about
gxlc and gxlc++.
Now let's look issues you can encounter when migrating multithreaded applications from Solaris to Linux.
Solaris supports two threading implementations: Solaris threads and POSIX threads (pthreads). A few functions implemented by the Solaris threads API 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 callthr_exit(). - The ability to suspend or continue the execution of the thread using
thr_suspend()andthr_continue(). Note thatthr_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 ofpthread_join()to 0, the program will be terminated with a segmentation fault.
Table 7 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 forks. |
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-forks semantics, forkall(). This
function duplicates the address space and all the threads in the child.
This feature is not supported by POSIX thread standard.
Some POSIX thread routines implemented in Solaris are not in Linux, and vice versa. Table 8 lists those routines:
Table 8. POSIX thread routines
| Routine | Solaris | Linux |
|---|---|---|
| pthread_cond_reltimedwait_np | y | n |
| pthread_mutexattr_getrobust_np | y | n |
| pthread_mutexattr_setrobust_np | y | n |
| pthread_mutex_consistent_np | y | n |
| pthread_mutex_reltimedlock_np | y | n |
| pthread_rwlock_reltimedrdlock_np | y | n |
| pthread_rwlock_reltimedwrlock_np | 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-to-one threading model where there is a one-to-one
relationship between user threads and kernel threads. NPTL also implements
the interprocess 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. Table 9 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" (Resources).
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 includes a list of tools available for Linux on POWER to help accomplish that.
Two common tools—the sysstat package and nmon—can make performance monitoring and tuning easier:
- The sysstat package contains the common extended tools for basic performance monitoring, including tools like mpstat, iostat, and sar.
- nmon combines a variety of classic system monitoring tools into a one-stop data gathering tool. A number of really handy post-processing add-ons are available to summarize, graph, and consolidate information.
OProfile is a system-wide profiler for Linux 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™, POWER6™ and PowerPC™ 970. Please visit the OProfile Web site for more information (Resources for a link).
OProfile for Linux on POWER is available with SLES10, SLES11, RHEL5, and in the Advance Toolchain. A quick reference for OProfile on an installed system can be found at /usr/share/doc/packages/oprofile.
Post-link optimization (FDPRpro)
This tool optimizes the executable image of a program by collecting information on the behavior of the program while the program is used for a 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, the Post-Link Optimization tool is supported on SLES8 and later and RHEL3 and later. For more information, please visit the alphaWorks Post-Link Optimization for Linux on POWER site (Resources for a link).
Application software is delivered to 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 (Resources for a link).
Note that the format of the package specification template files used by pkgadmin in Solaris is different from the spec file used by RPM; translating packaging information from template file into spec files requires a substantial effort.
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.
- Linux is designed with portability in mind, and it is supported on almost all hardware platforms available today.
Today's Linux distributions are based on the Linux 2.6 kernel, which continues to take advantage of significant community and corporate improvements. SLES10 SP2, RHEL5.2, and SLES11 all have significantly improved performance, scalability, and reliability from the earlier releases, which were based on the Linux 2.4 kernel. For Linux running on Power systems, applications have access to the full power, performance, scalability, virtualization, and management capabilities.
Keep in mind that there are some system-specific features available on Solaris that are not available on Linux.
We would like to thank Steve Munroe, Mark Brown, Kevin Li, and Ralph Nissler for their invaluable comments.
Learn
- Check out the latest
XL C/C++ compiler,
and
XL Fortran compiler.
- Red Hat publishes a
comparison of the features between RHEL3, RHEL4, and RHEL5
across the supported platforms.
- Visit the
Advance Toolchain for Linux on POWER forum
to pose questions about Advance Toolchain.
- The
IBM Systems Application Advantage for Linux (Chiphopper)
offering can assist you in porting, testing, and supporting your existing
Linux x86 applications on Intel to IBM System and middleware platforms.
- 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.
- 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).
- Check out
rpm.org for more information about
RPM.
- See
Port Your Solaris Application to Linux on POWER
to decide 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.
- Why Linux on Power?
is a resource for Independent Software Vendors interested in enabling
and promoting their applications for Linux on Power.
- IBM Business Partners can
access the
Migration Kit for
Solaris OS to Linux
- The IBM Redbook
Solaris to Linux Migration: A Guide for System Administrators
(IBM Redbooks, 2006) is a technical reference for IT system
administrators in organizations considering a migration from
Solaris to Linux-based systems.
- "Porting Linux applications to 64-bit systems"
(developerWorks, April 2006) is a good reference for 32-bit to 64-bit
porting.
-
In the
developerWorks Linux zone,
find more resources for Linux developers, and scan our
most popular articles and
tutorials.
-
See all
Linux tips and
Linux tutorials on developerWorks.
-
Stay current with
developerWorks technical events and Webcasts.
Get products and technologies
- Download the
Advance Toolchain for Linux on POWER compiler.
- Get your
IBM Developer Kit for Linux, Java Technology Edition:
J2SE 1.3.1, 1.4.2, 5.0, and Java SE 6 available.
- 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
runtime profiles.
- Visit the
OProfile Web site. OProfile
for Linux on POWER is generally packaged as an rpm install on the RHEL and
SLES SDK media.
-
With
IBM trial software,
available for download directly from developerWorks, build your next development
project on Linux.
Discuss
- Participate in the discussion forum.
-
Get involved in the
My developerWorks community; with your personal profile and custom home page, you
can tailor developerWorks to your interests and interact with other developerWorks users.
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.
Sean Perry is a senior compiler developer in the IBM Rational group in Toronto. He has almost 20 years of experience writing C and C++ compilers for many different operating systems including AIX, Linux/ppc, Solaris, and zOS.
Matt Davis was a Linux on POWER consultant in Austin, Texas before he came to IBM in May 2000 (while still studying at the University of Texas from which he earned two degrees with academic honors). He has been engaged with the Linux on POWER development community for nearly five 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.
Comments (Undergoing maintenance)





