There are limits to what you can do in the child process.
Posted on May 2, 2011 by Tommy McGuireAccording to the Mac OS X manual page for fork(2),
There are limits to what you can do in the child process. To be totally safe you should restrict yourself to only executing async-signal safe operations until such time as one of the exec functions is called. All APIs, including global data symbols, in any framework or library should be assumed to be unsafe after a fork() unless explicitly documented to be safe or async-signal safe. If you need to use these frameworks in the child process, you must exec. In this situation it is reasonable to exec yourself.
Async-signal safe operations are (according to Mac OS X sigaction(2)):
Base Interfaces: _exit(), access(), alarm(), cfgetispeed(), cfgetospeed(), cfsetispeed(), cfsetospeed(), chdir(), chmod(), chown(), close(), creat(), dup(), dup2(), execle(), execve(), fcntl(), fork(), fpathconf(), fstat(), fsync(), getegid(), geteuid(), getgid(), getgroups(), getpgrp(), getpid(), getppid(), getuid(), kill(), link(), lseek(), mkdir(), mkfifo(), open(), pathconf(), pause(), pipe(), raise(), read(), rename(), rmdir(), setgid(), setpgid(), setsid(), setuid(), sigaction(), sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember(), signal(), sigpending(), sigprocmask(), sigsuspend(), sleep(), stat(), sysconf(), tcdrain(), tcflow(), tcflush(), tcgetattr(), tcgetpgrp(), tcsendbreak(), tcsetattr(), tcsetpgrp(), time(), times(), umask(), uname(), unlink(), utime(), wait(), waitpid(), write().
Realtime Interfaces: aio_error(), clock_gettime(), sigpause(), timer_getoverrun(), aio_return(), fdatasync(), sigqueue(), timer_gettime(), aio_suspend(), sem_post(), sigset(), timer_settime().
ANSI C Interfaces: strcpy(), strcat(), strncpy(), strncat(), and perhaps some others.
Extension Interfaces: strlcpy(), strlcat().
All functions not in the above lists are considered to be unsafe with respect to signals. That is to say, the behaviour of such functions when called from a signal handler is undefined. In general though, signal handlers should do little more than set a flag; most other actions are not safe.
The bottom line is that the child process is apparently sharing a lot with the parent process, along the same lines as older BSD's vfork(2) optimization. The normal Unix behavior of fork(2) involves sharing almost nothing; with the exception of some pretty painful operations, the child process shares nothing with the parent. Resources are duplicated instead.
fork(2) is a fundamental operation in Unix; radical changes in it's behavior imply radical changes in the operating system. Certainly, reading that CAVEATS paragraph would lead me to believe that essentially no existing Unix programs could run on iOS without significant code changes. (Ahhh, brings back memories of the bad old days when I had to deal with SunOS, Solaris, AIX, HP-UX, and Irix; all of which claimed to be Unix, but all of which had their own bizarre behavior from basic operations. Wonderful.)
My conclusion: Mac OS X isn't Unix, no matter how many people say it is. That man page is a fine counter-example.
Want to know more about Unix system programming? See Advanced Programming in the Unix Environment by the late W. Richard Stevens. (Truthfully, you'll probably want the second edition.) It shouldn't take long to appreciate the depraved insanity that is the Unix family tree.