prctl系统调用及示例

prctl 函数详解

  1. 函数介绍

prctl 是 Linux 系统中用于进程控制的万能系统调用。可以把 prctl 想象成”进程的控制面板”——它允许你对进程的各种属性进行精细控制,就像调节电视遥控器上的各种设置一样。

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

prctl 提供了丰富的进程控制功能,包括设置进程名称、控制进程行为、管理安全属性等。它是 Linux 进程管理的重要工具。

相关文章:prctl系统调用及示例-CSDN博客 set_thread_area系统调用及示例 pwritev2系统调用及示例

  1. 函数原型
1
2
3
4
5
#include <sys/prctl.h>

int prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);

  1. 功能

prctl 函数用于控制进程的各种属性和行为。它是一个多功能的系统调用,支持数十种不同的操作选项。

  1. 主要参数
  • option: 控制选项(详见下表)

  • arg2, arg3, arg4, arg5: 根据选项而定的参数

  1. 常用选项

选项功能参数说明PR_SET_NAME设置进程名称arg2: 指向名称字符串PR_GET_NAME获取进程名称arg2: 指向缓冲区PR_SET_SECCOMP设置安全计算模式seccomp 模式控制PR_GET_SECCOMP获取安全计算模式PR_SET_DUMPABLE设置核心转储权限0=不可转储, 1=可转储PR_GET_DUMPABLE获取核心转储权限PR_SET_KEEPCAPS设置保持权能标志0=不保持, 1=保持PR_GET_KEEPCAPS获取保持权能标志PR_SET_PDEATHSIG设置父进程死亡信号arg2: 信号编号PR_GET_PDEATHSIG获取父进程死亡信号arg2: 指向信号变量

  1. 返回值
  • 成功: 返回 0

  • 失败: 返回 -1,并设置相应的 errno 错误码

  1. 常见错误码
  • EINVAL: 参数无效

  • EPERM: 权限不足

  • EFAULT: 参数指针无效

  • EACCES: 访问被拒绝

  1. 相似函数或关联函数
  • pthread_setname_np: 设置线程名称

  • getpid: 获取进程 ID

  • kill: 发送信号

  • setuid/setgid: 设置用户/组 ID

  • capset: 设置进程权能

  1. 示例代码

示例1:基础用法 - 设置和获取进程名称

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <string.h>
#include <errno.h>

int main() {
char old_name&#91;16];
char new_name&#91;16];
char current_name&#91;16];

printf("=== prctl 进程名称控制示例 ===\n\n");

// 获取当前进程名称
if (prctl(PR_GET_NAME, old_name) == 0) {
old_name&#91;15] = '\0'; // 确保字符串结束
printf("原始进程名称: %s\n", old_name);
} else {
perror("获取进程名称失败");
}

// 设置新的进程名称
snprintf(new_name, sizeof(new_name), "MyProcess_%d", getpid() % 1000);
if (prctl(PR_SET_NAME, new_name) == 0) {
printf("设置新进程名称: %s\n", new_name);
} else {
perror("设置进程名称失败");
}

// 验证名称是否设置成功
if (prctl(PR_GET_NAME, current_name) == 0) {
current_name&#91;15] = '\0';
printf("当前进程名称: %s\n", current_name);
printf("设置%s成功\n",
strcmp(current_name, new_name) == 0 ? "" : "未完全");
}

// 恢复原始名称
if (prctl(PR_SET_NAME, old_name) == 0) {
printf("恢复原始名称: %s\n", old_name);
}

printf("\n查看进程名称的方法:\n");
printf(" ps -ef | grep %d\n", getpid());
printf(" cat /proc/%d/comm\n", getpid());

return 0;
}

示例2:进程安全控制

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

// 父进程死亡信号处理
void parent_death_handler(int sig) {
printf("收到父进程死亡信号 %d\n", sig);
printf("子进程即将退出...\n");
exit(0);
}

int main() {
printf("=== prctl 进程安全控制示例 ===\n\n");

// 设置父进程死亡信号
if (prctl(PR_SET_PDEATHSIG, SIGTERM) == 0) {
printf("✓ 设置父进程死亡信号为 SIGTERM\n");
} else {
printf("✗ 设置父进程死亡信号失败: %s\n", strerror(errno));
}

// 设置核心转储权限
int old_dumpable = -1;
if (prctl(PR_GET_DUMPABLE, &old_dumpable) == 0) {
printf("当前核心转储权限: %s\n",
old_dumpable ? "允许" : "禁止");
}

// 禁止核心转储
if (prctl(PR_SET_DUMPABLE, 0) == 0) {
printf("✓ 禁止核心转储\n");

// 验证设置
int new_dumpable = -1;
if (prctl(PR_GET_DUMPABLE, &new_dumpable) == 0) {
printf("验证: 核心转储权限现在是 %s\n",
new_dumpable ? "允许" : "禁止");
}
}

// 恢复核心转储权限
if (old_dumpable != -1) {
prctl(PR_SET_DUMPABLE, old_dumpable);
}

printf("\n安全控制说明:\n");
printf("1. PR_SET_PDEATHSIG: 父进程死亡时发送信号\n");
printf("2. PR_SET_DUMPABLE: 控制核心转储权限\n");
printf("3. 提高进程安全性,防止敏感信息泄露\n");

return 0;
}

示例3:完整的进程控制工具

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>

// 显示帮助信息
void show_help(const char *program_name) {
printf("用法: %s &#91;选项]\n", program_name);
printf("\n选项:\n");
printf(" -n, --name=NAME 设置进程名称\n");
printf(" -s, --show 显示当前进程信息\n");
printf(" -d, --dumpable=0|1 设置核心转储权限\n");
printf(" -p, --pdeathsig=SIG 设置父进程死亡信号\n");
printf(" -h, --help 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -n MyNewName # 设置进程名称\n", program_name);
printf(" %s -s # 显示进程信息\n", program_name);
printf(" %s -d 0 # 禁止核心转储\n", program_name);
printf(" %s -p 15 # 设置父进程死亡信号为 SIGTERM\n", program_name);
}

// 显示当前进程信息
void show_process_info() {
char name&#91;16];

printf("=== 当前进程信息 ===\n");
printf("进程 ID: %d\n", getpid());
printf("父进程 ID: %d\n", getppid());
printf("用户 ID: %d\n", getuid());
printf("组 ID: %d\n", getgid());

// 获取进程名称
if (prctl(PR_GET_NAME, name) == 0) {
name&#91;15] = '\0';
printf("进程名称: %s\n", name);
}

// 获取核心转储权限
int dumpable = -1;
if (prctl(PR_GET_DUMPABLE, &dumpable) == 0) {
printf("核心转储: %s\n", dumpable ? "允许" : "禁止");
}

// 获取父进程死亡信号
int pdeathsig = -1;
if (prctl(PR_GET_PDEATHSIG, &pdeathsig) == 0) {
if (pdeathsig > 0) {
printf("父进程死亡信号: %d\n", pdeathsig);
} else {
printf("父进程死亡信号: 无\n");
}
}
}

int main(int argc, char *argv&#91;]) {
char *new_name = NULL;
int dumpable = -1;
int pdeathsig = -1;
int show_info = 0;

printf("=== prctl 进程控制工具 ===\n\n");

// 解析命令行参数
static struct option long_options&#91;] = {
{"name", required_argument, 0, 'n'},
{"show", no_argument, 0, 's'},
{"dumpable", required_argument, 0, 'd'},
{"pdeathsig", required_argument, 0, 'p'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};

int opt;
while ((opt = getopt_long(argc, argv, "n:sd:p:h", long_options, NULL)) != -1) {
switch (opt) {
case 'n':
new_name = optarg;
break;
case 's':
show_info = 1;
break;
case 'd':
dumpable = atoi(optarg);
break;
case 'p':
pdeathsig = atoi(optarg);
break;
case 'h':
show_help(argv&#91;0]);
return 0;
default:
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv&#91;0]);
return 1;
}
}

// 显示信息
if (show_info || (!new_name && dumpable == -1 && pdeathsig == -1)) {
show_process_info();
printf("\n");
}

// 设置进程名称
if (new_name) {
if (prctl(PR_SET_NAME, new_name) == 0) {
printf("✓ 设置进程名称为: %s\n", new_name);
} else {
printf("✗ 设置进程名称失败: %s\n", strerror(errno));
}
}

// 设置核心转储权限
if (dumpable != -1) {
if (prctl(PR_SET_DUMPABLE, dumpable) == 0) {
printf("✓ %s核心转储\n", dumpable ? "允许" : "禁止");
} else {
printf("✗ 设置核心转储权限失败: %s\n", strerror(errno));
}
}

// 设置父进程死亡信号
if (pdeathsig > 0) {
if (prctl(PR_SET_PDEATHSIG, pdeathsig) == 0) {
printf("✓ 设置父进程死亡信号为: %d\n", pdeathsig);
} else {
printf("✗ 设置父进程死亡信号失败: %s\n", strerror(errno));
}
}

// 显示使用建议
if (new_name || dumpable != -1 || pdeathsig != -1) {
printf("\n=== 使用建议 ===\n");
printf("进程名称控制:\n");
printf(" - 用于进程标识和监控\n");
printf(" - 方便系统管理工具识别\n");
printf(" - 长度限制为 15 个字符\n");
printf("\n安全控制:\n");
printf(" - 禁止核心转储保护敏感信息\n");
printf(" - 设置父进程死亡信号实现优雅退出\n");
printf(" - 提高进程的安全性和可控性\n");
}

return 0;
}

编译和运行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
# 编译示例程序
gcc -o prctl_example1 example1.c
gcc -o prctl_example2 example2.c
gcc -o prctl_example3 example3.c

# 运行示例
./prctl_example1
./prctl_example2
./prctl_example3 --help
./prctl_example3 -s
./prctl_example3 -n TestProcess
./prctl_example3 -d 0

系统要求检查

1
2
3
4
5
6
7
8
9
10
# 检查内核版本(需要 2.1.57+)
uname -r

# 检查 prctl 支持
grep -w prctl /usr/include/asm/unistd_64.h

# 查看进程信息
cat /proc/self/comm
ps -p $$ -o pid,ppid,comm,cmd

重要注意事项

权限要求: 大多数操作不需要特殊权限

名称长度: 进程名称最长 15 个字符(加上终止符共 16 字节)

错误处理: 始终检查返回值和 errno

平台相关: prctl 是 Linux 特有的系统调用

安全性: 合理使用安全控制选项

实际应用场景

进程管理: 设置有意义的进程名称

安全控制: 控制核心转储和敏感信息

容器技术: 容器化进程控制

服务管理: 系统服务进程管理

调试工具: 调试和监控工具使用

常用选项详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 进程名称控制
prctl(PR_SET_NAME, "MyProcess"); // 设置进程名称
prctl(PR_GET_NAME, name_buffer); // 获取进程名称

// 安全控制
prctl(PR_SET_DUMPABLE, 0); // 禁止核心转储
prctl(PR_GET_DUMPABLE, &dumpable); // 获取核心转储权限
prctl(PR_SET_PDEATHSIG, SIGTERM); // 设置父进程死亡信号

// 权能控制
prctl(PR_SET_KEEPCAPS, 1); // 设置保持权能标志
prctl(PR_GET_KEEPCAPS, &keepcaps); // 获取保持权能标志

// 内存保护
prctl(PR_SET_MM, PR_SET_MM_START_BRK, addr); // 设置堆起始地址

最佳实践

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
// 安全的进程名称设置
int safe_set_process_name(const char *name) {
if (!name) {
errno = EINVAL;
return -1;
}

// 验证名称长度
if (strlen(name) >= 16) {
fprintf(stderr, "进程名称过长,最多 15 个字符\n");
errno = EINVAL;
return -1;
}

// 设置进程名称
int result = prctl(PR_SET_NAME, name);
if (result == -1) {
fprintf(stderr, "设置进程名称失败: %s\n", strerror(errno));
}

return result;
}

// 获取进程信息的安全函数
int get_process_info_safe(char *name, int *dumpable, int *pdeathsig) {
int result = 0;

// 获取进程名称
if (name) {
if (prctl(PR_GET_NAME, name) == -1) {
result = -1;
} else {
name&#91;15] = '\0'; // 确保字符串结束
}
}

// 获取核心转储权限
if (dumpable) {
if (prctl(PR_GET_DUMPABLE, dumpable) == -1) {
result = -1;
}
}

// 获取父进程死亡信号
if (pdeathsig) {
if (prctl(PR_GET_PDEATHSIG, pdeathsig) == -1) {
result = -1;
}
}

return result;
}

这些示例展示了 prctl 函数的各种使用方法,从基础的进程名称设置到完整的进程控制工具,帮助你全面掌握 Linux 系统中的进程控制机制。

data-ad-format="auto" data-full-width-responsive="true">