且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

运行时在 Mac OS X 上设置进程名称

更新时间:2022-06-01 02:30:27

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

There are good reasons to change the process name. Java software should change process names because when running different java tools I want to see which java process is for which tool.

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

Chromium does it: 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

Node.js uses same code to implement Process.title = 'newtitle': https://github.com/joyent/node/blob/master/src/platform_darwin_proctitle.cc

注意:如果有人对不同的未登录用户执行su,这将失败:https://github.com/joyent/node/issues/1727

Note: This fails if someone does su to a different not logged-in user: https://github.com/joyent/node/issues/1727

这里是完整复杂的源代码.顺便说一句,有人告诉我它也适用于 Mac OS X Lion,并且在使用 su 时也会失败.

Here the source code in its full complex glory. By the way, someone told me it also works for Mac OS X Lion and also fails with 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;
}

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

Edit: It's a complex and confusing problem.

在 OS X 上没有 setproctitle(3).必须写入 argv 数组(丑陋并且有点危险,因为有可能用虚假的东西覆盖一些环境变量).做得正确,效果很好.

On OS X there is no setproctitle(3). One has to write into the argv array (ugly and a bit dangerous because it is possible to overwrite some environment variables with bogus stuff). Done correctly it works very well.

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

Additionally Apple has the ActivityMonitor application, something like the Task Manager under Windows. The code above manipulates ActivityMonitor but this manipulation doesn't seem to be encouraged by Apple (hence the use of undocumented functions).

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

Important: ps and ActivityMonitor don't show the same information.

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

Also important: ActivityMonitor is not available if you don't have GUI. This can happen if you ssh in to a remote Apple box and there is nobody logged in by GUI. Sadly there is a bug by Apple IMO. Just querying if there is a GUI sends an annoying warning message to stderr.

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

Summary: If you need to change ActivityMonitor, use the code above. If you have GUI-less situations and and dislike warnings on stderr, redirect stderr temporarily to /dev/null during the call of SetProcessName. If you need to change ps information, write into argv.