【问题标题】:Setting process name on Mac OS X at runtime在运行时在 Mac OS X 上设置进程名称
【发布时间】:2010-11-18 17:54:14
【问题描述】:

我正在尝试更改运行时出现在psActivity Monitor 中的进程名称。我发现了几条笔记,没有可移植的方式来做到这一点(我不在乎)。

这是我尝试过的。这些方法都不适合我。

  • 更改 argv[0](似乎是某些 Unix 系统上的方式)
  • 致电[[NSProcessInfo processInfo] setProcessName:@"someName"]
  • 调用setprogname(调用getprogname返回我设置的名称,但这无关紧要)

我还读到了一个名为 setproctitle 的函数,如果它可用,它应该在 stdlib.h 中定义,但它不存在。

必须有一种方法来实现这一点,因为 QTKitServer - QuickTime Player X 的匿名解码器 - 在其进程名称中具有相应的 QuickTime Player 的 PID。

有没有人知道如何做到这一点?我更喜欢 Core Foundation 或 POSIXy 方式,而不是 Objective-C 方法。

谢谢,

马可

编辑:如果有任何相关性,我使用的是 Mac OS X 10.6.5 和 Xcode 3.2.5

【问题讨论】:

  • @Alex Brown(谁编辑了标签):我故意将这个问题的标签设置为“Mac”而不是“posix”,因为我发现这样做的 POSIXy 方式不起作用我(argv[0]setproctitle())。

标签: macos posix process


【解决方案1】:

更改进程名称有充分的理由。 Java 软件应该更改进程名称,因为在运行不同的 Java 工具时,我想查看哪个 Java 进程适用于哪个工具。

Chromium 做到了:http://src.chromium.org/viewvc/chrome/trunk/src/base/mac/mac_util.mm

Node.js 使用相同的代码实现Process.title = 'newtitle':https://github.com/joyent/node/blob/master/src/platform_darwin_proctitle.cc

注意:如果有人对其他未登录的用户执行su,则会失败:https://github.com/joyent/node/issues/1727

这里是完整的复杂荣耀的源代码。顺便说一句,有人告诉我它也适用于 Mac OS X Lion,但也适用于 su

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
void SetProcessName(CFStringRef process_name) {
  if (!process_name || CFStringGetLength(process_name) == 0) {
    NOTREACHED() << "SetProcessName given bad name.";
    return;
  }

  if (![NSThread isMainThread]) {
    NOTREACHED() << "Should only set process name from main thread.";
    return;
  }

  // Warning: here be dragons! This is SPI reverse-engineered from WebKit's
  // plugin host, and could break at any time (although realistically it's only
  // likely to break in a new major release).
  // When 10.7 is available, check that this still works, and update this
  // comment for 10.8.

  // Private CFType used in these LaunchServices calls.
  typedef CFTypeRef PrivateLSASN;
  typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
  typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
                                                          CFStringRef,
                                                          CFStringRef,
                                                          CFDictionaryRef*);

  static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
      NULL;
  static LSSetApplicationInformationItemType
      ls_set_application_information_item_func = NULL;
  static CFStringRef ls_display_name_key = NULL;

  static bool did_symbol_lookup = false;
  if (!did_symbol_lookup) {
    did_symbol_lookup = true;
    CFBundleRef launch_services_bundle =
        CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
    if (!launch_services_bundle) {
      LOG(ERROR) << "Failed to look up LaunchServices bundle";
      return;
    }

    ls_get_current_application_asn_func =
        reinterpret_cast<LSGetCurrentApplicationASNType>(
            CFBundleGetFunctionPointerForName(
                launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
    if (!ls_get_current_application_asn_func)
      LOG(ERROR) << "Could not find _LSGetCurrentApplicationASN";

    ls_set_application_information_item_func =
        reinterpret_cast<LSSetApplicationInformationItemType>(
            CFBundleGetFunctionPointerForName(
                launch_services_bundle,
                CFSTR("_LSSetApplicationInformationItem")));
    if (!ls_set_application_information_item_func)
      LOG(ERROR) << "Could not find _LSSetApplicationInformationItem";

    CFStringRef* key_pointer = reinterpret_cast<CFStringRef*>(
        CFBundleGetDataPointerForName(launch_services_bundle,
                                      CFSTR("_kLSDisplayNameKey")));
    ls_display_name_key = key_pointer ? *key_pointer : NULL;
    if (!ls_display_name_key)
      LOG(ERROR) << "Could not find _kLSDisplayNameKey";

    // Internally, this call relies on the Mach ports that are started up by the
    // Carbon Process Manager.  In debug builds this usually happens due to how
    // the logging layers are started up; but in release, it isn't started in as
    // much of a defined order.  So if the symbols had to be loaded, go ahead
    // and force a call to make sure the manager has been initialized and hence
    // the ports are opened.
    ProcessSerialNumber psn;
    GetCurrentProcess(&psn);
  }
  if (!ls_get_current_application_asn_func ||
      !ls_set_application_information_item_func ||
      !ls_display_name_key) {
    return;
  }

  PrivateLSASN asn = ls_get_current_application_asn_func();
  // Constant used by WebKit; what exactly it means is unknown.
  const int magic_session_constant = -2;
  OSErr err =
      ls_set_application_information_item_func(magic_session_constant, asn,
                                               ls_display_name_key,
                                               process_name,
                                               NULL /* optional out param */);
  LOG_IF(ERROR, err) << "Call to set process name failed, err " << err;
}

编辑:这是一个复杂而令人困惑的问题。

在 OS X 上没有 setproctitle(3)。一个必须写入 argv 数组(丑陋 并且有点危险,因为可以用伪造的东西覆盖一些环境变量)。操作正确,效果很好。

另外,Apple 有 ActivityMonitor 应用程序,类似于 Windows 下的任务管理器。上面的代码操纵了 ActivityMonitor,但 Apple 似乎并不鼓励这种操纵(因此使用了未记录的函数)。

重要提示:ps 和 ActivityMonitor 显示的信息不同。

同样重要的是:如果您没有 GUI,ActivityMonitor 将不可用。如果您通过 ssh 进入远程 Apple 机器并且没有人通过 GUI 登录,则可能会发生这种情况。可悲的是,Apple IMO 有一个错误。只需查询是否有 GUI 就会向 stderr 发送一条恼人的警告消息。

总结:如果您需要更改 ActivityMonitor,请使用上面的代码。如果您遇到无 GUI 的情况并且不喜欢 stderr 上的警告,请在调用 SetProcessName 期间将 stderr 临时重定向到 /dev/null。如果需要修改ps信息,写入argv即可。

【讨论】:

  • 顺便问一下,你的项目是开源的吗?如果没有,请考虑版权,因为代码属于 Chromium 项目。
  • 不,我需要的项目不是开源的。但请放心,我不会照原样复制代码。我正在寻找的本质上是调用 _LSSetApplicationInformationItem() 给我想要的信息。我将围绕该函数编写的代码在我们自己的库的上下文中看起来完全不同,因此不应违反 GPL。
  • 嗯...我又玩了一些,似乎有些机制根本没有接受这种变化。 [[NSProcessInfo processInfo] processName] 例如仍然有旧名称。我不知道它是从哪里得到它的信息的,但是如果有这样的不一致,我觉得使用那个代码会不够舒服。无论如何,再次感谢!
  • 它是直接来自 Apple 的部分未记录代码。难怪 Chromium 开发人员会说:警告:这里是龙!此外,像_LSSetApplicationInformationItem 这样以下划线开头的名称是私有的,这是一个常见的约定。很遗憾,Apple 不得不用 Activity Monitor 做一些专有的东西。
  • 这已从 Chromium 中删除。来自修订日志:“这涉及进程管理器功能,在 10.9 中会导致 WindowServer 签入,这会在任何不泵送事件的子进程(除了渲染器之外的所有子进程)上导致“不响应”通知。'
【解决方案2】:

您可以使用 macOS 至少从 10.6 到现在 (10.13.2) 附带的lsappinfo 工具:

壳牌:

lsappinfo setinfo <PID> --name <NAME>

C++:

#include <sstream>
#include <string>
#include <stdlib.h>

void setProcessName (pid_t pid, std::string name)
{
    std::ostringstream cmd;
    cmd << "/usr/bin/lsappinfo setinfo " << pid;
    cmd << " --name \"" << name << "\"";
    system (cmd.str().c_str());
}

【讨论】:

  • 哇,我不知道这个命令,看起来很有趣。像这样设置名称就像一个魅力! lsappinfo 似乎是 @nalply 代码描述的功能的前端。七年多之后,这样做的需要已经消失了,虽然?
  • 我尝试使用它来重命名 Python 进程,但没有成功。 lsappinfo processList 的输出只列出了图形应用程序,而不是在 shell 上运行的进程,所以我认为它只适用于那种应用程序。
  • 我在 Catalina 上看到 err=-600
  • lsappinfo 仅在您使用 sudo 运行时更改名称。
  • 我认为这仅适用于 GUI 应用程序,而不适用于从终端运行的脚本,因为您将得到 err=-600 表示不是有效的 PID
猜你喜欢
  • 2011-01-04
  • 2013-07-27
  • 1970-01-01
  • 1970-01-01
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多