Настройка имени процесса в Mac OS X во время выполнения - PullRequest
19 голосов
/ 18 ноября 2010

Я пытаюсь изменить имя моего процесса, как оно отображается в ps и Activity Monitor во время выполнения. Я нашел несколько заметок о том, что не существует портативного способа сделать это (который мне безразличен).

Вот что я попробовал. Ни один из этих подходов не помог мне.

  • Изменение argv[0] (похоже, это путь для некоторых систем Unix)
  • Звонок [[NSProcessInfo processInfo] setProcessName:@"someName"]
  • Вызов setprogname (вызов getprogname возвращает имя, которое я задал, но это не имеет значения)

Я также читал о функции с именем setproctitle, которая должна быть определена в stdlib.h, если она доступна, но ее там нет.

Должен быть способ сделать это, потому что QTKitServer - безликий декодер для QuickTime Player X - имеет соответствующий PID QuickTime Player в своем имени процесса.

Кто-нибудь знает, как этого добиться? Я бы очень предпочел Core Foundation или POSIXy, чем метод Objective-C, чтобы сделать это.

Спасибо

Marco

Редактировать: Если это имеет какое-либо отношение, я использую Mac OS X 10.6.5 и Xcode 3.2.5

Ответы [ 2 ]

11 голосов
/ 18 сентября 2011

Есть веские причины для изменения имени процесса. Программное обеспечение Java должно изменять имена процессов, потому что при запуске различных инструментов Java я хочу видеть, какой процесс Java предназначен для какого инструмента.

Хром делает это: 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 не показывают одинаковую информацию.

Также важно: ActivityMonitor недоступен, если у вас нет графического интерфейса. Это может произойти, если вы подключились к удаленному компьютеру Apple, и никто не вошел в систему через графический интерфейс. К сожалению, есть ошибка Apple IMO. Просто запрос о наличии графического интерфейса отправляет раздражающее предупреждающее сообщение в stderr.

Сводка : Если вам нужно изменить ActivityMonitor, используйте код выше. Если у вас есть ситуации без графического интерфейса и вам не нравятся предупреждения о stderr, временно перенаправьте stderr в / dev / null во время вызова SetProcessName. Если вам нужно изменить информацию ps, напишите в argv.

3 голосов
/ 04 марта 2018

Вы можете использовать инструмент lsappinfo, который поставляется с macOS по крайней мере с 10.6 и по настоящее время (10.13.2):

Оболочка:

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());
}
...