Linux API 列出正在运行的进程? - 有论据
Posted
技术标签:
【中文标题】Linux API 列出正在运行的进程? - 有论据【英文标题】:Linux API to list running processes? - with arguments 【发布时间】:2011-11-23 21:12:54 【问题描述】:这篇文章几乎回答了我的问题: Linux API to list running processes?
但示例代码(C++/C - 见下文)中缺少的是进程的参数。例如,名为 mypaint 的程序只会被列为“python2.7”,而不是完整的可执行文件和参数“python2.7 /usr/bin/mypaint”。
来自 proc 的手册页: /proc/[pid]/cmdline 这包含该进程的完整命令行,除非该进程是僵尸进程。在后一种情况下,该文件中没有任何内容:也就是说,对该文件的读取将返回 0 个字符。命令行参数在此文件中显示为一组由空字节 ('\0') 分隔的字符串,在最后一个字符串之后还有一个空字节。
如何修改此代码以同时列出进程的参数?
#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 ;
(chrarry_NameOfProcess = only executable 如何显示参数)?
【问题讨论】:
您引用的手册页说您需要从文件中读取参数作为以空字符结尾的字符串。你试过什么?你到底在哪里坚持这个? 如何将文件读取为以空字符结尾的字符串?我尝试了很多东西,但我从来没有得到过争论。while(fgets(line, sizeof line, fd_CmdLineFile)) printf("PROCESS NAME WITH ARGS %s\n", line
【参考方案1】:
假设您真的不想单独解析每个参数,而只想像当前一样打印它们,一个简单的方法是:
(1) 用fread
替换fscanf
并读取任意数量的字节。 (或者如果你想专业地做的话,先找到文件长度并读取数量。)这会将包括空字节在内的所有内容读入你的缓冲区。
(2) 循环遍历缓冲区,用空格替换空值。
(3) 在读取的字节末尾放置一个 null 以终止字符串。
(4) 然后打印出来。
所以在你的 GetPIDbyName() 函数中是这样的:
const int BUFFERSIZE = 300;
int bytesread;
char chrarry_NameOfProcess[BUFFERSIZE] ;
//...........
FILE* fd_CmdLineFile = fopen (chrarry_CommandLinePath, "rb") ;
if (fd_CmdLineFile)
//fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess) ;
bytesread = fread(chrarry_NameOfProcess, 1, BUFFERSIZE, fd_CmdLineFile);
for (int i = 0; i < bytesread; ++i)
if (chrarry_NameOfProcess[i] == '\0')
chrarry_NameOfProcess[i] = ' ';
chrarry_NameOfProcess[bytesread] = '\0';
fclose(fd_CmdLineFile);
//... the rest
【讨论】:
【参考方案2】:要读取以 NULL 结尾的字符串,您将不得不做一些小技巧。
如果您像往常一样调用fgets(buffer, size, fp)
并尝试打印结果,那么您将得不到您想要的。原因是当您使用printf()
尝试打印结果时,您将获得第一个 NULL-terminated 字符串。打印完之后,您需要转到下一个以 NULL 结尾的字符串,因为 NULL 表示字符串的结尾。
所以,有一些代码可以做到这一点:
int len = 0;
while((len = read(fd, buffer, sizeof(buffer))) > 0)
/* NOTE: not sure if this condition is correct: might be -1. */
if(len == sizeof(buffer)) /* arguments too long for buffer, use what exists or print error message. */
int i = 0;
while(i < len)
char *s = buffer + i;
printf("Argument: %s\n", s);
i += strlen(s) + 1; /* +1 skips over NULL at end of string. */
澄清一下:buffer + i
等同于&buffer[i]
。它基本上将缓冲区视为一个字符数组,其中包含多个链接在一起的以 NULL 结尾的字符串。
我在这里使用read()
而不是fgets()
的原因是,当使用fgets()
时,很难确定从文件中真正读取了多少字节。 strlen()
没有帮助,我知道的唯一方法是检查换行符的位置,它假定首先存在换行符。 . .太丑了。
编辑:或者你可以使用 ftell(),我想,但这更难看!
【讨论】:
read(2)
条件可能应该是 while((len = read(...)) > 0)
-- 0
当你用完所有东西时,-1
是一个错误条件。【参考方案3】:
openproc
可用于列出完整的命令行参数,只需确保查询PROC_FILLCOM
。您需要 procps
库才能编译代码(例如,Debian 上的 apt install libprocps-dev
)。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <proc/readproc.h>
// compile with:
// gcc procls.c -lprocps -o procls
int main()
PROCTAB* proc = openproc(PROC_FILLCOM | PROC_EDITCMDLCVT);
proc_t proc_info;
memset(&proc_info, 0, sizeof(proc_info));
while (readproc(proc, &proc_info) != NULL)
if(proc_info.cmdline != NULL)
printf("%s\n", *proc_info.cmdline);
closeproc(proc);
【讨论】:
您的示例不显示命令行参数,这是 OP 的目标。在sleep 3600
上测试:您的代码仅打印sleep
。
要打印完整的命令行,应该遍历proc_info.cmdline
数组,直到遇到空指针。每个条目proc_info.cmdline[n]
对应于进程'argv[n]
。
你是对的,它作为数组加载的命令行。添加PROC_EDITCMDLCVT
标志应确保返回单个**char
。以上是关于Linux API 列出正在运行的进程? - 有论据的主要内容,如果未能解决你的问题,请参考以下文章