简体   繁体   English

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

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

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. 我正在玩NSSound类在我自己的后台进程中播放声音,以便不阻止用户输入。 I decided to call fork() but that is giving me problems. 我决定调用fork()但这给了我一些问题。 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. 有趣的是,如果我构造一个只调用fork()的示例,那么子进程可以NSSound调用NSSound ,只有在fork()调用之前我尝试使用其他cocoa API时才会出现崩溃。 See this example with the crashme?() calls commented: 使用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;
}

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. 当我从命令行运行它时,它可以工作,但如果我取消注释其中一个crashme?()函数的调用,分叉进程中的回放永远不会启动。 Is it because any of the Cocoa framework APIs are not async-signal safe? 是因为任何Cocoa框架API都不是异步信号安全的吗? Is there any way to make the calls async signal safe by wrapping them somehow? 是否有办法通过以某种方式包装它们来使调用异步信号安全?

I'll quote the Leopard CoreFoundation Framework Release Notes. 我将引用Leopard CoreFoundation Framework发行说明。 I don't know if they're still online, since Apple tends to replace rather than extend the Core Foundation release notes. 我不知道他们是否还在线,因为Apple倾向于取代而不是扩展Core Foundation发行说明。

CoreFoundation and fork() CoreFoundation和fork()

Due to the behavior of fork(), CoreFoundation cannot be used on the child-side of fork(). 由于fork()的行为,CoreFoundation不能在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*(). 如果你fork(),你必须使用某种类型的exec *()调用,并且你不应该在exec *()之前在子元素中使用CoreFoundation API。 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. 这适用于使用CoreFoundation的所有更高级别的API,并且由于您无法知道这些更高级别的API正在做什么,以及它们是否使用CoreFoundation API,因此您也不应使用任何更高级别的API。 This includes use of the daemon() function. 这包括使用守护进程()函数。

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

This has always been true, and there have been notes made of this on various Cocoa developer mailling lists in the past. 这一直都是事实,过去在各种Cocoa开发人员填写清单上都有这样的说明。 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. 但是CoreFoundation现在采取了一些更强有力的措施来“强制执行”这个限制,所以我们认为添加一个发行说明也是值得的。 A message is written to stderr when something uses API which is definitely known not to be safe in CoreFoundation after fork(). 当某些东西使用API​​时,会向stderr写入一条消息,而在fork()之后,这个API在CoreFoundation中肯定是不安全的。 If file descriptor 2 has been closed, however, you will get no message or notice, which is too bad. 但是,如果文件描述符2已关闭,则不会收到任何消息或通知,这太糟糕了。 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. 我们试图以一种非常容易识别的方式使进程终止,并且做了一段时间,这非常方便,但是向后的二进制兼容性阻止了我们这样做。

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. 换句话说,除了执行一个新程序之外,在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. 除了一般的POSIX禁令之外,经常提到的解释是:a)现在几乎所有的Cocoa程序都是多线程的,GCD之类的东西也是如此。 B) when you fork() , only the calling thread survives into the child process; B)当你fork() ,只有调用线程存活到子进程中; 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() . 例如, malloc()实现可能具有锁定以保护共享结构,并且在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. 等等。

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

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