繁体   English   中英

为什么我不能在不同的分叉进程中使用cocoa框架?

[英]Why can't I use cocoa frameworks in different forked processes?

我正在玩NSSound类在我自己的后台进程中播放声音,以便不阻止用户输入。 我决定调用fork()但这给了我一些问题。 在分配声音的那一刻,分叉进程崩溃。 有趣的是,如果我构造一个只调用fork()的示例,那么子进程可以NSSound调用NSSound ,只有在fork()调用之前我尝试使用其他cocoa API时才会出现崩溃。 使用crashme?()调用注释这个例子:

#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都不是异步信号安全的吗? 是否有办法通过以某种方式包装它们来使调用异步信号安全?

我将引用Leopard CoreFoundation Framework发行说明。 我不知道他们是否还在线,因为Apple倾向于取代而不是扩展Core Foundation发行说明。

CoreFoundation和fork()

由于fork()的行为,CoreFoundation不能在fork()的子端使用。 如果你fork(),你必须使用某种类型的exec *()调用,并且你不应该在exec *()之前在子元素中使用CoreFoundation API。 这适用于使用CoreFoundation的所有更高级别的API,并且由于您无法知道这些更高级别的API正在做什么,以及它们是否使用CoreFoundation API,因此您也不应使用任何更高级别的API。 这包括使用守护进程()函数。

另外,根据POSIX,只有async-cancel-safe函数可以安全地在fork()的子端使用,所以即使使用较低级别的libSystem / BSD / UNIX API也应该保持在最低限度,理想情况下只能保持异步 - 防伪功能。

这一直都是事实,过去在各种Cocoa开发人员填写清单上都有这样的说明。 但是CoreFoundation现在采取了一些更强有力的措施来“强制执行”这个限制,所以我们认为添加一个发行说明也是值得的。 当某些东西使用API​​时,会向stderr写入一条消息,而在fork()之后,这个API在CoreFoundation中肯定是不安全的。 但是,如果文件描述符2已关闭,则不会收到任何消息或通知,这太糟糕了。 我们试图以一种非常容易识别的方式使进程终止,并且做了一段时间,这非常方便,但是向后的二进制兼容性阻止了我们这样做。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM