【问题标题】:Linux API to list running processes?Linux API 列出正在运行的进程?
【发布时间】:2010-10-30 16:47:56
【问题描述】:

我需要一个 C/C++ API,它允许我列出 Linux 系统上正在运行的进程,并列出每个进程打开的文件。

我确实想直接读取 /proc/ 文件系统。

谁能想到办法做到这一点?

【问题讨论】:

  • 实用程序 lsof 可以做到这一点。它是开源的,阅读代码并了解它是如何工作的(不过它必须使用 /proc)

标签: c++ c linux api process


【解决方案1】:

http://procps.sourceforge.net/

http://procps.cvs.sourceforge.net/viewvc/procps/procps/proc/readproc.c?view=markup

是ps等进程工具的来源。他们确实使用 proc (表明它可能是传统和最好的方式)。他们的源代码可读性很强。文件

/procps-3.2.8/proc/readproc.c

可能有用。 ephemient 发布 的一个有用建议是链接到 libproc 提供的 API,它应该在您的 repo 中可用(或者我会说已经安装),但您需要标题的“-dev”变体等等。

祝你好运

【讨论】:

【解决方案2】:

如果您不想从 '/proc.然后你可以考虑编写一个内核模块来实现你自己的系统调用。并且你的系统调用应该写成可以获取当前进程的列表,比如:

/* ProcessList.c 
    Robert Love Chapter 3
    */
    #include < linux/kernel.h >
    #include < linux/sched.h >
    #include < linux/module.h >

    int init_module(void) {
        struct task_struct *task;
        for_each_process(task) {
              printk("%s [%d]\n",task->comm , task->pid);
        }
        return 0;
    }
   
    void cleanup_module(void) {
        printk(KERN_INFO "Cleaning Up.\n");
    }

上面的代码取自我在http://linuxgazette.net/133/saha.html的文章。一旦你有了自己的系统调用,你就可以从你的用户空间程序中调用它。

【讨论】:

  • 使您的程序依赖于自定义内核模块比读取 /proc/ 要脏得多。如果没有充分的理由,我不会这样做...
  • 不应该有锁吗?
  • 编写新的系统调用通常是个坏主意。更糟糕的是,您可以在没有它的情况下获得正在运行的进程列表。编写新的系统调用应该有真的很好的理由。 Linux kernel development book 简要描述。
  • 来吧伙计们,这很有趣!让我们生活一点点:-)
  • @aksommerville 当然。不过,如果您只是编写一个供自己使用的程序,我会说这是一个非常好的理由。
【解决方案3】:

给你(C/C++):

你可以在这里找到它: http://ubuntuforums.org/showthread.php?t=657097

本质上,它的作用是遍历/proc/&lt;pid&gt; 中的所有数字文件夹,然后在/proc/&lt;pid&gt;/exe 上执行读取链接,或者如果您需要命令行参数cat /proc/&lt;pid&gt;/cmdline

进程打开的文件描述符位于/proc/&lt;pid&gt;/fd/&lt;descriptor&gt;,您可以通过对每个符号链接执行读取链接来获取文件名,例如readlink /proc/&lt;pid&gt;/fd/&lt;descriptor&gt;。 fd 可以是设备,例如 /dev/null、套接字或文件,甚至可能更多。

#include

ssize_t readlink(const char *path, char *buf, size_t bufsiz);
成功时,readlink() 返回放在 buf 中的字节数。
出错时返回 -1 并设置 errno 以指示错误。

顺便说一句,这与readproc.c 所做的(或至少是所做的)相同。
当然,希望他们做到了没有缓冲区溢出的可能性。

#ifndef __cplusplus
    #define _GNU_SOURCE
#endif

#include <unistd.h>
#include <dirent.h>
#include <sys/types.h> // for opendir(), readdir(), closedir()
#include <sys/stat.h> // for stat()

#ifdef __cplusplus
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdarg>
#else
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdarg.h>
#endif


#define PROC_DIRECTORY "/proc/"
#define CASE_SENSITIVE    1
#define CASE_INSENSITIVE  0
#define EXACT_MATCH       1
#define INEXACT_MATCH     0


int IsNumeric(const char* ccharptr_CharacterList)
{
    for ( ; *ccharptr_CharacterList; ccharptr_CharacterList++)
        if (*ccharptr_CharacterList < '0' || *ccharptr_CharacterList > '9')
            return 0; // false
    return 1; // true
}


int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive)
{
    if (intCaseSensitive)
        return !strcmp(s1, s2);
    else
        return !strcasecmp(s1, s2);
}

int strstr_Wrapper(const char* haystack, const char* needle, int intCaseSensitive)
{
    if (intCaseSensitive)
        return (int) strstr(haystack, needle);
    else
        return (int) strcasestr(haystack, needle);
}


#ifdef __cplusplus
pid_t GetPIDbyName(const char* cchrptr_ProcessName, int intCaseSensitiveness, int intExactMatch)
#else
pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName, int intCaseSensitiveness, int intExactMatch)
#endif
{
    char chrarry_CommandLinePath[100]  ;
    char chrarry_NameOfProcess[300]  ;
    char* chrptr_StringToCompare = NULL ;
    pid_t pid_ProcessIdentifier = (pid_t) -1 ;
    struct dirent* de_DirEntity = NULL ;
    DIR* dir_proc = NULL ;

    int (*CompareFunction) (const char*, const char*, int) ;

    if (intExactMatch)
        CompareFunction = &strcmp_Wrapper;
    else
        CompareFunction = &strstr_Wrapper;


    dir_proc = opendir(PROC_DIRECTORY) ;
    if (dir_proc == NULL)
    {
        perror("Couldn't open the " PROC_DIRECTORY " directory") ;
        return (pid_t) -2 ;
    }

    // Loop while not NULL
    while ( (de_DirEntity = readdir(dir_proc)) )
    {
        if (de_DirEntity->d_type == DT_DIR)
        {
            if (IsNumeric(de_DirEntity->d_name))
            {
                strcpy(chrarry_CommandLinePath, PROC_DIRECTORY) ;
                strcat(chrarry_CommandLinePath, de_DirEntity->d_name) ;
                strcat(chrarry_CommandLinePath, "/cmdline") ;
                FILE* fd_CmdLineFile = fopen (chrarry_CommandLinePath, "rt") ;  // open the file for reading text
                if (fd_CmdLineFile)
                {
                    fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess) ; // read from /proc/<NR>/cmdline
                    fclose(fd_CmdLineFile);  // close the file prior to exiting the routine

                    if (strrchr(chrarry_NameOfProcess, '/'))
                        chrptr_StringToCompare = strrchr(chrarry_NameOfProcess, '/') +1 ;
                    else
                        chrptr_StringToCompare = chrarry_NameOfProcess ;

                    //printf("Process name: %s\n", chrarry_NameOfProcess);
                    //printf("Pure Process name: %s\n", chrptr_StringToCompare );

                    if ( CompareFunction(chrptr_StringToCompare, cchrptr_ProcessName, intCaseSensitiveness) )
                    {
                        pid_ProcessIdentifier = (pid_t) atoi(de_DirEntity->d_name) ;
                        closedir(dir_proc) ;
                        return pid_ProcessIdentifier ;
                    }
                }
            }
        }
    }
    closedir(dir_proc) ;
    return pid_ProcessIdentifier ;
}

#ifdef __cplusplus
    pid_t GetPIDbyName(const char* cchrptr_ProcessName)
    {
        return GetPIDbyName(cchrptr_ProcessName, CASE_INSENSITIVE, EXACT_MATCH) ;
    }
#else
    // C cannot overload functions - fixed
    pid_t GetPIDbyName_Wrapper(const char* cchrptr_ProcessName, ... )
    {
        int intTempArgument ;
        int intInputArguments[2] ;
        // intInputArguments[0] = 0 ;
        // intInputArguments[1] = 0 ;
        memset(intInputArguments, 0, sizeof(intInputArguments) ) ;
        int intInputIndex ;
        va_list argptr;

        va_start( argptr, cchrptr_ProcessName );
            for (intInputIndex = 0;  (intTempArgument = va_arg( argptr, int )) != 15; ++intInputIndex)
            {
                intInputArguments[intInputIndex] = intTempArgument ;
            }
        va_end( argptr );
        return GetPIDbyName_implements(cchrptr_ProcessName, intInputArguments[0], intInputArguments[1]);
    }

    #define GetPIDbyName(ProcessName,...) GetPIDbyName_Wrapper(ProcessName, ##__VA_ARGS__, (int) 15)

#endif

int main()
{
    pid_t pid = GetPIDbyName("bash") ; // If -1 = not found, if -2 = proc fs access error
    printf("PID %d\n", pid);
    return EXIT_SUCCESS ;
}

【讨论】:

  • strstr_Wrapper() 中看起来很奇怪的类型转换。 return (strstr(haystack, needle) != NULL) 可能更好。
  • 基本上,遍历 /proc/ 中的所有数字文件夹,然后执行 readlink /proc//exe 或 cat /proc//cmdline
  • 可能会出现chrarry_NameOfProcess[300] 的堆栈溢出。这段代码不太好。
  • @Craig McQueen:如果您的进程名称很长,请增加缓冲区大小,或者更好地将 fscanf 限制为缓冲区的长度...
  • 更好的是,使用 C++ 文件流 I/O 来避免缓冲区溢出问题,并避免 complexity of using fscanf() in a way that avoids buffer overflows and truncation
【解决方案4】:

如果您不这样做,那么我猜您将使用的任何 API 最终都会读取 /proc 文件系统。以下是一些执行此操作的程序示例:

但不幸的是,这并不构成 API。

【讨论】:

  • 没错。读取 /proc 文件系统 Linux 内核将进程信息导出到用户空间的官方机制。要求一个不使用它的解决方案基本上只是表明对系统如何工作的无知。它不会带来更好的软件。
【解决方案5】:

PS 和所有其他工具(内核模块除外)从/proc 读取。 /proc 是一个由内核动态创建的特殊文件系统,以便用户模式进程可以读取仅对内核可用的数据。

因此,推荐的方法是从/proc 读取。

您可以快速直观地查看/proc 文件系统,了解其结构。 对于每个进程,都有一个/proc/pid,其中 pid 是进程 ID 号。在这个文件夹中有几个文件,其中包含有关当前进程的不同数据。 如果你跑

strace ps -aux

您将看到程序 ps 如何从 /proc 读取此数据。

【讨论】:

    【解决方案6】:

    不读取 /proc 的唯一方法是调用“ps aux”,遍历每一行,读取第二列(PID)并使用它调用 lsof -p [PID]。

    ...我建议阅读 /proc ;)

    【讨论】:

    • 对机器上正在运行的进程进行操作本质上是不可移植的,所以我认为这在实践中不太可能成为问题。另请注意,GNU ps 具有控制输出格式的选项,允许您仅获取所需的数据而无需解析整行。
    • ps 读取 /proc,因此与其他答案相比,这不符合问题的不合理的约束
    【解决方案7】:

    libprocps 有一个库 procps-ng project。在 Ubuntu 13.04 上,如果你使用strace ps,那么你可以看到ps 使用libprocps

    【讨论】:

    • 这看起来像现在要走的路。一种非常标准化的读取/proc/###/* 数据的方法,而无需自己找出如何读取可用字段。
    【解决方案8】:

    阅读过程还不错。我无法用 C++ 向您展示,但以下 D 代码应该为您指明正确的方向:

    导入std.stdio; 导入标准字符串; 导入标准文件; 导入标准正则表达式; 导入std.c.linux.linux; 别名 std.string.split 爆炸; 字符串 srex = "^/proc/[0-9]+$"; 字符串trex =“状态:[\t][SR]”; 正则表达式 rex; 正则表达式 rext; string[] scanPidDirs(字符串目标) { 字符串[] 结果; 布尔回调(DirEntry* de) { 如果(de.isdir) { 如果 (rex.find(de.name) >= 0) { string[] a = explode(de.name, "/"); 字符串 pid = a[a.length-1]; 字符串 x = cast(string) std.file.read(de.name ~ "/status"); int n = rext.find(x); 如果 (n >= 0) { x = cast(string) std.file.read(de.name ~ "/cmdline"); // 这是空终止的 if (x.length) x.length = x.length-1; a = 爆炸(x,“/”); 如果(a.长度) x = a[a.length-1]; 别的 x = ""; 如果(x == 目标) { 结果 ~= pid ~ "/" ~x; } } } } 返回真; } listdir("/proc", &callback); 返回结果.dup; } 无效的主要(字符串 [] 参数) { rex=新正则表达式(srex); rext=新正则表达式(trex); 字符串[] a = scanPidDirs(args[1]); 如果 (!a.length) { writefln("未找到"); 返回; } writefln("%d 个匹配进程", a.length); foreach (s; a) { string[] p = explode(s, "/"); int pid = atoi(p[0]); writef("停止 %s (%d)?", s, pid); 字符串 r = readln(); if (r == "Y\n" || r == "y\n") 杀死(PID,SIGUSR1); } }

    【讨论】:

      【解决方案9】:

      通过名称查找任何进程的 pid 的简单方法

      pid_t GetPIDbyName(char* ps_name)
      {
      
          FILE *fp;
          char *cmd=(char*)calloc(1,200);
          sprintf(cmd,"pidof %s",ps_name);
          fp=popen(cmd,"r");
          fread(cmd,1,200,fp);
          fclose(fp);
          return atoi(cmd);
      }
      

      【讨论】:

      • 这不是 linux API。
      • 在我的 CentOS7 Linux 系统上 'pidof' 可用,rpm 'sysvinit-tools' 的成员
      • 在 Debian Linux 6.0 上也发现了它
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-31
      • 2015-12-31
      • 2013-05-21
      • 2010-12-10
      • 1970-01-01
      • 1970-01-01
      • 2011-05-09
      相关资源
      最近更新 更多