为什么我不能在不同的分叉过程中使用可可框架? [英] Why can't I use cocoa frameworks in different forked processes?

查看:85
本文介绍了为什么我不能在不同的分叉过程中使用可可框架?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩NSSound类,以在自己的后台进程中播放声音,以便不阻止用户输入.我决定打电话给fork(),但这给我带来了麻烦.在分配声音的那一刻,分叉的过程崩溃了.有趣的是,如果我构建了一个仅调用fork()的示例,那么子进程可以毫无问题地调用NSSound,只有当我尝试在之前使用fork()通话.参见此示例,其中crashme?()调用已注释:

I was playing with the NSSound class to play a sound in a background process of my own so as to not block user input. I decided to call fork() but that is giving me problems. At the very moment the sound is allocated the forked process crashes. The funny thing is if I construct an example which only calls fork(), then the child process can call NSSound without problems, the crashes come only if I try to use other cocoa APIs before the fork()call. See this example with the crashme?() calls commented:

#import <AppKit/AppKit.h>
#import <math.h>

#define FILENAME \
    "/System/Library/Components/CoreAudio.component/" \
    "Contents/SharedSupport/SystemSounds/dock/drag to trash.aif"

void crashme1(void)
{
    NSString *path = [[NSString alloc] initWithUTF8String:"false file"];
    NSSound *sound = [[NSSound alloc]
        initWithContentsOfFile:path byReference:YES];
}

void crashme2(void)
{
    NSInteger tag = 0;
    [[NSWorkspace sharedWorkspace]
        performFileOperation:NSWorkspaceRecycleOperation
        source:@"." destination:nil
        files:[NSArray arrayWithObject:@"false file"] tag:&tag];
}

double playAif(void)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *path = [[NSString alloc] initWithUTF8String:FILENAME];
    NSSound *sound = [[NSSound alloc]
        initWithContentsOfFile:path byReference:YES];
    [path release];

    if (!sound) {
        [pool release];
        return -1;
    }

    const double ret = [sound duration];
    [sound play];
    [sound autorelease];
    [pool release];
    return ret;
}

int main(int argc, char *argv[])
{
    //crashme1();
    //crashme2();
    int childpid = fork();
    if (0 == childpid)  {
        printf("Starting playback\n");
        double wait = playAif();
        sleep(ceil(wait));
        wait = playAif();
        sleep(ceil(wait));
        printf("Finished playback\n");
    }
    return 0;
}

当我从命令行运行此命令时,它可以工作,但是如果我取消对任一crashme?()函数的调用的注释,则永远不会开始分叉过程中的回放.是否是因为任何Cocoa框架API都不

When I run this from the commandline it works, but if I uncomment one of the calls to either crashme?() functions the playback in the forked process never starts. Is it because any of the Cocoa framework APIs are not async-signal safe? Is there any way to make the calls async signal safe by wrapping them somehow?

推荐答案

我将引用《 Leopard CoreFoundation框架发行说明》.我不知道它们是否仍然在线,因为Apple倾向于替换而不是扩展Core Foundation发行说明.

I'll quote the Leopard CoreFoundation Framework Release Notes. I don't know if they're still online, since Apple tends to replace rather than extend the Core Foundation release notes.

CoreFoundation和fork()

由于fork()的行为,无法在CoreFoundation上使用 fork()的子端.如果您使用fork(),则必须在其后加上一个 exec *()调用,您不应使用CoreFoundation API 在子对象中,在exec *()之前.适用于所有上级 使用CoreFoundation的API,并且由于您不知道那些API是什么 更高级别的API正在执行的操作,以及它们是否正在使用CoreFoundation API,您也不应使用任何更高级别的API.这包括 使用daemon()函数.

CoreFoundation and fork()

Due to the behavior of fork(), CoreFoundation cannot be used on the child-side of fork(). If you fork(), you must follow that with an exec*() call of some sort, and you should not use CoreFoundation APIs within the child, before the exec*(). The applies to all higher-level APIs which use CoreFoundation, and since you cannot know what those higher-level APIs are doing, and whether they are using CoreFoundation APIs, you should not use any higher-level APIs either. This includes use of the daemon() function.

此外,对于POSIX,只有异步取消安全功能可以安全地进行 用于fork()的子级,因此即使使用较低级别的 libSystem/BSD/UNIX API应该保持最小,最好是 仅异步取消安全功能.

Additionally, per POSIX, only async-cancel-safe functions are safe to use on the child side of fork(), so even use of lower-level libSystem/BSD/UNIX APIs should be kept to a minimum, and ideally to only async-cancel-safe functions.

这一直是正确的,并且对此有一些注释 过去各种Cocoa开发人员的邮件列表.但是CoreFoundation 现在正在采取一些更强有力的措施来强制执行"此限制,因此 我们认为值得添加发行说明来称呼它 以及.当某些东西使用API​​时,一条消息被写到stderr 在CoreFoundation之后肯定知道这是不安全的 叉().但是,如果文件描述符2已关闭,则不会得到任何结果. 消息或通知,这太糟糕了.我们试图进行流程 以一种非常容易辨认的方式终止,并做了一段时间,那是 非常方便,但是向后的二进制兼容性使我们无法这样做 如此.

This has always been true, and there have been notes made of this on various Cocoa developer mailling lists in the past. But CoreFoundation is taking some stronger measures now to "enforce" this limitation, so we thought it would be worthwhile to add a release note to call this out as well. A message is written to stderr when something uses API which is definitely known not to be safe in CoreFoundation after fork(). If file descriptor 2 has been closed, however, you will get no message or notice, which is too bad. We tried to make processes terminate in a very recognizable way, and did for a while and that was very handy, but backwards binary compatibility prevented us from doing so.

换句话说,除了执行新程序之外,在fork()的子方面做很多事情从来都不是安全的.

In other words, it has never been safe to do much of anything on the child side of a fork() other than exec a new program.

除了一般的POSIX禁止之外,经常提到的解释是:a)如今,几乎所有的Cocoa程序都是多线程的,例如GCD之类的. B)当您fork()时,只有调用线程幸存到子进程中;其他线程就消失了.由于这些线程本来可以操纵共享资源,所以子进程不能依赖于具有健全的状态.例如,malloc()实现可能具有一个锁来保护共享结构,并且该锁可能在fork()时已经由一个已经消失的线程持有.因此,如果其余线程尝试分配内存,则它可能会无限期挂起.其他共享数据结构可能只是处于损坏状态.等等.

Besides the general POSIX prohibition, an oft-mentioned explanation is: a) pretty much all Cocoa programs are multithreaded these days, what with GCD and the like. B) when you fork(), only the calling thread survives into the child process; the other threads just vanish. Since those threads could have been manipulating shared resources, the child process can't rely on having sane state. For example, the malloc() implementation may have a lock to protect shared structures and that lock could have been held by one of the now-gone threads at the time of the fork(). So, if the remaining thread tries to allocate memory, it may hang indefinitely. Other shared data structures may simply be in a corrupted state. Etc.

这篇关于为什么我不能在不同的分叉过程中使用可可框架?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆