1

我的应用程序启动了一个子进程程序,以使用 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);
}

有关更多讨论,请参阅此 SO 问题popen2()

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

4

3 回答 3

3

“不响应”部分很简单:您的子进程没有运行任何类型的 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);
}
于 2013-06-22T11:10:55.010 回答
0

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

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

也就是说,当然,如果您不必有一个单独的过程。

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

高温高压

于 2013-06-28T04:14:33.480 回答
0

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

于 2014-02-07T23:48:34.093 回答