【问题标题】:Programmatically check if a process is running on Mac以编程方式检查进程是否在 Mac 上运行
【发布时间】:2011-01-31 20:50:24
【问题描述】:

Mac 上是否有可用的 Carbon/Cocoa/C API 可用于枚举进程?我在 Windows 上寻找类似 @​​987654321@ 的东西。

我的目标是从代码中检查进程是否正在运行(按名称)。

谢谢!

【问题讨论】:

    标签: cocoa macos unix macos-carbon


    【解决方案1】:

    这里有一些具体的实现和细节,注意 proc->kp_proc.p_comm 有一个字符长度限制,这就是我实现 infoForPID: 的原因

    可可:

    [NSWorkspace 启动应用程序](10.2+ ,在 10.7 中已弃用,非常有限的进程列表) [NSWorkspace runningApplications](10.6+,较少限制的进程列表,但仍不包括守护进程)

    碳:

    - (NSArray*)getCarbonProcessList
    {
        NSMutableArray *ret = [NSMutableArray arrayWithCapacity:1];
        ProcessSerialNumber psn = { kNoProcess, kNoProcess };
        while (GetNextProcess(&psn) == noErr) {
            CFDictionaryRef cfDict = ProcessInformationCopyDictionary(&psn,  kProcessDictionaryIncludeAllInformationMask);
            if (cfDict) {
                NSDictionary *dict = (NSDictionary *)cfDict;
                [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                [NSString stringWithFormat:@"%@",[dict objectForKey:(id)kCFBundleNameKey]],@"pname",
                                [NSString stringWithFormat:@"%@",[dict objectForKey:@"pid"]],@"pid",
                                [NSString stringWithFormat:@"%d",(uid_t)getuid()],@"uid",                                               
                                nil]]; 
                CFRelease(cfDict);          
            }
        }
        return ret;
    }
    

    C:(见Technical Q&A QA1123 Getting List of All Processes on Mac OS X

    - (NSArray*)getBSDProcessList
    {
        NSMutableArray *ret = [NSMutableArray arrayWithCapacity:1];
        kinfo_proc *mylist;
        size_t mycount = 0;
        mylist = (kinfo_proc *)malloc(sizeof(kinfo_proc));
        GetBSDProcessList(&mylist, &mycount);
        int k;
        for(k = 0; k < mycount; k++) {
            kinfo_proc *proc = NULL;
            proc = &mylist[k];
            NSString *fullName = [[self infoForPID:proc->kp_proc.p_pid] objectForKey:(id)kCFBundleNameKey];
            if (fullName == nil) fullName = [NSString stringWithFormat:@"%s",proc->kp_proc.p_comm];
            [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                            fullName,@"pname",
                            [NSString stringWithFormat:@"%d",proc->kp_proc.p_pid],@"pid",
                            [NSString stringWithFormat:@"%d",proc->kp_eproc.e_ucred.cr_uid],@"uid",                                               
                            nil]];                                            
        }
        free(mylist);  
        return ret;
    }
    
    - (NSDictionary *)infoForPID:(pid_t)pid 
    {
        NSDictionary *ret = nil;
        ProcessSerialNumber psn = { kNoProcess, kNoProcess };
        if (GetProcessForPID(pid, &psn) == noErr) {
            CFDictionaryRef cfDict = ProcessInformationCopyDictionary(&psn,kProcessDictionaryIncludeAllInformationMask); 
            ret = [NSDictionary dictionaryWithDictionary:(NSDictionary *)cfDict];
            CFRelease(cfDict);
        }
        return ret;
    }
    

    【讨论】:

    • 这相当于使用 NSArray *runningApp=[[NSWorkspace sharedWorkspace]runningApplications];并且无法让守护进程列表运行。
    • @willyMon 是的,它相当于 runningApplications,但它比 launchApplications 的列表更完整,C 方法也为您提供了守护进程
    • 谁能解释一下我们如何获取这些进程的内存使用情况?
    • 首先,很好的答案。完整、详细和启发性。其次,可以说GetBSDProcessList(依赖)是取自Apple的TechNote的示例代码。最后 - Apple 的实现中存在内存问题,并且使用它的代码中可能存在泄漏。出于某种原因,Apple 的实现需要 '' assert(*procList == NULL);'' ,该代码立即失败。不知道应该怎么样!
    • infoForPID: 实现仅有助于 Cocoa/Carbon 捆绑应用程序(不涵盖绝大多数守护进程)。尽管 NSString 有“全名”命名,但守护进程的名称被缩减为 16 个字节。任何想法如何获得全名?
    【解决方案2】:

    TechZen 说:截至 2013 年 12 月,流程管理器已完全弃用。

    啊,我刚刚找到Process Manager reference

    看起来 GetNextProcessGetProcessInfo 有助于找出正在运行的内容。正如 Dave 所建议的,如果您正在寻找守护进程而不仅仅是 Carbon/Cocoa 进程,则可以使用GetBSDProcessList

    【讨论】:

    • 您可能不想使用这些旧功能,因为 Apple 可能会选择折旧它们。 Cocoa 类在这方面通常更安全。
    • 仅供参考,GetBSDProcessList 比您自己迭代流程管理器要快得多。
    • 进程管理器(当前)没有被弃用,并且在 64 位中可用。我不认为它像其他一些 API 那样有一把斧头。
    • 这似乎是上述所有选项中最好的记录,我没有看到任何关于弃用的说明。对于从任何 Carbon/Cocoa 应用程序(不仅仅是 Dock 应用程序)一次性检查正在运行的进程,这似乎是理想的,即使它可能比其他选项慢。
    • 还没有弃用,正在运行的应用程序是 10.6,只有这些看起来很棒
    【解决方案3】:

    有几种方法可以做到这一点:

    1. 如果是带有 Dock 图标的 GUI 应用,请使用 -[NSWorkspace launchedApplications]
    2. 通过NSTask 分叉另一个进程(如 ps 或 top 或其他),阅读结果,然后搜索自己(或通过 grep 或其他方式进行管道传输)。
    3. 使用此处描述的GetBSDProcessList 函数:http://developer.apple.com/legacy/mac/library/#qa/qa2001/qa1123.html(我过去曾成功使用过此功能)

    【讨论】:

    • 嗨,戴夫,你有机会找到更新的链接吗?似乎Apple已经重新安排了他们的开发者网站,该链接不再有效。谢谢!
    【解决方案4】:

    NSRunningApplicationClass的概述中,它说:

    NSRunningApplication 是一个为应用程序的单个实例操作和提供信息的类。仅跟踪用户应用程序;这并不提供有关系统上每个进程的信息。

    要访问所有正在运行的应用程序的列表,请使用NSWorkspace 中的 runningApplications 方法。

    我建议看看Workspace Services Programming Topics

    【讨论】:

    • 如果您可以限制自己在 10.6 上使用 GUI 应用程序,这些都很棒
    【解决方案5】:

    迟到了,但如果您确实需要一个强大的解决方案来检查任何进程是否正在运行(包括 BSD 进程),您可以执行以下操作:

    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    #include <sys/sysctl.h>
    #include <sys/types.h>
    
    int main(int argc, const char* argv[]) {
    
      pid_t pid = atoi(argv[2]);  
    
      // This MIB array will get passed to sysctl()
      // See man 3 sysctl for details
      int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
    
      struct kinfo_proc result;
      size_t oldp_len = sizeof(result);
    
      // sysctl() refuses to fill the buffer if the PID does not exist,
      // so the only way to detect failure is to set all fields to 0 upfront
      memset(&result, 0, sizeof(struct kinfo_proc));
    
      if (sysctl(name, 4, &result, &oldp_len, NULL, 0) < 0) { 
        perror("sysctl");
        return 1;
      }
    
      // SZOMB means a zombie process, one that is still visible but is not running anymore
      if (result.kp_proc.p_pid > 0 && result.kp_proc.p_stat != SZOMB) {
        printf("Process is running.\n");
      } else {
        printf("Process is NOT running.\n");
      }
    
      return 0;
    
    }
    

    请注意,上面的代码是我的一个私有库的修改版本,未经测试。但是,它应该清楚 API 的使用方式,并在 macOS 10.14.5 上成功运行。

    【讨论】:

    • 我认为你说得有道理。但是,我用它来创建一个简单的通用任务管理器。如果您确实需要列出当前在系统上运行的所有进程(如任务管理器所要求的),它是(或曾经是)为数不多的可用选项之一。
    • 对不起,我删除了我的评论,因为它被应用于错误的答案 --- 我的话是针对上面建议使用新 EndpointSecurity 框架的人......你的(看起来像你会在 'ps' 的实现中找到的代码是合理的。
    • 在 Big Sur 上完美运行
    【解决方案6】:

    您可以使用自 macOS 10.15 起可用的 EndPointSecurity.framework。更多信息请参考Writing a Process Monitor with Apple's Endpoint Security Framework

    【讨论】:

    • 虽然这最终可以奏效,但这就像用大锤杀死一只蚂蚁,简而言之:矫枉过正。首先,从启动的早期阶段开始,您需要您的“进程监视器”始终处于活动状态,并且始终保持其运行,以便始终拥有完整且可靠的列表。那么——从苹果那里获得授权也不是一件容易的事。 – Motti Shneor 11 月 28 日 19:20 删除
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 2011-12-08
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多