macos - 为什么我不能在不同的 fork 进程中使用 cocoa 框架?

标签 macos cocoa audio fork bsd

我正在使用 NSSound 类在我自己的后台进程中播放声音,以免阻止用户输入。我决定调用 fork() 但这给我带来了问题。就在声音被分配的那一刻, fork 进程崩溃了。有趣的是,如果我构建一个仅调用 fork() 的示例,那么子进程可以毫无问题地调用 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?() 函数的调用之一的注释,则 fork 进程中的播放永远不会开始。是不是因为任何Cocoa框架API都不是async-signal安全的?有没有办法通过某种方式包装它们来使调用异步信号安全?

最佳答案

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

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.

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.

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() 的子端执行任何操作都是不安全的。

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

关于macos - 为什么我不能在不同的 fork 进程中使用 cocoa 框架?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15991036/

相关文章:

objective-c - WKWebKit Mac OS NSPrintOperation

objective-c - 完成启动后应用程序不会发布 NSWorkspaceDidLaunchApplicationNotification

Java JDK - 为 macOS 下载时出现 404

ios - Swift:声音输出和麦克风输入 |使用 AudioKit |

audio - 通过截断减少采样位深度

javascript - Java脚本-尝试按顺序一个一个播放音频时(通过for循环)一次播放所有音频

macos - 通过 Mac 终端运行 SWI Prolog

swift - 使用 cocoa/osx/swift 更改图像的分辨率和大小(无移动应用程序)

objective-c - NSString drawAtPoint - 模糊

forms - 如何在 AppKit 上的自定义 SwiftUI 表单中右对齐项目标签?