Linux C: Program Name and Program ID

准备

在Linux上查询程序的pid,可以通过如下方式:

  • ps命令

    1
    2
    3
    4
    pi@localhost:~/study$ ps -ef | grep bluealsa
    root 1017 1 0 19:01 ? 00:00:00 bluealsa --profile=a2dp-source
    pi 2852 2393 50 19:35 pts/0 00:00:00 grep --color=auto bluealsa

  • pidof命令

    1
    2
    pi@localhost:~/study$ pidof bluealsa
    1017

那么是否可以通过C代码封装成接口,方便直接在代码中调用呢?可以

首先,Linux所有进程状态信息都存储在这个目录下:/proc ,每一个数字文件夹代表一个进程。

image-20241028195503492

进程文件夹下有status文件,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
pi@localhost:~/study$ cat /proc/1017/status 
Name: bluealsa
Umask: 0022
State: S (sleeping)
Tgid: 1017
Ngid: 0
Pid: 1017
PPid: 1
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 128
Groups:
NStgid: 1017
NSpid: 1017
NSpgid: 1017
NSsid: 1017
Kthread: 0
VmPeak: 231224 kB
VmSize: 231224 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 5504 kB
VmRSS: 5504 kB
RssAnon: 384 kB
RssFile: 5120 kB
RssShmem: 0 kB
VmData: 25416 kB
VmStk: 132 kB
VmExe: 160 kB
VmLib: 7720 kB
VmPTE: 84 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
THP_enabled: 1
untag_mask: 0xffffffffffffff
Threads: 4
SigQ: 1/28918
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000100004002
CapInh: 0000000000000000
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Seccomp_filters: 0
Speculation_Store_Bypass: vulnerable
SpeculationIndirectBranch: unknown
Cpus_allowed: f
Cpus_allowed_list: 0-3
Mems_allowed: 1
Mems_allowed_list: 0
voluntary_ctxt_switches: 26
nonvoluntary_ctxt_switches: 8

实现

通过 /proc/<pid_dir>/status 文件内的Name字段可以获取程序名,Pid字段可以获取程序ID。逐步遍历所有这样的目标文件,将输入的程序名和Name字段的值进行比较,最后就可以得出程序的pid。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* gcc pid_get.c -Wall -o pid_get */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>

pid_t get_pid_by_name(char *task_name)
{
pid_t pid = 0;
DIR *dir = NULL;
struct dirent *ptr = NULL;
FILE *fp = NULL;
char filepath[512] = {0};
char cur_task_name[50] = {0};
char buf[1024] = {0};

if (NULL == task_name)
return pid;
dir = opendir("/proc");
if (NULL != dir)
{
while ((ptr = readdir(dir)) != NULL) //循环读取/proc下的每一个文件/文件夹
{
//如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0))
continue;
// 如果目标不是目录,则跳过
if (DT_DIR != ptr->d_type)
continue;
sprintf(filepath, "/proc/%s/status", ptr->d_name);//生成要读取的文件的路径
fp = fopen(filepath, "r");
// 逐行读取,并进行比较
if (NULL != fp)
{
if (NULL == fgets(buf, sizeof(buf) - 1, fp))
{
fclose(fp);
continue;
}
sscanf(buf, "%*s %s", cur_task_name);
//如果文件内容满足要求则打印路径的名字(即进程的PID)
if (!strcmp(task_name, cur_task_name))
{
sscanf(ptr->d_name, "%d", &pid);
fclose(fp);
break;
}
fclose(fp);
}
}
closedir(dir);
}
return pid;
}

int main(int argc, char *argv[])
{
if (2 != argc)
{
printf("too small params\n"
"%s <program_name>\n", argv[0]);
return 1;
}

char task_name[256] = {0};
pid_t pid = 0;
sprintf(task_name, "%s", argv[1]);
pid = get_pid_by_name(task_name);
printf("Name: %s\n"
"Pid: %d\n", task_name, pid);

return 0;
}

编译并运行程序:

1
2
3
4
5
pi@localhost:~/study$ gcc pid_get.c -Wall -o pid_get
pi@localhost:~/study$ ./pid_get bluealsa
Name: bluealsa
Pid: 1017

通过ps命令、pidof命令查询出来的bluealsa进程的pid是1017,而通过上述程序得出的bluealsa的pid也是1017,所以一切正常。