... however the child process is still running, consuming lots of cpu (and its ppid is now 1 which is weird).
That's standard Unix behavior when a process' parent dies – it's reparented by the init process. On OS X, that's the system-wide launched instance.
I have noticed that libdispatch calls pthread_sigmask() when creating new threads blocking many signals. I have found that calling pthread_sigmask() in my dispatch_asynced code to unblock the SIGINT signal fixes the issue (the child process is also terminated when doing a ctrl-c in my Terminal window).
I would expect GCD to do some thread-preparation before it starts dequeueing work from queues; I'm not surprised that setting up the thread's signal mask is part of that preparation. Considering the main goal of GCD is to abstract away the whole notion of threads, I would consider any property of a GCD thread to be immutable (such as those manipulated with the pthread_* APIs.)
So, what do you guys think?
To accomplish your goal, I imagine I would simply use the posix_spawn APIs to safely spawn the process from the GCD queue without the need to muck with the GCD thread's state; see posix_spawnattr_setsigmask(). Alternatively, you could spawn a thread to be designated the "fork thread", which could be configured with the appropriate signal mask and whatever other attributes. Using a simple queueing mechanism and the appropriate synchronization, your GCD blocks can communicate with the fork thread to instruct it to spawn the necessary process, which inherits the configured signal mask and whatever other state.