2004, 2005 by Marc J. Rochkind. All rights reserved. Portions marked "Open Source" may be copied under license.

 

Mac OS X (Darwin) Revisited

This is preliminary draft for those anxious to get started on OS X 10.4.
A new source-code archive is not yet ready for download.

I used Version 10.2 (Jaguar) of OS X when I prepared the examples for the book, which included Darwin 6.6. Since then, OS X has been updated twice. I'm writing this in May, 2005; 10.3 (Panther) has been out for over a year, and now I'm using 10.4 (Tiger), which was just shipped a few days ago. My version of 10.3 was running Darwin 7.9, and 10.4 starts with Darwin 8.0. The versions of gcc supplied by Apple have advanced as well; on my 10.3 gcc was up to 3.3, and now I'm using 4.0 on OS X.

This page has two main purposes:

bulletTo provide up-to-date information on compiling and linking the examples for 10.3 and 10.4.
bulletTo indicate what missing features of 10.2, such as the lack of System V messages, have been added.

In addition, readers who aren't using Macs will find some hints here about what has to be changed to get the examples to compile with newer versions of gcc.

The next section explains how to get the examples compiled on OS X 10.3 and 10.4, and then there are sections that update each of the the nine chapters in the book.

OS X 10.4 is a significant advance over 10.3 and earlier systems insofar as Darwin is concerned, and I strongly recommend that UNIX programmers using OS X upgrade to 10.4. Of course, there are many other nice features at the higher OS levels, where Mac users normally are.

Compiling the Examples

The first thing you have to do is install the development system, which includes gcc and related tools, system header files, and libraries. Apple calls this the BSD environment, because that's what the outside of the kernel is based on. (The inside is the micro-kernel Mach.) If you want to read more about the Mac kernel, Apple has an excellent paper here.

The BSD environment is a tiny part of Apple's development system, most of which is for developing Mac applications, not UNIX programs. You need to install Xcode, which is their interactive development tool, and that will bring in BSD as well. To tell if you have the development system, open the Terminal application (in the Finder, click on Applications/Utilities/Terminal) and type “gcc” to see if it's there. If it is, probably the rest of the BSD environment is, too.

Xcode is on the standard OS X install DVD (or CD set), although you have to install it separately. If for some reason you can't get it from there, you can download the latest version from the Apple Developer Connection (ADC) Member Site (membership is free; use Google to find the site).

You'll need a text editor. You can use Xcode (just for editing the text), TextEdit, or even vi, which are already installed on your Mac, or you can download something like the free, open-source Smultron or, a much better choice, TextWrangler, also free.

Download the source, from the AUP website to the directory where you want the source rooted (for me is was /Users/marc/aup/src). Then unzip it with gunzip and untar it with “tar -xf aup2ex.tar”.

General instructions for compiling the AUP source haven't changed since the book was published; they're on this site's How to Compile the Examples page. For 10.3, I didn't have to do anything to common/makebuild.spec as it's shipped with the example code. The shell script to run Awk works fine just the way it's shown on the How-to-Compile page.

Because, with 10.4, Darwin claims to be a SUS3 system, some code in defs.h that makes assumptions about what the macro DARWIN means needs to be changed, and it uses the macro SUV_SUS3 to do so. To get this behavior, you need to define SUV_SUS3 on the gcc command line. First, add it to your normal compilation shell script (mine is called "k"):

AUPSRC=/Users/marc/aup/src

OS=DARWIN

SUV=SUV_SUS3

LIBS="-lncurses"

TLIBS="-llthread -llgcc_r"

INCLUDES="-I/usr/local/include/pthread/linuxthreads"

export AUPSRC OS LIBS TLIBS PTHREADSTUB INCLUDES SUV

make $2 -f $AUPSRC/$1/Makefile $3

Aside from the addition of SUV, this is almost identical to the script for FreeBSD, as you would expect. You have to change the first line to wherever you've stored the source code.

You also have to change makebuild.spec in the common directory to handle the new SUV environment variable by addition a -D argument to the line that defines CFLAGS:

!echo CFLAGS = $(INCLUDES) -I$(AUPSRC) -I$(AUPSRC)/include -I$(AUPSRC)/common -I$(AUPSRC)/c6

-Wimplicit -Wstrict-prototypes -Wall -D$(OS) -D_REENTRANT -D_THREAD_SAFE -std=c99 -D$(SUV)

To make the example ckvers in directory c1, I type:

k c1 ckvers

(I have my PATH changed to start the search in the current directory. If you don't do that, you'll have to type “./k” instead of plain “k”.)

At this point, you'll get lots of error messages when you try to compile on 10.4 (10.3 may work OK), and defs.h isn't yet set up to treat the new Darwin as a SUS3 system. Follow the instructions below to make the needed changes.

After you've made the Chap. 1 changes, you can see the improvements in the new Darwin right away by compiling and running the suvreq program (p. 44 in the book):

$ k c1 suvreq
...
$ suvreq
Request:
_POSIX_SOURCE defined
_POSIX_C_SOURCE = 199506
_XOPEN_SOURCE = 500
_XOPEN_SOURCE_EXTENDED defined
Claims:
_POSIX_VERSION = 200112
X/Open
_XOPEN_VERSION = 600
$

Indeed, the new Darwin is claiming to be a SUS3 system. (In the book, 10.2's Darwin reported a _POSIX_VERSION = 198808, as shown on p. 47.)

Chap. 1 - Fundamental Concepts

1. Change defs.h to treat 10.4's Darwin as a SUS3 system by adding these lines:

/* Special case for OS X 10.4+; see comment above dated 7-My-2005. */
#if defined(SUV_SUS3) && defined(DARWIN)
#include "suvreq.h"
#endif

to include/defs.h near the top, just before this:

#if !defined(BSD_DERIVED) /* _POSIX_SOURCE too restrictive */
#define SUV_SUS2
#include "suvreq.h"
#endif

Without this change, Darwin systems didn't even include subreq.h. But, now, you tell defs.h that you're on the SUS3 Darwin by defining SUV_SUS3 on the gcc command line (see previous section for how), and that causes defs.h to included suvreq.h with a request for SUS3.

2. The file common\macrostr.c, which translates error codes to strings, assumed that the error codes were constants, so it used an initialization to populate the macrostr_db array. (The code isn't in the book. This file replaces the code shown on p. 27, which would have the same problem.) To find out more about how this file works and to download the new version, click here.

3. The program c1/version.c, which wasn't described in the book, had Darwin hard-coded to skip the main processing and just report that the tests aren't supported, but that can now be changed to run the tests if SUV_SUS3 is also defined. The first #if should be changed to:

#if defined(FREEBSD) || (defined(DARWIN) && !defined(SUV_SUS3))

Also, search for the line that contains the macro _POSIX2_C_VERSION and protect it with an #ifdef, like this:

#ifdef _POSIX2_C_VERSION
    printf("POSIX2_C_VERSION = %ld.\n",_POSIX2_C_VERSION);
#else
    printf("POSIX2_C_VERSION not defined\n");
#endif

Now, having said this about version.c, I should say that I have little confidence in this program. It appears to be based on an old POSIX standard. (I didn't write the program; I got it from http://www.comptechdoc.org/os/linux/programming/c/linux_pgc.html, which is why it's not in the book.) For a newer, much better, way to report options, see a new page I've just written.

4. There are a few places where the code won't compile with 10.3 because _POSIX_ASYNCHRONOUS_IO is undefined. Use your editor's find command to locate the lines based on the context. (You can skip these changes if you're using 10.4.)

c1/sysconf.c: Change the line

printf("_POSIX_ASYNCHRONOUS_IO = %ld\n", (long)_POSIX_ASYNCHRONOUS_IO);

to

printf("_POSIX_ASYNCHRONOUS_IO = %ld\n", (long)_POSIX_ASYNCHRONOUS_IO + 0);

common/options.c: Change the line that tests _POSIX_ASYNCHRONOUS_IO to:

#if !defined(_POSIX_ASYNCHRONOUS_IO) || (_POSIX_ASYNCHRONOUS_IO <= 0)

common/opttest.c: Change the line that tests _POSIX_ASYNCHRONOUS_IO to:

#if !defined(_POSIX_ASYNCHRONOUS_IO) || (_POSIX_ASYNCHRONOUS_IO == -1)

c1/sysconf.c: In the code that calls sysconf(_SC_ASYNCHRONOUS_IO), special case DARWIN because 10.3 defines _SC_ASYNCHRONOUS_IO, but sysconf considers it to be invalid:

if ((value = sysconf(_SC_ASYNCHRONOUS_IO)) == -1)
    if (errno == EINVAL)
#if defined(DARWIN) && _XOPEN_VERSION < 600 /* pre-10.4 Darwin */
        printf("sysconf(...) failed with EINVAL\n");
#else
        EC_FAIL
#endif

Chap. 2 - Basic File I/O

1. In c2/writev.c, line 64: The sizeof argument to printf should have a cast, and why not a space before BUFSIZ, too:

printf("sizeof(int) = %d; BUFSIZ = %d\n", (int)sizeof(int), BUFSIZ);

2. Table 2.4 on p. 113 shows write vs. writev for Solaris, Linux, and FreeBSD. The table with Darwin 7.9 and 8.0 added becomes:

System Call

User

System

Solaris

writev

1.35

50.00

write

8.45

67.61

Linux

writev

.17

50.00

write

1.83

27.67

FreeBSD

writev

.39

50.00

write

4.90

209.89

Darwin 7.9 (OS X 10.3)

writev

1.05

50.00

write

10.18

61.23

Darwin 8.0 (OS X 10.4)

writev

.95

50.00

write

8.97

122.33

The table shows that the ratio for user time of write to writev is about what it is for the other systems, whereas the system time is closer for Darwin 7.9. It's undoubtedly true that the file system is one of the parts of Darwin that Apple paid the most attention to, which perhaps explains why the Darwin and FreeBSD numbers appear to be unrelated. It's also the case that Darwin was the only one of the four systems I used that was optimized for its hardware (a Mac Mini, in my case); the other systems ran on assorted cast-off Intel-based PCs. That's normal for Linux and FreeBSD, but not the hardware that Sun optimizes Solaris for. (Recall that I used Solaris on an Intel CPU, not a Sparc.)

Chap. 3 - Advanced File I/O

1. In c3/bigdir.c: Add a cast:

printf("max_path = %ld; strlen(path) = %d\n", max_path, (int)strlen(path));

2. The print_statvfs function on p. 133 works on Darwin. In the source code that you download, the test for “defined(FREEBSD)” has already been changed to “defined(BSD_DERIVED)”, which includes Darwin.

3. In case you were wondering, the function dir_read_test on pp. 158-159 doesn't work on Darwin, even though it does on FreeBSD. (Probably directory structures on the Mac are nothing like those on FreeBSD.)

Chap. 4 - Terminal I/O

1. On p. 274, 4th line from the bottom: The ec_neg1 test should be ec_false. (See the comment on the 3rd line from the top on p. 37.) This is a bug that affects all systems, not just Darwin.

Chap. 5 - Processes and Threads

(More to be added.)

Chap. 6 - Basic Interprocess Communication

(More to be added.)

Chap. 7 - Advanced Interprocess Communication

1. Darwin 7.9 (OS 10.3) still doesn't have System V messages, but 10.4 does. Neither has POSIX IPC.

(More to be added.)

Chap. 8 - Networking and Sockets

(More to be added.)

Chap. 9 - Signals and Timers

(More to be added.)