【问题标题】:Subprocess icon bounces in dock子进程图标在停靠栏中弹跳
【发布时间】:2013-05-06 03:41:35
【问题描述】:

我的应用程序使用 QuickTime 框架通过fork() 和管道启动一个子进程程序来读取视频。子进程在不忙时进入等待循环,即它执行usleep 直到有输入。子进程不是 GUI 应用程序,它是用 C++ 编写的。

打开使用 MSVC 编解码器编码的 AVI 视频时,应用程序图标的第二个副本会显示在 Dock 中并反弹。在活动监视器中大约 30 秒后,我可以看到子进程变为“无响应”,即使 CPU 似乎为 ~0%。子进程仍在运行并响应;只是活动监视器另有说明。

如果我查看子进程的状态,通过gdb attach 或检查其输出;一切看起来都很好。我可以告诉子进程关闭文件并打开另一个文件并继续使用它,此时弹跳停靠图标消失并且进程未标记为无响应。

就好像 OSX 认为我的子进程已经崩溃(?)但我无法检测到异常。

如何停止子进程在 Dock 中显示图标、弹跳并被标记为无响应?

这是我与子进程建立通信的方式:

#include <unistd.h>

#define READ 0
#define WRITE 1

// Start process
pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
  int p_stdin[2], p_stdout[2];
  pid_t pid;

  // Set up pipes
  if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
    return(-1);

  pid = fork();

  if(pid < 0)
    return(pid);
  else if(pid == 0)
  {
    // Set up communication via stdin/out
    close(p_stdin[WRITE]);
    dup2(p_stdin[READ], READ);
    close(p_stdout[READ]);
    dup2(p_stdout[WRITE], WRITE);

    execvp(command, argv); // run subprocess
    perror("execvp");

    exit(1);
  }

  // Provide pointers to the file descriptors to the caller
  if(infp == NULL)
    close(p_stdin[WRITE]);
  else
    *infp = p_stdin[WRITE];

  if(outfp == NULL)
    close(p_stdout[READ]);
  else
    *outfp = p_stdout[READ];

  return(pid);
}

有关popen2()的更多讨论,请参阅this SO question

注意:此代码可能是也可能不是我的问题的原因。作为第一步,我真的很想证明是什么原因。

【问题讨论】:

  • 向我们展示一些代码(并告诉我们“某些特定数据”的含义)。
  • 什么部分的代码?子流程使用 QuickTime 框架。当使用 MSVC 编解码器打开一些 AVI 时,我可以观察到所描述的行为。我怀疑 QuickTime 可以 识别的其他一些编解码器也会导致图标弹跳/无响应。
  • 我很难知道代码的哪一部分是相关的,但是有一些代码——它的任何部分——可能会有所帮助。我在这里有一台 OS X 计算机,但我无法解决这个问题(事实上,问题是什么??)。
  • 子进程不应获得停靠图标(弹跳或其他方式),并且不应将其标记为在响应所有意图和目的时未响应。我的问题是如何阻止这种行为?
  • 例如,您还没有告诉我们您使用什么机制或 API 来生成子进程。这不是一个真正的问题。

标签: c++ macos debugging


【解决方案1】:

“不响应”部分很简单:您的子进程没有运行任何类型的 runloop,因此从系统的 POV 来看,它没有处理事件。

我对为什么您的新进程获得停靠图标有点模糊,但它基本上归结为fork() 创建一个继承父进程属性的进程(在这种情况下,它是一个前景应用)。 OS X 有许多机制以比fork() 更明智的方式启动子进程。如果您的应用在 Cocoa 中,请使用 NSTask,否则请查看 posix_spawn(2)

这应该是您日常工作的替代品:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <spawn.h>
#include <signal.h>
#include <crt_externs.h>

#define READ 0
#define WRITE 1
#define environ (*_NSGetEnviron())

pid_t popen2(const char *command, char * const argv[], int *infp, int *outfp)
{
    int p_stdin[2], p_stdout[2];
    pid_t pid;

    if(pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
        return(-1);

    posix_spawn_file_actions_t file_actions;
    posix_spawn_file_actions_init(&file_actions);
    posix_spawn_file_actions_adddup2(&file_actions, p_stdin[READ], 0);
    posix_spawn_file_actions_adddup2(&file_actions, p_stdout[WRITE], 1);
    posix_spawn_file_actions_adddup2(&file_actions, 2, 2);

    posix_spawnattr_t spawnAttributes;

    posix_spawnattr_init(&spawnAttributes);
    sigset_t no_signals;
    sigset_t all_signals;
    sigemptyset (&no_signals);
    sigfillset (&all_signals);
    posix_spawnattr_setsigmask(&spawnAttributes, &no_signals);
    posix_spawnattr_setsigdefault(&spawnAttributes, &all_signals);
    short flags = POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
    posix_spawnattr_setflags(&spawnAttributes, flags);

    if (posix_spawn(&pid, command, &file_actions, &spawnAttributes, argv, environ)) {
        perror("posix_spawn");

        exit(1);
    }

    close(p_stdin[READ]);
    if(infp == NULL)
        close(p_stdin[WRITE]);
    else
        *infp = p_stdin[WRITE];

    close(p_stdout[WRITE]);
    if(outfp == NULL)
        close(p_stdout[READ]);
    else
        *outfp = p_stdout[READ];

    return(pid);
}

【讨论】:

  • 当我打开其他视频文件时,子进程不会生成停靠图标或“无响应”。如果可能的话,我想继续使用fork();它似乎大部分时间都有效;除非你知道为什么它会更好,否则它可能意味着更多的工作,但仍然存在同样的问题。
  • 很可能特定编解码器使用了其他编解码器未使用的框架,这与您从应用程序进程继承的状态产生了不利的交互。在应用进程上使用fork() 时,此类问题非常普遍,因为您继承了各种状态,其中大部分不应该被继承。
  • 如果我们同意你的理论,有没有办法测试这个?如果我直接从命令行运行子进程并打开视频文件,则没有反弹或“无响应”。也许我们可以通过从 Python 或其他脚本语言运行 fork() 和 NSTask 来证明你的理论? (虽然我不知道 Python)。
  • 你可以从 python 测试这个,但只能从基于 python 的可可应用程序,因为弹跳是由于从你的主应用程序继承的状态。查看 PyObjC pythonhosted.org/pyobjc 的示例。但是,是什么让您如此犹豫要重新构建您的实际应用程序呢?您的子进程似乎是一个单独的二进制文件,所以在我看来,您有一个非常简单的 fork()/exec() 构造,它可以映射到同样简单的 NSTask
  • 我不太了解Objective C,通过std io建立通信可能很棘手。如果您有一个示例显示使用 fork 可能会导致类似的停靠图标弹跳/不响应,那么我会更相信您的回答会解决我的问题。
【解决方案2】:

如果您使用的是支持 c++11 的编译器,我建议您查看packaged_tasks

或者,您也可以使用condition_variables,但我会先尝试使其与打包任务一起使用。条件变量比更高级别的打包任务更原始。无论哪种方式,使用这些机制都比传统的 IPC 技术更容易(并且符合标准)。

当然,如果您不必有单独的流程。

如需更多信息,我强烈建议您查看The C++ Programming Language, 4th Edition。它有一个简单的生产者/消费者机制示例,带有一个可能非常适合您的简单向量。如果你不喜欢这本书,我相信你可以在网上找到使用condition_variablesfuturespromises 的类似示例。

HTH

【讨论】:

  • 子进程必须分开。在这种情况下,这就是它存在的原因:QuickTime 是一个仅限 3​​2 位的库,这是一个辅助进程。
【解决方案3】:

我最终将子进程重写为 MacOS XPC 服务。在 XPC 服务的属性列表中,我添加了 LSBackgroundOnly 以让 Launch Services 忽略系统事件处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多