Appendix B. Source Code Portability: Windows, UNIX, and Linux

A common, but not universal, application requirement, especially for server applications, is that the application must run on some combination of Windows, Linux, and UNIX.1 This requirement leads to the need for “source code portability” whereby:

1 More precisely, “UNIX” means the POSIX functions specified in The Single UNIX Specification (www.opengroup.org/onlinepubs/007908799/). UNIX and Linux implement this specification. In turn, the specification has its historical origins in UNIX.

• There is a single set of source code for all target platforms.

• Macros and build parameters define the target platform, which could be any of the three operating systems, along with choices of processor architecture, 32-bit or 64-bit, and operating system vendor.

• Conditional compilation statements, while unavoidable, should be minimal, and the bulk of the source code should be the same for all target platforms.

• The source code is compatible with a wide variety of compilers.

• Performance, resource requirements, and other application characteristics should be similar for all targets.

With sufficient care and some exceptions, you can meet these requirements and build nontrivial source code portable applications. This discussion assumes, however, that there is no GUI interface. This appendix starts by describing some techniques that have been successful and have met all the requirements. There are, of course, other ways to achieve source code portability beyond what is described here.

The discussion is limited to the POSIX API that is comparable to the topics in this book, and the discussion is not concerned with the complete POSIX environment. However, it’s worth pointing out that Cygwin (www.cygwin.com) provides an excellent open source set of POSIX commands and utilities for Windows.

After the discussion are tables that list the Windows functions and their POSIX equivalents, if any. The tables are organized by chapter.

Source Code Portability Strategies

There are several ways to tackle this problem, although there is no single strategy suitable for all functionality areas. Viable strategies, not always mutually exclusive, include:

• Use libraries, possibly open source or Microsoft-provided, that emulate POSIX functions on Windows. This strategy is common and successful, with some examples in this appendix.

• Use libraries, possibly open source, that emulate Windows functions on UNIX/Linux. This strategy is rare, and there are no examples here.

• Use industry-standard functions that Microsoft supports directly. This is also a common and successful strategy for some functionality.

• Use macros instead of libraries to emulate one OS under the other. This strategy is also rare, but there is one example in this appendix.

Windows Services for UNIX

Windows Services for UNIX (SFU) is a Microsoft product that provides a UNIX subsystem, also called Interix, for Windows. The subsystem is implemented in user space on the NT kernel. The current version is 3.5, and you can download it from the Microsoft Web site.

In principle, SFU should satisfy all the requirements. Unfortunately, at publication time, Microsoft plans to discontinue support (the plans were announced in 2005). For example, the Web site states that the supported operating systems are “Windows 2000; Windows 2000 Service Pack 3; Windows 2000 Service Pack 4; Windows Server 2003; Windows XP.” Furthermore, “the product will not install on Windows 9x or Windows XP Home Edition or Windows Vista. The product should not be installed on Windows Server 2003 R2. This is an unsupported configuration.”

The Wikipedia entry (http://en.wikipedia.org/wiki/Microsoft_Windows_Services_for_UNIX) says, “SFU will be available for download until 2009; general support will continue until 2011; extended support until 2014,” and citations to the trade press support this statement.

Source Code Portability for Windows Functionality

The following sections describe some techniques, arranged by chapter order. Some areas are easier than others, and in some cases, there are no straightforward solutions.

File and Directory Management

The Standard C library (CLIB) will support normal file I/O without significant performance impact. However, CLIB does not provide directory management, among other limitations.

Another possible solution is to use the normal POSIX functions, such as open() and read() and the corresponding Windows functions (see Chapter 1 and the cpUC project), such as _open() and _read(). A simple header file, in the Examples file, allows you to use the POSIX function names in the source code. The header file also includes definitions to support time, file attributes, and file locking.

There is no POSIX equivalent to the registry.

Exception and Signal Handling

POSIX signal handling is difficult to emulate in Windows except for a few special cases described in Chapter 4. However, you can write in C++ rather than C and use C++ exception handling rather than Structured Exception Handling (SEH) to achieve some of the requirements.

Memory Management and Memory-Mapped File I/O

There are several aspects to portable memory management code.

• The CLIB functions malloc, calloc, realloc, and free are sufficient in most cases for application memory management.

• POSIX does not have functions equivalent to the Windows heap management functions, and Chapter 5 describes some heap management advantages. There are, however, open source solutions, available on Windows and UNIX/Linux, that provide the heap management benefits. One such solution is “Hoard: A Scalable Memory Allocator for Multithreaded Applications” (www.cs.umass.edu/~emery/hoard/asplos2000.pdf).

• Memory-mapped file I/O provides similar performance and programming simplicity advantages on all operating systems. The Web site’s wcMT (multithreaded word count) example includes portable file memory-mapping code that has been tested on multiple target systems.

Process Management

Windows CreateProcess allows you to emulate the POSIX fork-exec sequence, as described in Chapter 6. The principal difficulties arise in:

• Emulating the various exec options to specify the command line and environment variables

• Passing handles to the child process

• Managing the parent-child relationships, which Windows does not support

I am not aware of a good open source solution to the process management problem. However, it’s worth mentioning that I’ve successfully developed a library, usable from all OSs, that provides a significant subset of the POSIX process management functionality. This subset was sufficient for the project needs. However, the code was developed under nondisclosure, so it’s not in the Examples file. Suffice it to say, however, that the task was not difficult and required about two days of work.

Thread Management and Synchronization

Thread management and synchronization portability, at first sight, may seem to be intractable, considering the need for correct operation on a wide variety of platforms. Fortunately, it is not difficult at all. Here is one successful approach.

• Develop your source code using the Pthreads API (www.unix.org/version3/ieee_std.html)

• Use the open source Pthreads library for Windows (http://sources.redhat.com/pthreads-win32/)

This open source library provides good performance, compatible with native Windows code. However, at publication time, it does not yet support slim reader/writer locks (Chapter 9) or thread pools. However, the library is open source, and upgrading this library would be a worthwhile contribution.

I’ve also had success with a simple set of macros that nicely emulate nearly all Pthreads functionality in Windows. In this case, the client did not want to use open source code. The macros are on the book’s Web site.

Interprocess Communication and Network Programming

This problem also has multiple aspects.

• One-directional pipes (Chapter 12) are fairly close in Windows and POSIX, and they are usually associated with process management (mentioned in a previous section).

• Windows supports the sockets API and provides the simplest portability and interoperability strategy for network programming and interprocess communication, even on a single system.

• Named pipes and mailslots are Windows-specific and are best avoided in portable source code.

Services

Windows Services correspond very roughly to POSIX “daemons.” Service and daemon management are administrative functions, and there is no direct way to provide portable source code that uses the Windows Services functions.

Asynchronous I/O

The Windows and POSIX models for asynchronous I/O are considerably different. I’ve found that it’s simplest to use threads and avoid asynchronous I/O altogether, although this is a matter of personal taste.

Windows, POSIX, and C Library Comparison Tables

The following tables show the Windows functions described in the main text along with the corresponding UNIX/Linux and ANSI Standard C library functions, if any.

The tables are arranged by chapter (some chapters are combined). Within each chapter, they are sorted first by functionality area (file system, directory management, and so on) and then by the Windows function name.

Each table row gives the following information:

• The functionality area (subject)

• The Windows function name

• The corresponding UNIX function name, in some cases, more than one

• The corresponding C library function name, if any

• Comments as appropriate

The notation used in the tables requires some explanation.

• The Microsoft Visual Studio library contains some UNIX compatibility functions. For example, _open is the compatibility library function for UNIX open. If the UNIX function is in italics, there is a compatibility function. An asterisk next to the name indicates that there is also a wide character Unicode version. For example, there is a _wopen function.

• A program that uses just the Standard C library, and no Windows or UNIX system functions, should compile, build, and run on both systems if normal precautions are taken. Such a program will, however, be limited to file and I/O operations.

• Commas separating functions indicate alternatives, often using different characteristics or emulating one aspect of the Windows function.

• Semicolons separating functions indicate that you use the functions in sequence to emulate the Windows function. Thus, fork; exec corresponds roughly to CreateProcess.

• An underlined entry indicates a global variable, such as errno.

• In a few cases, the UNIX equivalent may be stated imprecisely in terms such as “terminal I/O” for Windows functions such as AllocConsole. Often, “Use C library” is the appropriate comment, as in the case of GetTempFileName. In other cases, the situation is reversed. Thus, under the UNIX signal management functions (sigaddset and so on), the Windows entry is “Use SEH, VEH” to indicate that the programmer should set up structured or vectored exception handlers and filter functions to get the desired behavior. Unlike UNIX, Windows does not support process groups, so the Windows entries are “N/A,” although job management, as done by the programs in Chapter 6, could emulate process relationships.

• There are numerous N/A entries, especially for the C library, if there is no comparable function or set of functions. This is the case, for example, with directory management.

• The POSIX threads (Pthreads) functions are the UNIX equivalents shown in the tables for Chapters 7 through 10, even though they are not properly a part of UNIX.

Generally, the correspondence is more precise in the earlier chapters, particularly for file management. The systems tend to diverge with the more advanced functionality, and in many cases, there is no C library equivalent. For example, the UNIX and Windows security models differ significantly, so the relationships shown are, at best, approximations.

These functional correspondences are not exact. There are many differences, small and large, among the three systems. Therefore, these tables are only for guidance. The individual chapters discuss many of the differences.

Chapters 2 and 3: File and Directory Management

Table B-1 Chapters 2 and 3: File and Directory Management

image

image

image

image

image

image

image

image

image

Chapter 4: Exception Handling

Table B-2 Chapter 4: Exception Handling

image

image

Chapter 5: Memory Management, Memory-Mapped Files, and DLLs

Table B-3 Chapter 5: Memory Management, Memory-Mapped Files, and DLLs

image

image

Chapter 6: Process Management

Table B-4 Chapter 6: Process Management

image

image

image

image

Chapter 7: Threads and Scheduling

Table B-5 Chapter 7: Threads and Scheduling

image

image

Chapters 8–10: Thread Synchronization

Table B-6 Chapters 8–10: Thread Synchronization

image

image

image

image

Chapter 11: Interprocess Communication

Table B-7 Chapter 11: Interprocess Communication

image

image

image

Chapter 14: Asynchronous I/O

Table B-8 Chapter 14: Asynchronous I/O

image

Chapter 15: Securing Windows Objects

Table B-9 Chapter 15: Securing Windows Objects

image

image

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset