代码之家  ›  专栏  ›  技术社区  ›  Grzegorz Adam Hankiewicz

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

  •  1
  • Grzegorz Adam Hankiewicz  · 技术社区  · 11 年前

    我在玩 NSSound 类在我自己的后台进程中播放声音,以免阻止用户输入。我决定打电话 fork() 但这给我带来了问题。就在声音被分配的那一刻,分叉的进程崩溃了。有趣的是,如果我构建一个只调用 分叉() ,则子进程可以调用 NSSound公司 没有问题,只有当我尝试使用其他可可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;
    }
    

    当我从命令行运行它时,它是有效的,但如果我取消对 克拉希米?() 函数,则分叉进程中的播放永远不会开始。是因为任何Cocoa框架API都不是 async-signal 安全有没有什么方法可以通过某种方式包装调用异步信号来确保其安全?

    1 回复  |  直到 7 年前
        1
  •  3
  •   Ken Thomases    11 年前

    我将引用Leopard CoreFoundation框架发布说明。我不知道它们是否仍然在线,因为苹果倾向于取代而不是扩展Core Foundation的发布说明。

    核心基础和分叉()

    由于fork()的行为,CoreFoundation不能用于 分叉的子侧()。如果你分叉(),你必须用一个 exec*()调用,并且不应该使用CoreFoundation API 在子级中,在exec*()之前。适用于所有更高级别 使用CoreFoundation的API,并且由于您不知道 更高级别的API在做什么,以及它们是否在使用CoreFoundation API,您也不应该使用任何更高级别的API。这包括 daemon()函数的使用。

    此外,根据POSIX,只有异步取消安全功能才是安全的 在fork()的子级使用,因此即使使用较低级别 libSystem/BSD/UNIX API应该保持在最低限度,最好是 只有异步取消安全功能。

    这一直都是真的,并且在 过去的各种Cocoa开发人员邮件列表。但核心基金会 现在正在采取一些更有力的措施来“强制执行”这一限制,因此 我们认为值得添加一个发布说明来命名 也可以。当某些东西使用API时,会将消息写入stderr 这在CoreFoundation中肯定是不安全的 分叉()。但是,如果文件描述符2已关闭,则不会得到 消息或通知,这太糟糕了。我们试图制定流程 以一种非常容易识别的方式终止,并持续了一段时间 非常方便,但向后的二进制兼容性使我们无法执行 所以

    换句话说,为孩子做很多事情从来都不安全 fork() 除了执行一个新程序。

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