父进程中的Task_set_bootstrap_port导致子进程挂起



task_set_bootstrap_port的行为在OS X 10.6和10.7之间发生了变化。在10.6中,这可以工作:

// parent process
mach_port_t parent_recv_port = MACH_PORT_NULL;
setup_recv_port (&parent_recv_port);
task_set_bootstrap_port(mach_task_self(), parent_recv_port); 
NSTask *qtTask = [[NSTask alloc] init];
[qtTask setLaunchPath:...]; 
[qtTask launch];
...

// child process
int main (int argc, const char * argv[])
{
    mach_port_t parent_recv_port = MACH_PORT_NULL; 
    task_get_bootstrap_port(mach_task_self(), &parent_recv_port); 
    ...
}

在10.7中,子进程在父进程调用-launch后不会立即执行main的第一行——它等待直到父进程终止。(虽然子进程在ps中会立即出现)

我已经把问题缩小到呼叫task_set_bootstrap_port。当调用不存在时,子进程不会挂起。而且,如果我在task_set_bootstrap_port之后立即恢复引导端口到原始端口——

task_set_bootstrap_port(mach_task_self(), bootstrap_port);

——那么子进程也不会挂起。

上面省略了错误处理代码,但实际上它是在每次调用后检查错误。没有错误。

那么,你知道为什么task_set_bootstrap_port的行为从10.6改变到10.7吗?有解决办法吗?

约束条件包括:

  • 需要访问mach_port_t以传递给IOSurfaceLookupFromMachPort
  • Runloop可能不存在,所以不能使用NSMachPortCFMachPort

Apple Developer Technical Support告诉我,暂时改变引导端口,就像这段代码所做的那样,永远不能保证工作。在这种情况下,XPC试图在引导端口被更改后向它发送消息,但是失败了,所以事情就坏了。

快速修复是在父进程中使用bootstrap_register(尽管已弃用),在子进程中使用bootstrap_look_up。这允许子进程通过名称(一个字符串)查找父进程并从中获取端口。

最新更新