grep输出显示文件名的方法总结

grep输出显示文件名的方法总结,使用grep查找时,如何在输出中显示文件名?详解grep显示文件名的方法。在不同场景下的文件名添加方法;单文件、多文件、目录等场景;

grep 可以在输出的匹配行前加上文件名。这是 grep 的默认行为之一,具体取决于使用方式:

当搜索多个文件时,grep 会自动在匹配行前加上文件名:# 搜索多个文件时,默认显示文件名 grep “pattern” file1.txt file2.txt file3.txt 输出会类似:file1.txt:这是包含pattern的行 file2.txt:这是另一个包含pattern的行

当只搜索单个文件时,默认不显示文件名,可通过 -H 选项强制显示:# 单个文件搜索时,强制显示文件名 grep -H “pattern” file.txt 输出会显示:file.txt:这是包含pattern的行

当搜索目录下所有文件时(结合 -r 递归选项),也会自动显示文件名:# 递归搜索目录,显示文件名 grep -r “pattern” ./directory/

相关常用选项:

  • -H:总是显示文件名(即使只搜索一个文件)

  • -h:从不显示文件名(即使搜索多个文件)

  • -n:同时显示行号(可与文件名一起显示)

示例(同时显示文件名和行号):

1
2
grep -Hn "error" /var/log/*.log

输出会类似:

1
2
/var/log/syslog:123:2023-08-11 10:00:00 [error] 系统错误信息
/var/log/auth.log:45:2023-08-11 10:05:00 [error

sed 初级学习手册

sed 初级学习手册(修订版)

一、认识 sed

什么是 sed

  • 全称:Stream Editor(流编辑器)

  • 功能:非交互式命令行文本编辑器,用于对输入流进行文本转换和编辑

  • 特点:批量处理、支持正则表达式、强大的文本处理能力

安装与验证# 检查是否安装 sed –version # 安装命令 # Ubuntu/Debian: apt install sed # CentOS/RHEL: yum install sed # 基本测试 echo “Hello World” | sed ‘s/World/sed/‘

基本概念

  • 地址:指定要处理的行

  • 命令:指定要执行的操作

  • 脚本:命令的组合

  • 模式空间:sed内部用于存储当前处理行的缓冲区

二、基本语法与选项

1. 基本用法格式

1
2
3
4
5
6
7
8
9
10
11
12
# 格式1:直接指定脚本
sed [选项] '脚本' 文件名...

# 格式2:从文件读取脚本
sed [选项] -f 脚本文件 文件名...

# 格式3:管道使用
命令 | sed [选项] '脚本'

# 格式4:标准输入
sed [选项] '脚本'

2. 常用选项详解(基于 sed –help)

选项长选项说明-n–quiet, –silent抑制默认输出(只输出明确指定的内容)-e–expression=脚本添加脚本到命令中(可多次使用)-f–file=脚本文件从文件读取脚本-i–in-place[=后缀]原地编辑文件(提供后缀则备份原文件)-r–regexp-extended使用扩展正则表达式(同-E)-E–regexp-extended使用扩展正则表达式(推荐使用)-s–separate将多个文件分别处理,而非作为连续流-u–unbuffered减少I/O缓冲,及时刷新输出-z–null-data使用NUL字符作为行分隔符-b–binary二进制模式(Windows兼容)–posix禁用所有GNU扩展,使用POSIX兼容模式–sandbox沙盒模式(禁用e/r/w命令)–follow-symlinks原地编辑时跟随符号链接-c–copy使用复制而非重命名方式处理文件(-i模式)-l–line-length=N指定’l’命令的行包装长度

三、基本命令详解

1. 替换命令 (s)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 基本替换语法:s/原字符串/新字符串/标志
sed 's/old/new/' file.txt # 只替换每行第一次出现
sed 's/old/new/g' file.txt # 全局替换(所有出现)
sed 's/old/new/i' file.txt # 忽略大小写替换
sed 's/old/new/p' file.txt # 替换并打印匹配行
sed 's/old/new/gp' file.txt # 全局替换并打印匹配行
sed 's/old/new/2' file.txt # 替换每行第2次出现
sed 's/old/new/gw output.txt' file.txt # 替换结果写入指定文件

# 使用不同分隔符(避免转义斜杠)
sed 's|/old/path|/new/path|' file.txt
sed 's_old_new_' file.txt
sed 's#http://#https://#' file.txt

2. 删除命令 (d)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 删除指定行
sed '3d' file.txt # 删除第3行
sed '3,5d' file.txt # 删除第3到第5行
sed '3,$d' file.txt # 删除第3行到文件末尾
sed '1~2d' file.txt # 删除奇数行
sed '2~2d' file.txt # 删除偶数行

# 删除匹配模式的行
sed '/pattern/d' file.txt # 删除包含pattern的行
sed '/^$/d' file.txt # 删除空行
sed '/^[[:space:]]*$/d' file.txt # 删除空白行
sed '/^#/d' file.txt # 删除注释行

3. 打印命令 §

1
2
3
4
5
6
7
# 需要配合-n选项使用
sed -n '3p' file.txt # 打印第3行
sed -n '3,5p' file.txt # 打印第3到第5行
sed -n '/pattern/p' file.txt # 打印包含pattern的行
sed -n '1~2p' file.txt # 打印奇数行
sed -n '2~2p' file.txt # 打印偶数行

4. 插入命令 (i, a, c)

1
2
3
4
5
6
7
8
9
10
11
12
# 插入命令
sed '3i\新行内容' file.txt # 在第3行前插入
sed '/pattern/i\新行内容' file.txt # 在匹配行前插入

# 追加命令
sed '3a\新行内容' file.txt # 在第3行后追加
sed '/pattern/a\新行内容' file.txt # 在匹配行后追加

# 替换命令
sed '3c\新行内容' file.txt # 替换第3行
sed '/pattern/c\新行内容' file.txt # 替换匹配行

5. 其他常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 行号显示 (=)
sed '=' file.txt # 显示所有行号
sed '/pattern/=' file.txt # 显示匹配行的行号

# 列出行内容 (l)
sed -n 'l' file.txt # 显示所有行(转义特殊字符)
sed -n '/pattern/l' file.txt # 显示匹配行(转义特殊字符)

# 读取文件 (r)
sed '/pattern/r insert.txt' file.txt # 在匹配行后读入文件内容

# 写入文件 (w)
sed -n '/pattern/w output.txt' file.txt # 将匹配行写入文件

# 退出命令 (q)
sed '10q' file.txt # 处理10行后退出
sed '/pattern/q' file.txt # 匹配到pattern后退出

四、地址范围

1. 行号地址

1
2
3
4
5
6
7
8
9
10
11
12
# 单行地址
sed '5命令' file.txt # 处理第5行

# 行号范围
sed '5,10命令' file.txt # 处理第5到第10行
sed '5,$命令' file.txt # 处理第5行到文件末尾
sed '1,3!命令' file.txt # 处理除第1到第3行外的所有行

# 步进地址
sed '1~2命令' file.txt # 处理奇数行(1,3,5...)
sed '2~2命令' file.txt # 处理偶数行(2,4,6...)

2. 模式地址

1
2
3
4
5
6
7
8
9
10
11
# 单模式地址
sed '/pattern/命令' file.txt # 处理包含pattern的行

# 模式范围
sed '/start/,/end/命令' file.txt # 处理从包含start到包含end的行
sed '/pattern/,+3命令' file.txt # 处理包含pattern的行及其后3行
sed '/pattern/,$命令' file.txt # 处理包含pattern的行到文件末尾

# 模式与行号组合
sed '/pattern/,10命令' file.txt # 处理包含pattern的行到第10行

3. 地址否定

1
2
3
4
# 否定地址(处理不匹配的行)
sed '3!命令' file.txt # 处理除第3行外的所有行
sed '/pattern/!命令' file.txt # 处理不包含pattern的行

五、正则表达式详解

1. 基本正则表达式(默认)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 字符匹配
sed 's/a/b/' file.txt # 匹配字符a
sed 's/./b/' file.txt # 匹配任意字符
sed 's/[abc]/X/' file.txt # 匹配a、b或c
sed 's/[^abc]/X/' file.txt # 匹配除a、b、c外的字符

# 重复匹配
sed 's/a*/X/' file.txt # 匹配0个或多个a
sed 's/a\+/X/' file.txt # 匹配1个或多个a(需要转义)
sed 's/a\{2,4\}/X/' file.txt # 匹配2到4个a(需要转义)

# 位置匹配
sed 's/^/X/' file.txt # 匹配行首
sed 's/$/X/' file.txt # 匹配行尾
sed 's/\<word\>/X/' file.txt # 匹配单词边界

2. 扩展正则表达式(-E选项)

1
2
3
4
5
6
7
# 使用-E选项启用扩展正则表达式
sed -E 's/a+/X/' file.txt # 匹配1个或多个a
sed -E 's/a{2,4}/X/' file.txt # 匹配2到4个a
sed -E 's/(group)+/X/' file.txt # 分组匹配
sed -E 's/pattern1|pattern2/X/' file.txt # OR操作
sed -E 's/(..)(..)/\2\1/' file.txt # 捕获组交换

3. 捕获组和引用

1
2
3
4
5
6
7
8
# 基本正则中的捕获组
sed 's/\(pattern\)/&#91;\1]/' file.txt # 使用\1引用第一个捕获组
sed 's/\(first\)\(second\)/\2\1/' file.txt # 交换两个捕获组

# 扩展正则中的捕获组
sed -E 's/(pattern)/&#91;\1]/' file.txt # 使用\1引用第一个捕获组
sed -E 's/(first)(second)/\2\1/' file.txt # 交换两个捕获组

六、实用示例

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
# 简单替换
sed 's/old/new/' file.txt

# 全局替换
sed 's/old/new/g' file.txt

# 忽略大小写替换
sed 's/old/new/gi' file.txt

# 使用捕获组
sed 's/\(pattern\)/&#91;\1]/' file.txt

# 删除行首空白
sed 's/^&#91; \t]*//' file.txt

# 删除行尾空白
sed 's/&#91; \t]*$//' file.txt

# 替换多个空格为单个空格
sed 's/ */ /g' file.txt

# 删除所有空白字符
sed 's/&#91;&#91;:space:]]//g' file.txt

2. 文件处理

1
2
3
4
5
6
7
8
9
10
11
12
# 原地编辑文件
sed -i 's/old/new/g' file.txt

# 原地编辑并备份
sed -i.bak 's/old/new/g' file.txt

# 处理多个文件
sed 's/old/new/g' file1.txt file2.txt

# 从标准输入处理
cat file.txt | sed 's/old/new/g'

3. 复杂操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 多命令组合(使用分号)
sed 's/old/new/g; s/abc/xyz/g' file.txt

# 多命令组合(使用-e选项)
sed -e 's/old/new/g' -e 's/abc/xyz/g' file.txt

# 使用脚本文件
echo -e 's/old/new/g\ns/abc/xyz/g' > script.sed
sed -f script.sed file.txt

# 条件处理
sed '/pattern/{s/old/new/;p}' file.txt

# 多行处理
sed '/start/,/end/{s/old/new/g}' file.txt

4. 实际应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
# 配置文件处理
sed 's/#.*$//' file.conf | sed '/^$/d' # 删除注释和空行

# 日志处理
sed -n '/ERROR/p' application.log # 提取错误日志
sed '/DEBUG/d' application.log # 过滤调试日志

# CSV处理
sed 's/,/|/g' data.csv # 替换分隔符

# 格式转换
sed 's/\t/ /g' file.txt # Tab转空格

七、高级功能

1. 保持空间操作

1
2
3
4
5
6
7
8
9
10
11
12
# h - 将模式空间复制到保持空间
# H - 将模式空间追加到保持空间
# g - 将保持空间复制到模式空间
# G - 将保持空间追加到模式空间
# x - 交换模式空间和保持空间

# 示例:在文件末尾添加文件头
sed '1h; 1d; $G' file.txt

# 示例:反转文件行顺序
sed -n '1!G; h; $p' file.txt

2. 分支和测试命令

1
2
3
4
5
6
7
# b - 分支到标签(无标签则跳到脚本末尾)
# t - 测试(如果上次替换成功则分支)
# T - 测试(如果上次替换失败则分支)

# 示例:替换成功后退出
sed '/pattern/{s/old/new/; t; d}' file.txt

3. 原地编辑高级用法

1
2
3
4
5
6
7
8
9
# 创建带时间戳的备份
sed -i.$(date +%Y%m%d) 's/old/new/g' file.txt

# 处理符号链接
sed --follow-symlinks -i 's/old/new/g' symlink.txt

# 使用复制模式
sed -c -i 's/old/new/g' file.txt

八、常见问题解决

1. 特殊字符处理

1
2
3
4
5
6
7
8
9
# 处理包含斜杠的路径
sed 's|/old/path|/new/path|' file.txt
sed 's#/old/path#/new/path#' file.txt

# 处理特殊字符
sed 's/\*/STAR/g' file.txt # 替换星号
sed 's/\$/DOLLAR/g' file.txt # 替换美元符号
sed 's/\\/BACKSLASH/g' file.txt # 替换反斜杠

2. 性能优化

1
2
3
4
5
6
7
8
9
# 对大文件只处理前N行
sed '1000q; s/old/new/g' largefile.txt

# 使用地址范围限制处理范围
sed '100,200s/old/new/g' file.txt

# 避免不必要的全局替换
sed 's/old/new/' file.txt # 只替换第一次出现

3. 跨平台兼容性

1
2
3
4
5
6
7
8
9
10
# 使用POSIX模式
sed --posix 's/old/new/g' file.txt

# Windows兼容模式
sed -b 's/old/new/g' file.txt

# 使用扩展正则表达式的可移植性
sed -E 's/pattern1|pattern2/replacement/' file.txt # GNU/Linux
sed -r 's/pattern1|pattern2/replacement/' file.txt # 旧版本GNU sed

九、快速参考表

基本命令速查

1
2
3
4
5
6
7
8
9
10
11
s/old/new/    替换(第一次出现)
s/old/new/g 全局替换
d 删除行
p 打印行(需-n选项)
i\text 在前插入
a\text 在后追加
c\text 替换行
= 显示行号
l 列出行内容
q 退出

地址范围速查

1
2
3
4
5
6
7
8
5             第5行
5,10 第5到第10行
5,$ 第5行到末尾
/pattern/ 包含pattern的行
/pattern/,/end/ 从pattern到end的行
1~2 奇数行
2~2 偶数行

常用正则表达式

1
2
3
4
5
6
7
8
9
10
11
^             行首
$ 行尾
. 任意字符
* 前一字符0次或多次
\+ 前一字符1次或多次
\{n,m\} 前一字符n到m次
&#91;abc] 字符类
&#91;^abc] 非字符类
\(abc\) 捕获组(基本正则)
(abc) 捕获组(扩展正则)

实用组合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 删除空行和注释行
sed '/^$/d; /^#/d' file.txt

# 提取文件头和尾各5行
sed -n '1,5p; $-4,$p' file.txt

# 给文件添加行号
sed '=' file.txt | sed 'N; s/\n/\t/'

# 合并连续的空行为单个空行
sed '/^$/N; /\n$/D' file.txt

# 在每个匹配行后添加标记
sed '/pattern/a\--- MATCH ---' file.txt

退出状态码

1
2
3
4
5
0   成功
1 无效命令或语法错误
2 无法访问文件或I/O错误
4 GNU sed内部错误

这个修订版手册基于sed –help的完整输出,涵盖了所有主要选项和功能。掌握这些内容后,你可以高效地进行各种文本处理工作。

如何用taskset绑定CPU核心以提升性能

如何用taskset绑定CPU核心以提升性能

相关文章:​​taskset 命令CPU 核绑定

使用taskset绑定CPU核心可显著提升性能,优化系统资源分配,提高程序运行效率。要将shell命令绑定到特定CPU核心运行,可以使用taskset命令。taskset允许你设置或检索进程的CPU亲和性,即限制进程只能在指定的CPU核心上运行。

使用方法如下:

直接运行命令并绑定到特定CPU:

1
2
3
4
5
6
7
8
9
10
11
12
# 将命令绑定到CPU 0运行(CPU编号从0开始)
taskset -c 0 你的命令

# 例如,将ls命令绑定到CPU 1运行
taskset -c 1 ls

# 将命令绑定到多个CPU,比如CPU 0和1
taskset -c 0,1 你的命令

# 将命令绑定到一个CPU范围,比如CPU 0到3
taskset -c 0-3 你的命令

为已运行的进程设置CPU亲和性:

1
2
3
4
5
6
# 首先找到进程ID(PID)
ps -ef | grep 进程名

# 然后将进程绑定到特定CPU
taskset -c 0 -p 进程ID

示例:将ping命令绑定到CPU 2运行:

1
2
taskset -c 2 ping www.example.com

这在需要控制进程资源使用、进行性能测试或避免特定进程之间资源竞争时非常有用。不同CPU核心之间可能存在性能差异,合理的CPU绑定可以优化系统整体性能。

fadvise64系统调用及示例

fadvise64 - 文件访问建议

函数介绍

fadvise64是一个Linux系统调用,用于向内核提供关于文件访问模式的建议。它帮助内核优化文件I/O操作,提高性能。fadvise64系统调用详解,提供文件访问建议函数示例与使用方法,优化Linux性能必备。

关键词:fadvise64 系统调用, fadvise64 函数用法, linux fadvise64 详解, fadvise64 示例代码, linux 系统调用 fadvise64, fadvise64 文件访问建议, 如何使用 fadvise64, fadvise64 系统调用介绍, linux fadvise64 函数说明, fadvise64 作用与用法

函数原型

1
2
3
4
5
6
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>

int fadvise64(int fd, off_t offset, off_t len, int advice);

功能

向内核提供文件访问模式建议,帮助内核优化缓存和预读策略。

参数

  • int fd: 文件描述符

  • off_t offset: 建议适用的文件起始偏移量

  • off_t len: 建议适用的文件长度(0表示到文件末尾)

int advice: 访问建议类型

  • POSIX_FADV_NORMAL: 普通访问模式(默认)

  • POSIX_FADV_SEQUENTIAL: 顺序访问

  • POSIX_FADV_RANDOM: 随机访问

  • POSIX_FADV_NOREUSE: 数据只访问一次

  • POSIX_FADV_WILLNEED: 数据即将被访问

  • POSIX_FADV_DONTNEED: 数据不再需要

返回值

  • 成功时返回0

  • 失败时返回-1,并设置errno

特殊限制

  • 需要Linux 2.5.60以上内核支持

  • 某些文件系统可能不完全支持

  • 建议只是提示,内核可能忽略

相似函数

  • madvise(): 内存访问建议

  • readahead(): 文件预读

  • posix_fadvise(): POSIX标准版本

示例代码

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <sys/syscall.h>

// 系统调用包装
static int fadvise64_wrapper(int fd, off_t offset, off_t len, int advice) {
return syscall(__NR_fadvise64, fd, offset, len, advice);
}

// 创建测试文件
int create_test_file(const char* filename, size_t size) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}

// 写入测试数据
char* buffer = malloc(4096);
if (buffer) {
memset(buffer, 'A', 4096);
for (size_t i = 0; i < size; i += 4096) {
size_t write_size = (size - i > 4096) ? 4096 : (size - i);
write(fd, buffer, write_size);
}
free(buffer);
}

return fd;
}

int main() {
int fd;
int result;

printf("=== Fadvise64 函数示例 ===\n");

// 示例1: 基本使用
printf("\n示例1: 基本使用\n");

// 创建大文件用于测试
fd = create_test_file("test_fadvise64.dat", 1024 * 1024); // 1MB
if (fd == -1) {
exit(EXIT_FAILURE);
}
printf("创建测试文件: test_fadvise64.dat (1MB)\n");

close(fd);

// 重新打开文件进行测试
fd = open("test_fadvise64.dat", O_RDONLY);
if (fd == -1) {
perror("打开测试文件失败");
unlink("test_fadvise64.dat");
exit(EXIT_FAILURE);
}
printf("打开测试文件进行fadvise64测试\n");

// 示例2: 不同的访问建议
printf("\n示例2: 不同的访问建议\n");

// POSIX_FADV_NORMAL - 普通访问模式
result = fadvise64_wrapper(fd, 0, 0, POSIX_FADV_NORMAL);
if (result == 0) {
printf("设置POSIX_FADV_NORMAL成功\n");
} else {
printf("设置POSIX_FADV_NORMAL失败: %s\n", strerror(errno));
}

// POSIX_FADV_SEQUENTIAL - 顺序访问
result = fadvise64_wrapper(fd, 0, 1024*1024, POSIX_FADV_SEQUENTIAL);
if (result == 0) {
printf("设置POSIX_FADV_SEQUENTIAL成功\n");
printf("提示内核将进行顺序访问,优化预读策略\n");
}

// POSIX_FADV_RANDOM - 随机访问
result = fadvise64_wrapper(fd, 0, 1024*1024, POSIX_FADV_RANDOM);
if (result == 0) {
printf("设置POSIX_FADV_RANDOM成功\n");
printf("提示内核将进行随机访问,减少预读\n");
}

// POSIX_FADV_WILLNEED - 数据即将被访问
result = fadvise64_wrapper(fd, 0, 64*1024, POSIX_FADV_WILLNEED);
if (result == 0) {
printf("设置POSIX_FADV_WILLNEED成功\n");
printf("提示内核预读前64KB数据\n");
}

// POSIX_FADV_DONTNEED - 数据不再需要
result = fadvise64_wrapper(fd, 0, 64*1024, POSIX_FADV_DONTNEED);
if (result == 0) {
printf("设置POSIX_FADV_DONTNEED成功\n");
printf("提示内核可以丢弃前64KB数据的缓存\n");
}

// POSIX_FADV_NOREUSE - 数据只访问一次
result = fadvise64_wrapper(fd, 64*1024, 64*1024, POSIX_FADV_NOREUSE);
if (result == 0) {
printf("设置POSIX_FADV_NOREUSE成功\n");
printf("提示内核64KB-128KB范围的数据只访问一次\n");
}

// 示例3: 错误处理演示
printf("\n示例3: 错误处理演示\n");

// 使用无效的文件描述符
result = fadvise64_wrapper(999, 0, 1024, POSIX_FADV_NORMAL);
if (result == -1) {
if (errno == EBADF) {
printf("无效文件描述符错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的建议类型
result = fadvise64_wrapper(fd, 0, 1024, 999);
if (result == -1) {
if (errno == EINVAL) {
printf("无效建议类型错误处理正确: %s\n", strerror(errno));
}
}

// 使用负的偏移量
result = fadvise64_wrapper(fd, -1024, 1024, POSIX_FADV_NORMAL);
if (result == -1) {
printf("负偏移量处理: %s\n", strerror(errno));
}

// 示例4: 实际使用场景演示
printf("\n示例4: 实际使用场景演示\n");

// 场景1: 大文件顺序读取
printf("场景1: 大文件顺序读取优化\n");
printf("处理大日志文件的代码示例:\n");
printf("int process_large_log(const char* filename) {\n");
printf(" int fd = open(filename, O_RDONLY);\n");
printf(" if (fd == -1) return -1;\n");
printf(" \n");
printf(" // 提示内核将顺序访问整个文件\n");
printf(" posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);\n");
printf(" \n");
printf(" // 读取处理文件...\n");
printf(" char buffer&#91;8192];\n");
printf(" ssize_t bytes;\n");
printf(" while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) {\n");
printf(" // 处理数据\n");
printf(" }\n");
printf(" \n");
printf(" close(fd);\n");
printf(" return 0;\n");
printf("}\n\n");

// 场景2: 随机访问数据库文件
printf("场景2: 随机访问数据库文件\n");
printf("数据库文件访问优化:\n");
printf("int access_database_file(const char* filename) {\n");
printf(" int fd = open(filename, O_RDWR);\n");
printf(" if (fd == -1) return -1;\n");
printf(" \n");
printf(" // 提示内核将随机访问文件\n");
printf(" posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);\n");
printf(" \n");
printf(" // 根据需要预读特定区域\n");
printf(" posix_fadvise(fd, index_offset, index_size, POSIX_FADV_WILLNEED);\n");
printf(" \n");
printf(" // 访问完成后释放不需要的缓存\n");
printf(" posix_fadvise(fd, old_data_offset, old_data_size, POSIX_FADV_DONTNEED);\n");
printf(" \n");
printf(" close(fd);\n");
printf(" return 0;\n");
printf("}\n\n");

// 示例5: 不同建议类型的效果说明
printf("示例5: 不同建议类型的效果说明\n");

printf("POSIX_FADV_NORMAL:\n");
printf(" - 默认访问模式\n");
printf(" - 使用系统默认的预读和缓存策略\n");
printf(" - 适用于一般情况\n\n");

printf("POSIX_FADV_SEQUENTIAL:\n");
printf(" - 优化顺序访问\n");
printf(" - 增加预读量\n");
printf(" - 适用于大文件顺序读取\n");
printf(" - 提高顺序读取性能\n\n");

printf("POSIX_FADV_RANDOM:\n");
printf(" - 优化随机访问\n");
printf(" - 减少或禁用预读\n");
printf(" - 适用于数据库、索引文件\n");
printf(" - 减少不必要的内存占用\n\n");

printf("POSIX_FADV_NOREUSE:\n");
printf(" - 数据只访问一次\n");
printf(" - 访问后尽快释放缓存\n");
printf(" - 适用于一次性处理的大文件\n");
printf(" - 节省内存资源\n\n");

printf("POSIX_FADV_WILLNEED:\n");
printf(" - 数据即将被访问\n");
printf(" - 提前预读数据到缓存\n");
printf(" - 适用于已知访问模式的场景\n");
printf(" - 减少实际访问时的等待\n\n");

printf("POSIX_FADV_DONTNEED:\n");
printf(" - 数据不再需要\n");
printf(" - 尽快释放缓存空间\n");
printf(" - 适用于处理完成的数据\n");
printf(" - 释放系统资源\n\n");

// 示例6: 性能测试演示
printf("示例6: 性能影响演示\n");

printf("fadvise64对性能的影响:\n");
printf("1. 正确使用可显著提高I/O性能\n");
printf("2. 错误使用可能导致性能下降\n");
printf("3. 效果因文件系统和硬件而异\n");
printf("4. 大文件效果更明显\n");
printf("5. 需要根据实际访问模式选择\n\n");

// 示例7: 实际应用建议
printf("示例7: 实际应用建议\n");

printf("使用fadvise64的最佳实践:\n");
printf("1. 在文件打开后尽早设置建议\n");
printf("2. 根据实际访问模式选择合适的建议\n");
printf("3. 对于大文件效果更明显\n");
printf("4. 不要过度使用,避免增加系统负担\n");
printf("5. 在长时间运行的应用中适时调整\n");
printf("6. 测试不同建议对性能的影响\n\n");

printf("常见应用场景:\n");
printf("- 大文件处理和分析\n");
printf("- 数据库系统\n");
printf("- 日志处理系统\n");
printf("- 备份和归档工具\n");
printf("- 媒体播放器\n");
printf("- 科学计算应用\n\n");

// 示例8: 与相关函数的对比
printf("示例8: 与相关函数的对比\n");

printf("fadvise64 vs madvise:\n");
printf("fadvise64:\n");
printf(" - 针对文件I/O\n");
printf(" - 影响文件缓存策略\n");
printf(" - 在文件描述符上操作\n\n");

printf("madvise:\n");
printf(" - 针对内存映射\n");
printf(" - 影响内存管理策略\n");
printf(" - 在内存地址上操作\n\n");

printf("fadvise64 vs readahead:\n");
printf("fadvise64:\n");
printf(" - 更通用的建议机制\n");
printf(" - 支持多种访问模式\n");
printf(" - 可以指定文件区域\n\n");

printf("readahead:\n");
printf(" - 专门用于预读\n");
printf(" - 立即执行预读操作\n");
printf(" - 较为直接但不够灵活\n\n");

// 清理资源
close(fd);
unlink("test_fadvise64.dat");

printf("总结:\n");
printf("fadvise64是Linux提供的文件访问优化机制\n");
printf("通过向内核提供访问建议来优化性能\n");
printf("支持多种访问模式的优化\n");
printf("是处理大文件和特定访问模式的重要工具\n");
printf("需要根据实际应用场景合理使用\n");

return 0;
}

https://www.calcguide.tech/2025/08/11/fadvise64系统调用及示例-3/

fchown系统调用及示例

fchown - 修改文件所有者(通过文件描述符)

fchown系统调用及示例:通过文件描述符修改文件所有者,包含使用示例与参数说明。fchown 系统调用, fchown 函数用法, 修改文件所有者系统调用, fchown 示例代码, Linux fchown 命令, 系统调用 fchown 详解, 如何使用 fchown, fchown 和 chown 区别, Linux 文件权限修改, 系统调用函数 fchown

1. 函数介绍

fchown 是一个 Linux 系统调用,用于修改已打开文件的所有者(owner)和所属组(group)。与 chown 不同,fchown 通过文件描述符而不是文件路径来指定要修改的文件,这样可以避免在多线程环境中因文件重命名或删除而导致的竞态条件。

你可以把它想象成通过”门把手”而不是”地址”来找到房子并更换房主,这样即使房子的地址变了,你仍然可以通过门把手找到它。

https://blog.csdn.net/timberwolf007/article/details/150869147?spm=1011.2415.3001.5331

2. 函数原型

1
2
3
4
#include <unistd.h>

int fchown(int fd, uid_t owner, gid_t group);

3. 功能

修改通过文件描述符指定的文件的所有者和所属组。如果只想修改所有者或所属组中的一个,可以将另一个参数设置为 -1。

4. 参数

  • int fd: 文件描述符,通过 open() 等函数获得

uid_t owner: 新的所有者用户 ID

  • 有效的用户 ID:设置为指定用户所有

  • (uid_t)-1:保持当前所有者不变

gid_t group: 新的所属组 ID

  • 有效的组 ID:设置为指定组所有

  • (gid_t)-1:保持当前所属组不变

5. 返回值

  • 成功时返回 0

  • 失败时返回 -1,并设置 errno

6. 相似函数,或关联函数

  • chown(): 通过文件路径修改文件所有者

  • fchownat(): 相对于目录文件描述符修改文件所有者

  • lchown(): 修改符号链接本身的所有者(而不是链接指向的文件)

  • getuid(): 获取当前用户 ID

  • getgid(): 获取当前组 ID

7. 示例代码

示例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
51
52
53
54
55
56
57
58
59
60
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main() {
int fd;
int ret;

// 创建测试文件
fd = open("test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建文件失败");
exit(EXIT_FAILURE);
}

printf("成功创建文件,文件描述符: %d\n", fd);

// 写入一些内容
const char *content = "这是一个测试文件\n";
write(fd, content, strlen(content));

// 修改文件所有者为 root (uid=0),组为 root (gid=0)
// 注意:这需要适当的权限(通常是 root 权限)
ret = fchown(fd, 0, 0);
if (ret == -1) {
if (errno == EPERM) {
printf("权限不足:需要 root 权限才能将文件所有者改为 root\n");
} else {
perror("fchown 调用失败");
}
} else {
printf("成功将文件所有者修改为 root:root\n");
}

// 只修改所有者,保持组不变
ret = fchown(fd, getuid(), -1); // -1 表示不修改组
if (ret == -1) {
perror("修改所有者失败");
} else {
printf("成功将文件所有者修改为当前用户\n");
}

// 只修改组,保持所有者不变
ret = fchown(fd, -1, getgid()); // -1 表示不修改所有者
if (ret == -1) {
perror("修改组失败");
} else {
printf("成功将文件组修改为当前组\n");
}

close(fd);
printf("文件已关闭\n");

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
58
59
60
61
62
63
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main() {
int fd;
int ret;
struct stat file_info;

// 打开系统文件进行测试
fd = open("/etc/passwd", O_RDONLY);
if (fd == -1) {
perror("打开 /etc/passwd 失败");
exit(EXIT_FAILURE);
}

printf("成功打开文件,文件描述符: %d\n", fd);

// 获取文件当前信息
if (fstat(fd, &file_info) == -1) {
perror("获取文件信息失败");
close(fd);
exit(EXIT_FAILURE);
}

printf("文件当前信息:\n");
printf(" 所有者 UID: %d\n", file_info.st_uid);
printf(" 所属组 GID: %d\n", file_info.st_gid);
printf(" 文件大小: %ld 字节\n", file_info.st_size);

// 尝试修改所有者(这通常会失败,除非我们有适当权限)
ret = fchown(fd, 1000, 1000); // 尝试修改为 UID 1000, GID 1000
if (ret == -1) {
switch (errno) {
case EPERM:
printf("权限拒绝:修改文件所有者需要适当权限\n");
break;
case EBADF:
printf("无效的文件描述符\n");
break;
case EROFS:
printf("文件位于只读文件系统上\n");
break;
case EIO:
printf("I/O 错误\n");
break;
default:
printf("其他错误: %s\n", strerror(errno));
break;
}
} else {
printf("成功修改文件所有者\n");
}

close(fd);
printf("文件已关闭\n");

return 0;
}

setpgid系统调用及示例

setpgid 函数详解

setpgid 是Linux系统调用,用于设置进程的进程组ID(Process Group ID)。进程组是进程的集合,用于信号分发和作业控制。通过设置进程组,可以将相关进程组织在一起,便于统一管理和控制。

  1. 函数介绍

setpgid 是Linux系统调用,用于设置进程的进程组ID(Process Group ID)。进程组是进程的集合,用于信号分发和作业控制。通过设置进程组,可以将相关进程组织在一起,便于统一管理和控制。

setpgid系统调用LinuxGuide

setpgid系统调用及示例_setpgid函数作用-CSDN博客

  1. 函数原型
1
2
3
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);

  1. 功能

setpgid 将指定进程(由pid标识)加入到指定的进程组(由pgid标识)。如果进程组不存在,则创建新的进程组。这个函数主要用于作业控制和信号管理。

  1. 参数
  • pid_t pid: 目标进程ID(0表示当前进程)

  • pid_t pgid: 进程组ID(0表示使用pid作为进程组ID)

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

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • getpgid: 获取进程的进程组ID

  • setpgrp: 设置进程组(等同于setpgid(0,0))

  • getsid: 获取会话ID

  • setsid: 创建新会话

  1. 示例代码

示例1:基础setpgid使用

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

/**
* 显示进程组信息
*/
void show_process_group_info() {
pid_t pid = getpid();
pid_t pgid = getpgid(0);
pid_t ppid = getppid();

printf("进程ID: %d\n", pid);
printf("父进程ID: %d\n", ppid);
printf("进程组ID: %d\n", pgid);
printf("会话ID: %d\n", getsid(0));
printf("\n");
}

/**
* 演示基础setpgid使用方法
*/
int demo_setpgid_basic() {
pid_t original_pgid, new_pgid;
int result;

printf("=== 基础setpgid使用示例 ===\n");

// 显示原始进程组信息
printf("1. 原始进程组信息:\n");
show_process_group_info();
original_pgid = getpgid(0);

// 将当前进程移动到新的进程组
printf("2. 创建新的进程组:\n");
pid_t new_group_id = getpid(); // 使用当前进程ID作为新进程组ID

printf(" 尝试将进程 %d 移动到进程组 %d\n", getpid(), new_group_id);
result = setpgid(0, new_group_id);

if (result == 0) {
printf(" ✓ 成功创建并加入新进程组\n");

// 验证设置结果
new_pgid = getpgid(0);
printf(" 新的进程组ID: %d\n", new_pgid);

if (new_pgid == new_group_id) {
printf(" ✓ 进程组设置正确\n");
} else {
printf(" ✗ 进程组设置可能有问题\n");
}

show_process_group_info();
} else {
printf(" ✗ 创建新进程组失败: %s\n", strerror(errno));
if (errno == EACCES) {
printf(" 原因:不允许从会话领导进程更改进程组\n");
} else if (errno == EINVAL) {
printf(" 原因:进程ID或进程组ID无效\n");
} else if (errno == EPERM) {
printf(" 原因:权限不足\n");
} else if (errno == ESRCH) {
printf(" 原因:指定的进程不存在\n");
}
}

return 0;
}

int main() {
return demo_setpgid_basic();
}

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

/**
* 子进程函数
*/
void child_process(int child_id) {
printf("子进程 %d 启动\n", getpid());
printf(" 子进程 %d 的原始进程组: %d\n", getpid(), getpgid(0));

// 子进程创建自己的进程组
pid_t new_pgid = getpid();
printf(" 子进程 %d 尝试创建新进程组: %d\n", getpid(), new_pgid);

int result = setpgid(0, new_pgid);
if (result == 0) {
printf(" ✓ 子进程 %d 成功创建新进程组\n", getpid());
printf(" 子进程 %d 当前进程组: %d\n", getpid(), getpgid(0));
} else {
printf(" ✗ 子进程 %d 创建新进程组失败: %s\n", getpid(), strerror(errno));
}

// 模拟一些工作
sleep(3);
printf("子进程 %d 结束\n", getpid());
exit(0);
}

/**
* 演示父子进程组管理
*/
int demo_parent_child_groups() {
pid_t child1_pid, child2_pid;
pid_t original_pgid;

printf("=== 父子进程组管理演示 ===\n");

// 显示父进程信息
printf("父进程信息:\n");
printf(" 进程ID: %d\n", getpid());
printf(" 原始进程组: %d\n", getpgid(0));
printf(" 会话ID: %d\n", getsid(0));
printf("\n");

// 创建第一个子进程
printf("1. 创建第一个子进程:\n");
child1_pid = fork();
if (child1_pid == 0) {
child_process(1);
} else if (child1_pid > 0) {
printf(" 父进程创建子进程1: PID=%d\n", child1_pid);
printf(" 子进程1的进程组: %d\n", getpgid(child1_pid));
} else {
perror("创建子进程1失败");
return -1;
}

// 创建第二个子进程
printf("\n2. 创建第二个子进程:\n");
child2_pid = fork();
if (child2_pid == 0) {
child_process(2);
} else if (child2_pid > 0) {
printf(" 父进程创建子进程2: PID=%d\n", child2_pid);
printf(" 子进程2的进程组: %d\n", getpgid(child2_pid));
} else {
perror("创建子进程2失败");
// 清理已创建的子进程
kill(child1_pid, SIGKILL);
return -1;
}

// 父进程也创建新进程组
printf("\n3. 父进程创建新进程组:\n");
original_pgid = getpgid(0);
pid_t parent_pgid = getpid();

printf(" 父进程尝试创建新进程组: %d\n", parent_pgid);
int result = setpgid(0, parent_pgid);
if (result == 0) {
printf(" ✓ 父进程成功创建新进程组\n");
printf(" 父进程当前进程组: %d\n", getpgid(0));
} else {
printf(" ✗ 父进程创建新进程组失败: %s\n", strerror(errno));
}

// 等待子进程结束
printf("\n4. 等待子进程结束:\n");
int status;
pid_t finished_pid;

while ((finished_pid = wait(&status)) > 0) {
printf(" 子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
}

// 恢复父进程原始进程组
printf("\n5. 恢复父进程原始进程组:\n");
result = setpgid(0, original_pgid);
if (result == 0) {
printf(" ✓ 父进程成功恢复原始进程组: %d\n", getpgid(0));
} else {
printf(" ✗ 父进程恢复原始进程组失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_parent_child_groups();
}

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

/**
* 信号处理函数
*/
void signal_handler(int sig) {
printf("进程 %d 接收到信号 %d (%s)\n", getpid(), sig, strsignal(sig));
}

/**
* 工作进程函数
*/
void worker_process(int worker_id) {
// 设置信号处理
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);

printf("工作进程 %d 启动,进程组: %d\n", getpid(), getpgid(0));

// 创建自己的进程组
pid_t new_pgid = getpid();
if (setpgid(0, new_pgid) == 0) {
printf("工作进程 %d 成功创建进程组: %d\n", getpid(), new_pgid);
}

// 模拟长时间工作
for (int i = 0; i < 10; i++) {
printf("工作进程 %d 正在工作... (%d/10)\n", getpid(), i + 1);
sleep(2);
}

printf("工作进程 %d 结束\n", getpid());
exit(0);
}

/**
* 演示进程组信号处理
*/
int demo_process_group_signaling() {
pid_t workers&#91;3];
pid_t original_pgid;

printf("=== 进程组信号处理演示 ===\n");

// 保存原始进程组
original_pgid = getpgid(0);
printf("主进程原始进程组: %d\n", original_pgid);

// 创建工作进程组
printf("\n1. 创建工作进程组:\n");
for (int i = 0; i < 3; i++) {
workers&#91;i] = fork();
if (workers&#91;i] == 0) {
worker_process(i + 1);
} else if (workers&#91;i] > 0) {
printf(" 创建工作进程 %d: PID=%d\n", i + 1, workers&#91;i]);

// 将子进程加入同一进程组
pid_t group_id = workers&#91;0]; // 使用第一个子进程的PID作为组ID
if (setpgid(workers&#91;i], group_id) == 0) {
printf(" 工作进程 %d 加入进程组: %d\n", workers&#91;i], group_id);
} else {
printf(" 工作进程 %d 加入进程组失败: %s\n", workers&#91;i], strerror(errno));
}
} else {
perror("创建工作进程失败");
// 清理已创建的进程
for (int j = 0; j < i; j++) {
kill(workers&#91;j], SIGKILL);
}
return -1;
}
}

// 显示进程组信息
printf("\n2. 进程组信息:\n");
for (int i = 0; i < 3; i++) {
pid_t pgid = getpgid(workers&#91;i]);
printf(" 工作进程 %d (PID=%d) 进程组: %d\n", i + 1, workers&#91;i], pgid);
}

// 等待一段时间后发送信号
printf("\n3. 等待5秒后发送信号到进程组...\n");
sleep(5);

// 向进程组发送信号(发送给组内所有进程)
pid_t target_group = getpgid(workers&#91;0]);
printf("向进程组 %d 发送 SIGUSR1 信号\n", target_group);

if (kill(-target_group, SIGUSR1) == 0) { // 负数表示发送给整个进程组
printf("✓ 成功发送信号到进程组\n");
} else {
printf("✗ 发送信号失败: %s\n", strerror(errno));
}

// 等待一段时间观察信号处理
sleep(3);

// 发送终止信号
printf("\n4. 发送终止信号:\n");
printf("向进程组 %d 发送 SIGTERM 信号\n", target_group);

if (kill(-target_group, SIGTERM) == 0) {
printf("✓ 成功发送终止信号\n");
} else {
printf("✗ 发送终止信号失败: %s\n", strerror(errno));
}

// 等待所有工作进程结束
printf("\n5. 等待工作进程结束:\n");
int status;
pid_t finished_pid;
int finished_count = 0;

while (finished_count < 3) {
finished_pid = wait(&status);
if (finished_pid > 0) {
finished_count++;
printf(" 工作进程 %d 已结束,退出状态: %d\n",
finished_pid, WEXITSTATUS(status));
} else {
break;
}
}

printf("所有工作进程已完成\n");
return 0;
}

int main() {
return demo_process_group_signaling();
}

示例4:会话和进程组管理

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

/**
* 显示会话和进程组信息
*/
void show_session_info() {
printf("=== 会话和进程组信息 ===\n");
printf("进程ID: %d\n", getpid());
printf("父进程ID: %d\n", getppid());
printf("进程组ID: %d\n", getpgid(0));
printf("会话ID: %d\n", getsid(0));
printf("前台进程组: %d\n", tcgetpgrp(STDIN_FILENO));
printf("\n");
}

/**
* 会话领导进程
*/
void session_leader() {
printf("会话领导进程启动 (PID: %d)\n", getpid());
show_session_info();

// 创建新会话
pid_t sid = setsid();
if (sid != -1) {
printf("✓ 成功创建新会话: %d\n", sid);
show_session_info();
} else {
printf("✗ 创建新会话失败: %s\n", strerror(errno));
exit(1);
}

// 创建子进程组
for (int i = 0; i < 2; i++) {
pid_t child_pid = fork();
if (child_pid == 0) {
// 子进程
printf("子进程 %d 启动\n", getpid());

// 创建自己的进程组
pid_t new_pgid = getpid();
if (setpgid(0, new_pgid) == 0) {
printf("子进程 %d 成功创建进程组: %d\n", getpid(), new_pgid);
}

// 模拟工作
for (int j = 0; j < 5; j++) {
printf("子进程 %d 工作中... (%d/5)\n", getpid(), j + 1);
sleep(2);
}

printf("子进程 %d 结束\n", getpid());
exit(0);
} else if (child_pid > 0) {
printf("会话领导进程创建子进程: %d\n", child_pid);

// 将子进程加入特定进程组
if (setpgid(child_pid, child_pid) == 0) {
printf("子进程 %d 加入进程组成功\n", child_pid);
}
}
}

// 等待子进程结束
int status;
pid_t finished_pid;
while ((finished_pid = wait(&status)) > 0) {
printf("子进程 %d 已结束\n", finished_pid);
}

printf("会话领导进程结束\n");
exit(0);
}

/**
* 演示会话和进程组管理
*/
int demo_session_management() {
pid_t session_leader_pid;

printf("=== 会话和进程组管理演示 ===\n");

// 显示初始信息
printf("1. 初始进程信息:\n");
show_session_info();

// 创建会话领导进程
printf("2. 创建会话领导进程:\n");
session_leader_pid = fork();
if (session_leader_pid == 0) {
session_leader();
} else if (session_leader_pid > 0) {
printf("主进程创建会话领导进程: %d\n", session_leader_pid);

// 等待会话领导进程结束
int status;
waitpid(session_leader_pid, &status, 0);
printf("会话领导进程已结束\n");
} else {
perror("创建会话领导进程失败");
return -1;
}

// 显示最终信息
printf("\n3. 最终进程信息:\n");
show_session_info();

return 0;
}

int main() {
return demo_session_management();
}

示例5:作业控制演示

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>

/**
* 后台作业进程
*/
void background_job(int job_id) {
printf("后台作业 %d 启动 (PID: %d)\n", job_id, getpid());

// 创建独立的进程组
if (setpgid(0, 0) == 0) {
printf("后台作业 %d 成功创建进程组\n", job_id);
} else {
printf("后台作业 %d 创建进程组失败: %s\n", job_id, strerror(errno));
}

// 模拟长时间运行的作业
for (int i = 0; i < 20; i++) {
printf("后台作业 %d 运行中... (%d/20)\n", job_id, i + 1);
sleep(1);

// 检查是否收到终止信号
if (i == 10) {
// 模拟作业暂停和恢复
printf("后台作业 %d 暂停工作\n", job_id);
sleep(2);
printf("后台作业 %d 恢复工作\n", job_id);
}
}

printf("后台作业 %d 完成\n", job_id);
exit(0);
}

/**
* 作业控制管理器
*/
typedef struct {
pid_t pid;
int job_id;
int status; // 0:运行, 1:暂停, 2:完成
char command&#91;256];
} job_t;

/**
* 显示作业列表
*/
void show_job_list(job_t *jobs, int count) {
printf("\n=== 作业列表 ===\n");
printf("%-5s %-8s %-10s %s\n", "作业ID", "进程ID", "状态", "命令");
printf("----------------------------------------\n");

for (int i = 0; i < count; i++) {
const char *status_str;
switch (jobs&#91;i].status) {
case 0: status_str = "运行"; break;
case 1: status_str = "暂停"; break;
case 2: status_str = "完成"; break;
default: status_str = "未知"; break;
}

printf("%-5d %-8d %-10s %s\n",
jobs&#91;i].job_id, jobs&#91;i].pid, status_str, jobs&#91;i].command);
}
printf("\n");
}

/**
* 演示作业控制
*/
int demo_job_control() {
job_t jobs&#91;3];
int job_count = 0;

printf("=== 作业控制演示 ===\n");

// 创建后台作业
printf("1. 创建后台作业:\n");
for (int i = 0; i < 3; i++) {
pid_t job_pid = fork();
if (job_pid == 0) {
background_job(i + 1);
} else if (job_pid > 0) {
// 父进程记录作业信息
jobs&#91;job_count].pid = job_pid;
jobs&#91;job_count].job_id = i + 1;
jobs&#91;job_count].status = 0; // 运行中
snprintf(jobs&#91;job_count].command, sizeof(jobs&#91;job_count].command),
"background_job_%d", i + 1);
job_count++;

printf(" 创建后台作业 %d: PID=%d\n", i + 1, job_pid);

// 将子进程加入独立进程组
if (setpgid(job_pid, job_pid) == 0) {
printf(" 作业 %d 成功创建独立进程组\n", i + 1);
}
} else {
perror("创建后台作业失败");
}
}

// 显示初始作业列表
show_job_list(jobs, job_count);

// 演示向特定作业发送信号
printf("2. 向作业发送信号:\n");
if (job_count > 0) {
printf(" 向作业 1 (PID=%d) 发送 SIGUSR1 信号\n", jobs&#91;0].pid);
if (kill(jobs&#91;0].pid, SIGUSR1) == 0) {
printf(" ✓ 信号发送成功\n");
} else {
printf(" ✗ 信号发送失败: %s\n", strerror(errno));
}
}

// 等待一段时间
printf("\n3. 等待作业运行...\n");
sleep(5);

// 显示当前作业状态
show_job_list(jobs, job_count);

// 演示作业控制操作
printf("4. 作业控制操作:\n");

// 暂停一个作业
if (job_count > 1) {
printf(" 暂停作业 2 (PID=%d)\n", jobs&#91;1].pid);
if (kill(-getpgid(jobs&#91;1].pid), SIGSTOP) == 0) { // 发送给整个进程组
jobs&#91;1].status = 1; // 暂停
printf(" ✓ 作业 2 已暂停\n");
} else {
printf(" ✗ 暂停作业 2 失败: %s\n", strerror(errno));
}
}

// 显示更新后的作业列表
show_job_list(jobs, job_count);

// 恢复暂停的作业
if (job_count > 1) {
printf(" 恢复作业 2 (PID=%d)\n", jobs&#91;1].pid);
if (kill(-getpgid(jobs&#91;1].pid), SIGCONT) == 0) {
jobs&#91;1].status = 0; // 运行中
printf(" ✓ 作业 2 已恢复\n");
} else {
printf(" ✗ 恢复作业 2 失败: %s\n", strerror(errno));
}
}

// 等待所有作业完成
printf("\n5. 等待所有作业完成:\n");
int completed_jobs = 0;
while (completed_jobs < job_count) {
int status;
pid_t finished_pid = waitpid(-1, &status, WNOHANG);
if (finished_pid > 0) {
// 找到完成的作业并更新状态
for (int i = 0; i < job_count; i++) {
if (jobs&#91;i].pid == finished_pid) {
jobs&#91;i].status = 2; // 完成
printf(" 作业 %d (PID=%d) 已完成\n", jobs&#91;i].job_id, finished_pid);
completed_jobs++;
break;
}
}
} else if (finished_pid == 0) {
// 没有作业完成,短暂等待
usleep(100000); // 100ms
} else {
// 错误
if (errno != ECHILD) {
perror("等待作业完成时出错");
}
break;
}
}

// 显示最终作业列表
show_job_list(jobs, job_count);

printf("作业控制演示完成\n");
return 0;
}

int main() {
return demo_job_control();
}

setpgid 使用注意事项

系统要求:

内核版本: 支持进程组管理的Linux内核

权限要求: 通常不需要特殊权限

架构支持: 支持所有主流架构

参数规则:

pid为0: 表示当前进程

pgid为0: 表示使用pid作为进程组ID

进程限制: 只能操作当前会话中的进程

会话限制: 不能将进程移到不同会话的进程组

错误处理:

EACCES: 不允许从会话领导进程更改进程组

EINVAL: 进程ID或进程组ID无效

EPERM: 权限不足

ESRCH: 指定的进程不存在

性能考虑:

系统调用开销: 频繁调用有性能开销

内核数据结构: 需要更新内核中的进程组信息

同步操作: 涉及内核数据结构的同步更新

安全考虑:

进程控制: 可能影响其他进程的行为

信号传播: 进程组信号会影响组内所有进程

会话管理: 不当的会话操作可能影响终端控制

最佳实践:

时机选择: 在进程创建后尽早设置进程组

错误处理: 妥善处理各种错误情况

状态检查: 验证设置结果的正确性

资源清理: 及时清理不需要的进程组

文档记录: 记录进程组管理策略

进程组和会话概念

进程组(Process Group):

  • 定义: 一组相关进程的集合

  • 用途: 信号分发、作业控制

  • 特点: 同一进程组的进程可以被统一控制

会话(Session):

  • 定义: 一个或多个进程组的集合

  • 用途: 终端控制、登录会话管理

  • 特点: 会话领导进程控制整个会话

关系层次:

1
2
3
4
会话 (Session)
└── 进程组 (Process Group)
└── 进程 (Process)

常见使用场景

1. Shell作业控制:

1
2
3
// 创建后台作业的独立进程组
setpgid(child_pid, child_pid);

2. 守护进程:

1
2
3
// 创建独立会话,脱离控制终端
setsid();

3. 多进程应用:

1
2
3
// 将相关进程组织到同一进程组
setpgid(worker_pid, group_leader_pid);

信号与进程组

组信号发送:

1
2
3
// 向整个进程组发送信号
kill(-pgid, SIGTERM);

前台进程组:

1
2
3
// 设置前台进程组(控制终端)
tcsetpgrp(STDIN_FILENO, pgid);

总结

setpgid 是Linux系统中重要的进程管理函数,提供了:

进程组控制: 灵活的进程组管理能力

作业控制: 支持shell风格的作业控制

信号管理: 便于向进程组发送信号

会话管理: 支持会话和终端控制

通过合理使用 setpgid,可以构建更加灵活和可控的多进程应用程序。在实际应用中,需要注意进程组的生命周期管理、错误处理和信号控制等关键问题。

setpgid系统调用及示例

setpgid 函数详解

  1. 函数介绍

setpgid 是Linux系统调用,用于设置进程的进程组ID(Process Group ID)。进程组是进程的集合,用于信号分发和作业控制。通过设置进程组,可以将相关进程组织在一起,便于统一管理和控制。

  1. 函数原型
1
2
3
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);

  1. 功能

setpgid 将指定进程(由pid标识)加入到指定的进程组(由pgid标识)。如果进程组不存在,则创建新的进程组。这个函数主要用于作业控制和信号管理。

  1. 参数
  • pid_t pid: 目标进程ID(0表示当前进程)

  • pid_t pgid: 进程组ID(0表示使用pid作为进程组ID)

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

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • getpgid: 获取进程的进程组ID

  • setpgrp: 设置进程组(等同于setpgid(0,0))

  • getsid: 获取会话ID

  • setsid: 创建新会话

  1. 示例代码

示例1:基础setpgid使用

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

/**
* 显示进程组信息
*/
void show_process_group_info() {
pid_t pid = getpid();
pid_t pgid = getpgid(0);
pid_t ppid = getppid();

printf("进程ID: %d\n", pid);
printf("父进程ID: %d\n", ppid);
printf("进程组ID: %d\n", pgid);
printf("会话ID: %d\n", getsid(0));
printf("\n");
}

/**
* 演示基础setpgid使用方法
*/
int demo_setpgid_basic() {
pid_t original_pgid, new_pgid;
int result;

printf("=== 基础setpgid使用示例 ===\n");

// 显示原始进程组信息
printf("1. 原始进程组信息:\n");
show_process_group_info();
original_pgid = getpgid(0);

// 将当前进程移动到新的进程组
printf("2. 创建新的进程组:\n");
pid_t new_group_id = getpid(); // 使用当前进程ID作为新进程组ID

printf(" 尝试将进程 %d 移动到进程组 %d\n", getpid(), new_group_id);
result = setpgid(0, new_group_id);

if (result == 0) {
printf(" ✓ 成功创建并加入新进程组\n");

// 验证设置结果
new_pgid = getpgid(0);
printf(" 新的进程组ID: %d\n", new_pgid);

if (new_pgid == new_group_id) {
printf(" ✓ 进程组设置正确\n");
} else {
printf(" ✗ 进程组设置可能有问题\n");
}

show_process_group_info();
} else {
printf(" ✗ 创建新进程组失败: %s\n", strerror(errno));
if (errno == EACCES) {
printf(" 原因:不允许从会话领导进程更改进程组\n");
} else if (errno == EINVAL) {
printf(" 原因:进程ID或进程组ID无效\n");
} else if (errno == EPERM) {
printf(" 原因:权限不足\n");
} else if (errno == ESRCH) {
printf(" 原因:指定的进程不存在\n");
}
}

return 0;
}

int main() {
return demo_setpgid_basic();
}

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

/**
* 子进程函数
*/
void child_process(int child_id) {
printf("子进程 %d 启动\n", getpid());
printf(" 子进程 %d 的原始进程组: %d\n", getpid(), getpgid(0));

// 子进程创建自己的进程组
pid_t new_pgid = getpid();
printf(" 子进程 %d 尝试创建新进程组: %d\n", getpid(), new_pgid);

int result = setpgid(0, new_pgid);
if (result == 0) {
printf(" ✓ 子进程 %d 成功创建新进程组\n", getpid());
printf(" 子进程 %d 当前进程组: %d\n", getpid(), getpgid(0));
} else {
printf(" ✗ 子进程 %d 创建新进程组失败: %s\n", getpid(), strerror(errno));
}

// 模拟一些工作
sleep(3);
printf("子进程 %d 结束\n", getpid());
exit(0);
}

/**
* 演示父子进程组管理
*/
int demo_parent_child_groups() {
pid_t child1_pid, child2_pid;
pid_t original_pgid;

printf("=== 父子进程组管理演示 ===\n");

// 显示父进程信息
printf("父进程信息:\n");
printf(" 进程ID: %d\n", getpid());
printf(" 原始进程组: %d\n", getpgid(0));
printf(" 会话ID: %d\n", getsid(0));
printf("\n");

// 创建第一个子进程
printf("1. 创建第一个子进程:\n");
child1_pid = fork();
if (child1_pid == 0) {
child_process(1);
} else if (child1_pid > 0) {
printf(" 父进程创建子进程1: PID=%d\n", child1_pid);
printf(" 子进程1的进程组: %d\n", getpgid(child1_pid));
} else {
perror("创建子进程1失败");
return -1;
}

// 创建第二个子进程
printf("\n2. 创建第二个子进程:\n");
child2_pid = fork();
if (child2_pid == 0) {
child_process(2);
} else if (child2_pid > 0) {
printf(" 父进程创建子进程2: PID=%d\n", child2_pid);
printf(" 子进程2的进程组: %d\n", getpgid(child2_pid));
} else {
perror("创建子进程2失败");
// 清理已创建的子进程
kill(child1_pid, SIGKILL);
return -1;
}

// 父进程也创建新进程组
printf("\n3. 父进程创建新进程组:\n");
original_pgid = getpgid(0);
pid_t parent_pgid = getpid();

printf(" 父进程尝试创建新进程组: %d\n", parent_pgid);
int result = setpgid(0, parent_pgid);
if (result == 0) {
printf(" ✓ 父进程成功创建新进程组\n");
printf(" 父进程当前进程组: %d\n", getpgid(0));
} else {
printf(" ✗ 父进程创建新进程组失败: %s\n", strerror(errno));
}

// 等待子进程结束
printf("\n4. 等待子进程结束:\n");
int status;
pid_t finished_pid;

while ((finished_pid = wait(&status)) > 0) {
printf(" 子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
}

// 恢复父进程原始进程组
printf("\n5. 恢复父进程原始进程组:\n");
result = setpgid(0, original_pgid);
if (result == 0) {
printf(" ✓ 父进程成功恢复原始进程组: %d\n", getpgid(0));
} else {
printf(" ✗ 父进程恢复原始进程组失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_parent_child_groups();
}

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

/**
* 信号处理函数
*/
void signal_handler(int sig) {
printf("进程 %d 接收到信号 %d (%s)\n", getpid(), sig, strsignal(sig));
}

/**
* 工作进程函数
*/
void worker_process(int worker_id) {
// 设置信号处理
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);

printf("工作进程 %d 启动,进程组: %d\n", getpid(), getpgid(0));

// 创建自己的进程组
pid_t new_pgid = getpid();
if (setpgid(0, new_pgid) == 0) {
printf("工作进程 %d 成功创建进程组: %d\n", getpid(), new_pgid);
}

// 模拟长时间工作
for (int i = 0; i < 10; i++) {
printf("工作进程 %d 正在工作... (%d/10)\n", getpid(), i + 1);
sleep(2);
}

printf("工作进程 %d 结束\n", getpid());
exit(0);
}

/**
* 演示进程组信号处理
*/
int demo_process_group_signaling() {
pid_t workers&#91;3];
pid_t original_pgid;

printf("=== 进程组信号处理演示 ===\n");

// 保存原始进程组
original_pgid = getpgid(0);
printf("主进程原始进程组: %d\n", original_pgid);

// 创建工作进程组
printf("\n1. 创建工作进程组:\n");
for (int i = 0; i < 3; i++) {
workers&#91;i] = fork();
if (workers&#91;i] == 0) {
worker_process(i + 1);
} else if (workers&#91;i] > 0) {
printf(" 创建工作进程 %d: PID=%d\n", i + 1, workers&#91;i]);

// 将子进程加入同一进程组
pid_t group_id = workers&#91;0]; // 使用第一个子进程的PID作为组ID
if (setpgid(workers&#91;i], group_id) == 0) {
printf(" 工作进程 %d 加入进程组: %d\n", workers&#91;i], group_id);
} else {
printf(" 工作进程 %d 加入进程组失败: %s\n", workers&#91;i], strerror(errno));
}
} else {
perror("创建工作进程失败");
// 清理已创建的进程
for (int j = 0; j < i; j++) {
kill(workers&#91;j], SIGKILL);
}
return -1;
}
}

// 显示进程组信息
printf("\n2. 进程组信息:\n");
for (int i = 0; i < 3; i++) {
pid_t pgid = getpgid(workers&#91;i]);
printf(" 工作进程 %d (PID=%d) 进程组: %d\n", i + 1, workers&#91;i], pgid);
}

// 等待一段时间后发送信号
printf("\n3. 等待5秒后发送信号到进程组...\n");
sleep(5);

// 向进程组发送信号(发送给组内所有进程)
pid_t target_group = getpgid(workers&#91;0]);
printf("向进程组 %d 发送 SIGUSR1 信号\n", target_group);

if (kill(-target_group, SIGUSR1) == 0) { // 负数表示发送给整个进程组
printf("✓ 成功发送信号到进程组\n");
} else {
printf("✗ 发送信号失败: %s\n", strerror(errno));
}

// 等待一段时间观察信号处理
sleep(3);

// 发送终止信号
printf("\n4. 发送终止信号:\n");
printf("向进程组 %d 发送 SIGTERM 信号\n", target_group);

if (kill(-target_group, SIGTERM) == 0) {
printf("✓ 成功发送终止信号\n");
} else {
printf("✗ 发送终止信号失败: %s\n", strerror(errno));
}

// 等待所有工作进程结束
printf("\n5. 等待工作进程结束:\n");
int status;
pid_t finished_pid;
int finished_count = 0;

while (finished_count < 3) {
finished_pid = wait(&status);
if (finished_pid > 0) {
finished_count++;
printf(" 工作进程 %d 已结束,退出状态: %d\n",
finished_pid, WEXITSTATUS(status));
} else {
break;
}
}

printf("所有工作进程已完成\n");
return 0;
}

int main() {
return demo_process_group_signaling();
}

示例4:会话和进程组管理

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

/**
* 显示会话和进程组信息
*/
void show_session_info() {
printf("=== 会话和进程组信息 ===\n");
printf("进程ID: %d\n", getpid());
printf("父进程ID: %d\n", getppid());
printf("进程组ID: %d\n", getpgid(0));
printf("会话ID: %d\n", getsid(0));
printf("前台进程组: %d\n", tcgetpgrp(STDIN_FILENO));
printf("\n");
}

/**
* 会话领导进程
*/
void session_leader() {
printf("会话领导进程启动 (PID: %d)\n", getpid());
show_session_info();

// 创建新会话
pid_t sid = setsid();
if (sid != -1) {
printf("✓ 成功创建新会话: %d\n", sid);
show_session_info();
} else {
printf("✗ 创建新会话失败: %s\n", strerror(errno));
exit(1);
}

// 创建子进程组
for (int i = 0; i < 2; i++) {
pid_t child_pid = fork();
if (child_pid == 0) {
// 子进程
printf("子进程 %d 启动\n", getpid());

// 创建自己的进程组
pid_t new_pgid = getpid();
if (setpgid(0, new_pgid) == 0) {
printf("子进程 %d 成功创建进程组: %d\n", getpid(), new_pgid);
}

// 模拟工作
for (int j = 0; j < 5; j++) {
printf("子进程 %d 工作中... (%d/5)\n", getpid(), j + 1);
sleep(2);
}

printf("子进程 %d 结束\n", getpid());
exit(0);
} else if (child_pid > 0) {
printf("会话领导进程创建子进程: %d\n", child_pid);

// 将子进程加入特定进程组
if (setpgid(child_pid, child_pid) == 0) {
printf("子进程 %d 加入进程组成功\n", child_pid);
}
}
}

// 等待子进程结束
int status;
pid_t finished_pid;
while ((finished_pid = wait(&status)) > 0) {
printf("子进程 %d 已结束\n", finished_pid);
}

printf("会话领导进程结束\n");
exit(0);
}

/**
* 演示会话和进程组管理
*/
int demo_session_management() {
pid_t session_leader_pid;

printf("=== 会话和进程组管理演示 ===\n");

// 显示初始信息
printf("1. 初始进程信息:\n");
show_session_info();

// 创建会话领导进程
printf("2. 创建会话领导进程:\n");
session_leader_pid = fork();
if (session_leader_pid == 0) {
session_leader();
} else if (session_leader_pid > 0) {
printf("主进程创建会话领导进程: %d\n", session_leader_pid);

// 等待会话领导进程结束
int status;
waitpid(session_leader_pid, &status, 0);
printf("会话领导进程已结束\n");
} else {
perror("创建会话领导进程失败");
return -1;
}

// 显示最终信息
printf("\n3. 最终进程信息:\n");
show_session_info();

return 0;
}

int main() {
return demo_session_management();
}

示例5:作业控制演示

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>

/**
* 后台作业进程
*/
void background_job(int job_id) {
printf("后台作业 %d 启动 (PID: %d)\n", job_id, getpid());

// 创建独立的进程组
if (setpgid(0, 0) == 0) {
printf("后台作业 %d 成功创建进程组\n", job_id);
} else {
printf("后台作业 %d 创建进程组失败: %s\n", job_id, strerror(errno));
}

// 模拟长时间运行的作业
for (int i = 0; i < 20; i++) {
printf("后台作业 %d 运行中... (%d/20)\n", job_id, i + 1);
sleep(1);

// 检查是否收到终止信号
if (i == 10) {
// 模拟作业暂停和恢复
printf("后台作业 %d 暂停工作\n", job_id);
sleep(2);
printf("后台作业 %d 恢复工作\n", job_id);
}
}

printf("后台作业 %d 完成\n", job_id);
exit(0);
}

/**
* 作业控制管理器
*/
typedef struct {
pid_t pid;
int job_id;
int status; // 0:运行, 1:暂停, 2:完成
char command&#91;256];
} job_t;

/**
* 显示作业列表
*/
void show_job_list(job_t *jobs, int count) {
printf("\n=== 作业列表 ===\n");
printf("%-5s %-8s %-10s %s\n", "作业ID", "进程ID", "状态", "命令");
printf("----------------------------------------\n");

for (int i = 0; i < count; i++) {
const char *status_str;
switch (jobs&#91;i].status) {
case 0: status_str = "运行"; break;
case 1: status_str = "暂停"; break;
case 2: status_str = "完成"; break;
default: status_str = "未知"; break;
}

printf("%-5d %-8d %-10s %s\n",
jobs&#91;i].job_id, jobs&#91;i].pid, status_str, jobs&#91;i].command);
}
printf("\n");
}

/**
* 演示作业控制
*/
int demo_job_control() {
job_t jobs&#91;3];
int job_count = 0;

printf("=== 作业控制演示 ===\n");

// 创建后台作业
printf("1. 创建后台作业:\n");
for (int i = 0; i < 3; i++) {
pid_t job_pid = fork();
if (job_pid == 0) {
background_job(i + 1);
} else if (job_pid > 0) {
// 父进程记录作业信息
jobs&#91;job_count].pid = job_pid;
jobs&#91;job_count].job_id = i + 1;
jobs&#91;job_count].status = 0; // 运行中
snprintf(jobs&#91;job_count].command, sizeof(jobs&#91;job_count].command),
"background_job_%d", i + 1);
job_count++;

printf(" 创建后台作业 %d: PID=%d\n", i + 1, job_pid);

// 将子进程加入独立进程组
if (setpgid(job_pid, job_pid) == 0) {
printf(" 作业 %d 成功创建独立进程组\n", i + 1);
}
} else {
perror("创建后台作业失败");
}
}

// 显示初始作业列表
show_job_list(jobs, job_count);

// 演示向特定作业发送信号
printf("2. 向作业发送信号:\n");
if (job_count > 0) {
printf(" 向作业 1 (PID=%d) 发送 SIGUSR1 信号\n", jobs&#91;0].pid);
if (kill(jobs&#91;0].pid, SIGUSR1) == 0) {
printf(" ✓ 信号发送成功\n");
} else {
printf(" ✗ 信号发送失败: %s\n", strerror(errno));
}
}

// 等待一段时间
printf("\n3. 等待作业运行...\n");
sleep(5);

// 显示当前作业状态
show_job_list(jobs, job_count);

// 演示作业控制操作
printf("4. 作业控制操作:\n");

// 暂停一个作业
if (job_count > 1) {
printf(" 暂停作业 2 (PID=%d)\n", jobs&#91;1].pid);
if (kill(-getpgid(jobs&#91;1].pid), SIGSTOP) == 0) { // 发送给整个进程组
jobs&#91;1].status = 1; // 暂停
printf(" ✓ 作业 2 已暂停\n");
} else {
printf(" ✗ 暂停作业 2 失败: %s\n", strerror(errno));
}
}

// 显示更新后的作业列表
show_job_list(jobs, job_count);

// 恢复暂停的作业
if (job_count > 1) {
printf(" 恢复作业 2 (PID=%d)\n", jobs&#91;1].pid);
if (kill(-getpgid(jobs&#91;1].pid), SIGCONT) == 0) {
jobs&#91;1].status = 0; // 运行中
printf(" ✓ 作业 2 已恢复\n");
} else {
printf(" ✗ 恢复作业 2 失败: %s\n", strerror(errno));
}
}

// 等待所有作业完成
printf("\n5. 等待所有作业完成:\n");
int completed_jobs = 0;
while (completed_jobs < job_count) {
int status;
pid_t finished_pid = waitpid(-1, &status, WNOHANG);
if (finished_pid > 0) {
// 找到完成的作业并更新状态
for (int i = 0; i < job_count; i++) {
if (jobs&#91;i].pid == finished_pid) {
jobs&#91;i].status = 2; // 完成
printf(" 作业 %d (PID=%d) 已完成\n", jobs&#91;i].job_id, finished_pid);
completed_jobs++;
break;
}
}
} else if (finished_pid == 0) {
// 没有作业完成,短暂等待
usleep(100000); // 100ms
} else {
// 错误
if (errno != ECHILD) {
perror("等待作业完成时出错");
}
break;
}
}

// 显示最终作业列表
show_job_list(jobs, job_count);

printf("作业控制演示完成\n");
return 0;
}

int main() {
return demo_job_control();
}

setpgid 使用注意事项

系统要求:

内核版本: 支持进程组管理的Linux内核

权限要求: 通常不需要特殊权限

架构支持: 支持所有主流架构

参数规则:

pid为0: 表示当前进程

pgid为0: 表示使用pid作为进程组ID

进程限制: 只能操作当前会话中的进程

会话限制: 不能将进程移到不同会话的进程组

错误处理:

EACCES: 不允许从会话领导进程更改进程组

EINVAL: 进程ID或进程组ID无效

EPERM: 权限不足

ESRCH: 指定的进程不存在

性能考虑:

系统调用开销: 频繁调用有性能开销

内核数据结构: 需要更新内核中的进程组信息

同步操作: 涉及内核数据结构的同步更新

安全考虑:

进程控制: 可能影响其他进程的行为

信号传播: 进程组信号会影响组内所有进程

会话管理: 不当的会话操作可能影响终端控制

最佳实践:

时机选择: 在进程创建后尽早设置进程组

错误处理: 妥善处理各种错误情况

状态检查: 验证设置结果的正确性

资源清理: 及时清理不需要的进程组

文档记录: 记录进程组管理策略

进程组和会话概念

进程组(Process Group):

  • 定义: 一组相关进程的集合

  • 用途: 信号分发、作业控制

  • 特点: 同一进程组的进程可以被统一控制

会话(Session):

  • 定义: 一个或多个进程组的集合

  • 用途: 终端控制、登录会话管理

  • 特点: 会话领导进程控制整个会话

关系层次:

1
2
3
4
会话 (Session)
└── 进程组 (Process Group)
└── 进程 (Process)

常见使用场景

1. Shell作业控制:

1
2
3
// 创建后台作业的独立进程组
setpgid(child_pid, child_pid);

2. 守护进程:

1
2
3
// 创建独立会话,脱离控制终端
setsid();

3. 多进程应用:

1
2
3
// 将相关进程组织到同一进程组
setpgid(worker_pid, group_leader_pid);

信号与进程组

组信号发送:

1
2
3
// 向整个进程组发送信号
kill(-pgid, SIGTERM);

前台进程组:

1
2
3
// 设置前台进程组(控制终端)
tcsetpgrp(STDIN_FILENO, pgid);

总结

setpgid 是Linux系统中重要的进程管理函数,提供了:

进程组控制: 灵活的进程组管理能力

作业控制: 支持shell风格的作业控制

信号管理: 便于向进程组发送信号

会话管理: 支持会话和终端控制

通过合理使用 setpgid,可以构建更加灵活和可控的多进程应用程序。在实际应用中,需要注意进程组的生命周期管理、错误处理和信号控制等关键问题。

setpriority系统调用及示例

setpriority 函数详解

  1. 函数介绍

setpriority 是Linux系统调用,用于设置进程、进程组或用户的调度优先级(nice值)。它允许调整进程的CPU调度优先级,从而影响进程获得CPU时间的相对比例。较低的nice值表示更高的优先级,较高的nice值表示更低的优先级。

  1. 函数原型
1
2
3
#include <sys/resource.h>
int setpriority(int which, id_t who, int prio);

  1. 功能

setpriority 设置指定进程、进程组或用户的调度优先级(nice值)。这个函数主要用于进程调度优化,通过调整优先级来平衡系统资源分配。

  1. 参数
  • int which: 指定操作类型(PRIO_PROCESS, PRIO_PGRP, PRIO_USER)

  • id_t who: 根据which参数指定的ID(进程ID、进程组ID或用户ID)

  • int prio: 新的优先级值(nice值,范围-20到19)

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

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • getpriority: 获取优先级

  • nice: 调整当前进程的nice值

  • sched_setscheduler: 设置进程调度策略

  • sched_setparam: 设置进程调度参数

  1. 示例代码

示例1:基础setpriority使用

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

/**
* 显示进程优先级信息
*/
void show_process_priority() {
pid_t pid = getpid();
uid_t uid = getuid();
int prio;

printf("=== 进程优先级信息 ===\n");
printf("进程ID: %d\n", pid);
printf("用户ID: %d\n", uid);

// 获取当前进程优先级
errno = 0;
prio = getpriority(PRIO_PROCESS, 0);
if (prio == -1 && errno != 0) {
printf("获取进程优先级失败: %s\n", strerror(errno));
} else {
printf("当前进程优先级 (nice值): %d\n", prio);
}

printf("\n");
}

/**
* 演示基础setpriority使用方法
*/
int demo_setpriority_basic() {
int original_prio, new_prio;
int result;

printf("=== 基础setpriority使用示例 ===\n");

// 显示原始优先级信息
show_process_priority();
original_prio = getpriority(PRIO_PROCESS, 0);
printf("原始优先级: %d\n", original_prio);

// 提高进程优先级(降低nice值)
printf("1. 提高进程优先级 (nice值从 %d 降到 %d):\n", original_prio, original_prio - 5);
result = setpriority(PRIO_PROCESS, 0, original_prio - 5);

if (result == 0) {
printf(" ✓ 成功提高优先级\n");

// 验证设置结果
new_prio = getpriority(PRIO_PROCESS, 0);
printf(" 新优先级: %d\n", new_prio);

if (new_prio == original_prio - 5) {
printf(" ✓ 优先级设置正确\n");
} else {
printf(" ✗ 优先级设置可能有问题\n");
}
} else {
printf(" ✗ 提高优先级失败: %s\n", strerror(errno));
if (errno == EACCES) {
printf(" 原因:需要root权限或CAP_SYS_NICE能力来提高优先级\n");
} else if (errno == EINVAL) {
printf(" 原因:优先级值超出有效范围\n");
} else if (errno == ESRCH) {
printf(" 原因:指定的进程不存在\n");
}
}

// 降低进程优先级(提高nice值)
printf("\n2. 降低进程优先级 (nice值从 %d 升到 %d):\n", new_prio, new_prio + 10);
result = setpriority(PRIO_PROCESS, 0, new_prio + 10);

if (result == 0) {
printf(" ✓ 成功降低优先级\n");

// 验证设置结果
new_prio = getpriority(PRIO_PROCESS, 0);
printf(" 新优先级: %d\n", new_prio);
} else {
printf(" ✗ 降低优先级失败: %s\n", strerror(errno));
}

// 恢复原始优先级
printf("\n3. 恢复原始优先级 (%d):\n", original_prio);
result = setpriority(PRIO_PROCESS, 0, original_prio);

if (result == 0) {
printf(" ✓ 成功恢复原始优先级\n");
new_prio = getpriority(PRIO_PROCESS, 0);
printf(" 最终优先级: %d\n", new_prio);
} else {
printf(" ✗ 恢复原始优先级失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_setpriority_basic();
}

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

/**
* 工作进程函数
*/
void worker_process(int worker_id, int priority) {
pid_t pid = getpid();

printf("工作进程 %d 启动 (PID: %d)\n", worker_id, pid);

// 设置进程优先级
if (setpriority(PRIO_PROCESS, 0, priority) == 0) {
printf("工作进程 %d 优先级设置为 %d\n", worker_id, priority);
} else {
printf("工作进程 %d 优先级设置失败: %s\n", worker_id, strerror(errno));
}

// 显示当前优先级
int current_prio = getpriority(PRIO_PROCESS, 0);
printf("工作进程 %d 当前优先级: %d\n", worker_id, current_prio);

// 模拟工作负载
volatile long sum = 0;
for (long i = 0; i < 1000000000L; i++) {
sum += i;
if (i % 100000000 == 0) {
printf("工作进程 %d: 进度 %ld%%\n", worker_id, i / 10000000);
}
}

printf("工作进程 %d 完成,计算结果: %ld\n", worker_id, sum);
exit(0);
}

/**
* 演示多进程优先级管理
*/
int demo_multiprocess_priority() {
pid_t workers&#91;3];
int priorities&#91;] = {-5, 0, 10}; // 高、中、低优先级
int worker_count = 3;

printf("=== 多进程优先级管理演示 ===\n");

// 显示父进程信息
printf("父进程信息:\n");
printf(" 进程ID: %d\n", getpid());
printf(" 当前优先级: %d\n", getpriority(PRIO_PROCESS, 0));
printf("\n");

// 创建工作进程
printf("创建 %d 个工作进程:\n", worker_count);
for (int i = 0; i < worker_count; i++) {
workers&#91;i] = fork();
if (workers&#91;i] == 0) {
// 子进程
worker_process(i + 1, priorities&#91;i]);
} else if (workers&#91;i] > 0) {
// 父进程
printf(" 创建工作进程 %d: PID=%d, 优先级=%d\n",
i + 1, workers&#91;i], priorities&#91;i]);
} else {
perror("创建工作进程失败");
// 清理已创建的进程
for (int j = 0; j < i; j++) {
kill(workers&#91;j], SIGKILL);
}
return -1;
}
}

// 显示所有进程的优先级
printf("\n所有进程优先级信息:\n");
for (int i = 0; i < worker_count; i++) {
errno = 0;
int prio = getpriority(PRIO_PROCESS, workers&#91;i]);
if (prio != -1 || errno == 0) {
printf(" 进程 %d (PID=%d): 优先级=%d\n", i + 1, workers&#91;i], prio);
} else {
printf(" 进程 %d (PID=%d): 无法获取优先级 (%s)\n",
i + 1, workers&#91;i], strerror(errno));
}
}

// 等待所有工作进程完成
printf("\n等待工作进程完成:\n");
int status;
pid_t finished_pid;
int finished_count = 0;

while (finished_count < worker_count) {
finished_pid = wait(&status);
if (finished_pid > 0) {
finished_count++;
printf(" 工作进程 (PID=%d) 已完成,退出状态: %d\n",
finished_pid, WEXITSTATUS(status));
} else {
break;
}
}

printf("所有工作进程已完成\n");
return 0;
}

int main() {
return demo_multiprocess_priority();
}

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

/**
* 显示进程组信息
*/
void show_process_group_info(pid_t pgid) {
printf("进程组 %d 信息:\n", pgid);

// 显示进程组中所有进程的优先级
// 注意:这里简化处理,实际应用中需要遍历/proc或使用其他方法
int current_prio = getpriority(PRIO_PGRP, pgid);
if (current_prio != -1 || errno == 0) {
printf(" 进程组优先级: %d\n", current_prio);
} else {
printf(" 无法获取进程组优先级: %s\n", strerror(errno));
}
}

/**
* 工作进程函数(进程组成员)
*/
void group_worker(int worker_id, pid_t group_leader) {
pid_t pid = getpid();

printf("组工作进程 %d 启动 (PID: %d)\n", worker_id, pid);

// 加入指定的进程组
if (setpgid(0, group_leader) == 0) {
printf("组工作进程 %d 成功加入进程组 %d\n", worker_id, group_leader);
} else {
printf("组工作进程 %d 加入进程组失败: %s\n", worker_id, strerror(errno));
}

// 显示当前进程信息
printf("组工作进程 %d 当前优先级: %d\n", worker_id, getpriority(PRIO_PROCESS, 0));
printf("组工作进程 %d 所属进程组: %d\n", worker_id, getpgid(0));

// 模拟工作
for (int i = 0; i < 10; i++) {
printf("组工作进程 %d 工作中... (%d/10)\n", worker_id, i + 1);
sleep(1);
}

printf("组工作进程 %d 完成\n", worker_id);
exit(0);
}

/**
* 演示进程组优先级设置
*/
int demo_process_group_priority() {
pid_t group_leader_pid;
pid_t workers&#91;2];
pid_t group_id;

printf("=== 进程组优先级设置演示 ===\n");

// 创建进程组领导进程
group_leader_pid = fork();
if (group_leader_pid == 0) {
// 进程组领导进程
printf("进程组领导进程启动 (PID: %d)\n", getpid());

// 创建自己的进程组
if (setpgid(0, 0) == 0) {
group_id = getpid();
printf("成功创建进程组: %d\n", group_id);
} else {
printf("创建进程组失败: %s\n", strerror(errno));
exit(1);
}

// 显示初始优先级
printf("进程组领导进程初始优先级: %d\n", getpriority(PRIO_PROCESS, 0));

// 创建组内工作进程
for (int i = 0; i < 2; i++) {
workers&#91;i] = fork();
if (workers&#91;i] == 0) {
group_worker(i + 1, group_id);
} else if (workers&#91;i] > 0) {
printf("创建组工作进程 %d: PID=%d\n", i + 1, workers&#91;i]);
}
}

// 等待子进程启动
sleep(1);

// 设置整个进程组的优先级
printf("\n设置进程组 %d 的优先级:\n", group_id);
show_process_group_info(group_id);

// 尝试设置进程组优先级
printf("尝试将进程组 %d 的优先级设置为 5:\n", group_id);
int result = setpriority(PRIO_PGRP, group_id, 5);
if (result == 0) {
printf("✓ 成功设置进程组优先级\n");
} else {
printf("✗ 设置进程组优先级失败: %s\n", strerror(errno));
if (errno == EACCES) {
printf(" 需要适当权限来设置优先级\n");
}
}

// 验证设置结果
show_process_group_info(group_id);

// 等待组内工作进程完成
for (int i = 0; i < 2; i++) {
int status;
waitpid(workers&#91;i], &status, 0);
printf("组工作进程 %d 已完成\n", i + 1);
}

printf("进程组领导进程结束\n");
exit(0);
} else if (group_leader_pid > 0) {
printf("主进程创建进程组领导进程: %d\n", group_leader_pid);

// 等待进程组领导进程完成
int status;
waitpid(group_leader_pid, &status, 0);
printf("进程组领导进程已结束\n");
} else {
perror("创建进程组领导进程失败");
return -1;
}

return 0;
}

int main() {
return demo_process_group_priority();
}

示例4:用户优先级管理

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

/**
* 显示用户优先级信息
*/
void show_user_priority(uid_t uid) {
struct passwd *pwd = getpwuid(uid);
if (pwd) {
printf("用户 %s (UID: %d) 信息:\n", pwd->pw_name, uid);
} else {
printf("用户 (UID: %d) 信息:\n", uid);
}

// 获取用户进程的优先级(注意:这会返回其中一个进程的优先级)
errno = 0;
int prio = getpriority(PRIO_USER, uid);
if (prio != -1 || errno == 0) {
printf(" 用户进程优先级: %d\n", prio);
} else {
printf(" 无法获取用户优先级: %s\n", strerror(errno));
if (errno == ESRCH) {
printf(" 该用户可能没有运行中的进程\n");
}
}
}

/**
* 演示用户优先级管理
*/
int demo_user_priority_management() {
uid_t current_uid = getuid();
uid_t effective_uid = geteuid();

printf("=== 用户优先级管理演示 ===\n");

// 显示当前用户信息
printf("1. 当前用户信息:\n");
printf(" 真实用户ID: %d\n", current_uid);
printf(" 有效用户ID: %d\n", effective_uid);

show_user_priority(current_uid);

// 检查权限
printf("\n2. 权限检查:\n");
if (current_uid == 0 || effective_uid == 0) {
printf(" ✓ 当前具有root权限\n");
} else {
printf(" ✗ 当前没有root权限\n");
printf(" 设置其他用户优先级需要root权限\n");
}

// 尝试设置当前用户优先级
printf("\n3. 设置当前用户优先级:\n");
int original_prio = getpriority(PRIO_USER, current_uid);
if (original_prio == -1 && errno != 0) {
original_prio = 0; // 默认值
}

printf(" 当前用户优先级: %d\n", original_prio);
printf(" 尝试设置为: %d\n", original_prio + 2);

int result = setpriority(PRIO_USER, current_uid, original_prio + 2);
if (result == 0) {
printf(" ✓ 成功设置用户优先级\n");

// 验证设置结果
int new_prio = getpriority(PRIO_USER, current_uid);
printf(" 新用户优先级: %d\n", new_prio);
} else {
printf(" ✗ 设置用户优先级失败: %s\n", strerror(errno));
if (errno == EACCES) {
printf(" 需要root权限来设置用户优先级\n");
} else if (errno == EPERM) {
printf(" 权限不足\n");
}
}

// 显示系统中其他用户(如果有权限)
printf("\n4. 系统用户优先级信息:\n");
struct passwd *pwd;
int user_count = 0;

// 重置密码文件指针
setpwent();

while ((pwd = getpwent()) != NULL && user_count < 5) {
// 只显示一些系统用户
if (pwd->pw_uid < 1000) { // 系统用户通常UID较小
show_user_priority(pwd->pw_uid);
user_count++;
}
}

endpwent();

// 恢复原始优先级
printf("\n5. 恢复原始优先级:\n");
result = setpriority(PRIO_USER, current_uid, original_prio);
if (result == 0) {
printf(" ✓ 成功恢复原始优先级\n");
} else {
printf(" ✗ 恢复原始优先级失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_user_priority_management();
}

示例5:优先级调度演示

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
149
150
151
152
#include <sys/resource.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <time.h>

/**
* CPU密集型工作函数
*/
void cpu_intensive_work(int worker_id, int priority, int duration_seconds) {
pid_t pid = getpid();
clock_t start_time = clock();
clock_t work_duration = duration_seconds * CLOCKS_PER_SEC;

printf("CPU密集型工作进程 %d 启动 (PID: %d, 优先级: %d)\n",
worker_id, pid, priority);

// 设置进程优先级
if (setpriority(PRIO_PROCESS, 0, priority) != 0) {
printf("工作进程 %d 设置优先级失败: %s\n", worker_id, strerror(errno));
}

// 执行CPU密集型工作
volatile long sum = 0;
long iterations = 0;

while ((clock() - start_time) < work_duration) {
sum += iterations;
iterations++;

// 定期报告进度
if (iterations % 10000000 == 0) {
double elapsed = (double)(clock() - start_time) / CLOCKS_PER_SEC;
printf("工作进程 %d: 迭代 %ld 次, 耗时 %.2f 秒\n",
worker_id, iterations, elapsed);
}
}

double total_time = (double)(clock() - start_time) / CLOCKS_PER_SEC;
printf("工作进程 %d 完成:\n", worker_id);
printf(" 总迭代次数: %ld\n", iterations);
printf(" 总耗时: %.2f 秒\n", total_time);
printf(" 平均迭代速度: %.0f 次/秒\n", iterations / total_time);

exit(0);
}

/**
* 演示优先级调度效果
*/
int demo_priority_scheduling() {
pid_t workers&#91;3];
int priorities&#91;] = {-10, 0, 10}; // 高、中、低优先级
const char *priority_names&#91;] = {"高优先级", "中优先级", "低优先级"};
int worker_count = 3;
int work_duration = 10; // 工作10秒

printf("=== 优先级调度效果演示 ===\n");
printf("工作时长: %d 秒\n", work_duration);
printf("优先级设置: 高(-10), 中(0), 低(10)\n\n");

// 检查权限
uid_t uid = getuid();
printf("权限检查:\n");
if (uid == 0) {
printf(" ✓ 具有root权限,可以设置所有优先级\n");
} else {
printf(" ✗ 没有root权限,可能无法设置负优先级\n");
}
printf("\n");

// 创建工作进程
printf("创建 %d 个工作进程:\n", worker_count);
for (int i = 0; i < worker_count; i++) {
workers&#91;i] = fork();
if (workers&#91;i] == 0) {
// 子进程
cpu_intensive_work(i + 1, priorities&#91;i], work_duration);
} else if (workers&#91;i] > 0) {
// 父进程
printf(" %s工作进程 %d: PID=%d, 优先级=%d\n",
priority_names&#91;i], i + 1, workers&#91;i], priorities&#91;i]);
} else {
perror("创建工作进程失败");
// 清理已创建的进程
for (int j = 0; j < i; j++) {
kill(workers&#91;j], SIGKILL);
}
return -1;
}
}

// 显示所有进程的初始优先级
printf("\n所有进程初始优先级:\n");
for (int i = 0; i < worker_count; i++) {
errno = 0;
int prio = getpriority(PRIO_PROCESS, workers&#91;i]);
if (prio != -1 || errno == 0) {
printf(" 进程 %d (PID=%d): 优先级=%d\n", i + 1, workers&#91;i], prio);
} else {
printf(" 进程 %d (PID=%d): 获取优先级失败 (%s)\n",
i + 1, workers&#91;i], strerror(errno));
}
}

// 等待所有工作进程完成
printf("\n等待工作进程完成 (约 %d 秒):\n", work_duration);
int status;
pid_t finished_pid;
int finished_count = 0;
time_t start_wait = time(NULL);

while (finished_count < worker_count) {
finished_pid = wait(&status);
if (finished_pid > 0) {
finished_count++;
time_t elapsed = time(NULL) - start_wait;
printf(" 工作进程 (PID=%d) 已完成,耗时 %ld 秒,退出状态: %d\n",
finished_pid, elapsed, WEXITSTATUS(status));
} else if (finished_pid == -1) {
if (errno == EINTR) {
continue; // 被信号中断,继续等待
} else {
break;
}
}
}

// 分析调度效果
printf("\n=== 调度效果分析 ===\n");
printf("高优先级进程应该获得更多的CPU时间\n");
printf("低优先级进程应该获得较少的CPU时间\n");
printf("实际效果取决于系统负载和调度器实现\n");

// 显示优先级相关建议
printf("\n=== 优先级使用建议 ===\n");
printf("1. 优先级范围: -20 (最高) 到 19 (最低)\n");
printf("2. root权限可以设置负优先级\n");
printf("3. 普通用户只能设置0到19的优先级\n");
printf("4. 过高的优先级可能影响系统稳定性\n");
printf("5. 合理使用优先级平衡系统资源\n");

return 0;
}

int main() {
return demo_priority_scheduling();
}

setpriority 使用注意事项

系统要求:

内核版本: 支持进程优先级管理的Linux内核

权限要求: 设置负优先级需要root权限或CAP_SYS_NICE能力

架构支持: 支持所有主流架构

优先级范围:

有效范围: -20 到 19

默认值: 通常为0

负值: 更高优先级(需要特权)

正值: 更低优先级(普通用户可设置)

which参数类型:

PRIO_PROCESS: 指定进程

PRIO_PGRP: 指定进程组

PRIO_USER: 指定用户

错误处理:

EACCES: 权限不足(设置负优先级时)

EINVAL: 优先级值超出有效范围

EPERM: 权限不足

ESRCH: 指定的进程/进程组/用户不存在

性能考虑:

调度开销: 频繁改变优先级有性能开销

系统影响: 不当的优先级设置可能影响系统稳定性

公平性: 避免过度偏向某些进程

安全考虑:

权限提升: 不当使用可能导致权限提升

系统稳定性: 过高的优先级可能影响系统响应

资源竞争: 合理分配优先级避免资源竞争

最佳实践:

权限检查: 执行前检查是否具有足够权限

范围验证: 确保优先级值在有效范围内

错误处理: 妥善处理各种错误情况

适度使用: 避免过度调整优先级

监控效果: 监控优先级调整的效果

优先级值说明

Nice值范围:

  • -20: 最高优先级

  • -10: 高优先级

  • 0: 默认优先级

  • 10: 低优先级

  • 19: 最低优先级

权限要求:

1
2
3
4
5
6
7
8
9
10
// 普通用户可以设置的范围
if (prio >= 0 && prio <= 19) {
// 可以设置
}

// 设置负优先级需要特权
if (prio < 0) {
// 需要root权限或CAP_SYS_NICE能力
}

相关系统调用

1. getpriority:

1
2
int getpriority(int which, id_t who);

2. nice:

1
2
int nice(int inc);

3. sched_setscheduler:

1
2
int sched_setscheduler(pid_t pid, int policy, const struct sched_param *param);

常见使用场景

1. 系统服务管理:

1
2
3
// 降低后台服务优先级
setpriority(PRIO_PROCESS, service_pid, 10);

2. 批处理作业:

1
2
3
// 为批处理作业设置较低优先级
setpriority(PRIO_PROCESS, batch_pid, 15);

3. 实时应用:

1
2
3
// 提高关键应用优先级(需要root权限)
setpriority(PRIO_PROCESS, critical_pid, -5);

优先级调度策略

1. 时间片分配:

  • 高优先级进程获得更多CPU时间片

  • 低优先级进程获得较少CPU时间片

2. 调度顺序:

  • 优先调度高优先级进程

  • 低优先级进程可能被延迟执行

3. I/O优先级:

  • 优先级也影响I/O调度

  • 高优先级进程获得更好的I/O性能

总结

setpriority 是Linux系统中重要的进程调度管理函数,提供了:

优先级控制: 灵活的进程优先级管理

多级调度: 支持进程、进程组、用户级别的优先级设置

权限管理: 通过权限控制保证系统安全

性能优化: 帮助优化系统资源分配

通过合理使用 setpriority,可以构建更加高效和可控的多进程应用程序。在实际应用中,需要注意权限要求、错误处理和系统稳定性等关键问题。

https://www.calcguide.tech/2025/08/11/setpriority系统调用及示例/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/

setregid系统调用及示例

setregid 函数详解

  1. 函数介绍

setregid系统调用详解及示例,掌握Linux进程权限管理,了解函数功能与使用方法,提升系统编程技能。setregid 是Linux系统调用,用于同时设置进程的真实组ID(real group ID)和有效组ID(effective group ID)。它是组ID管理的重要函数,允许进程在不同组权限之间切换,实现灵活的权限控制。

  1. 函数原型
1
2
3
#include <unistd.h>
int setregid(gid_t rgid, gid_t egid);

  1. 功能

setregid 同时设置进程的真实组ID和有效组ID。真实组ID标识进程的实际所有者,有效组ID决定进程当前的权限。这个函数主要用于权限切换和安全控制。

  1. 参数
  • gid_t rgid: 新的真实组ID(-1表示不改变)

  • gid_t egid: 新的有效组ID(-1表示不改变)

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

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • setgid: 仅设置有效组ID

  • setegid: 仅设置有效组ID

  • setrgid: 仅设置真实组ID

  • getgid: 获取真实组ID

  • getegid: 获取有效组ID

  1. 示例代码

示例1:基础setregid使用

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

/**
* 显示当前组ID信息
*/
void show_group_ids() {
gid_t real_gid = getgid();
gid_t effective_gid = getegid();

printf("=== 当前组ID信息 ===\n");
printf("真实组ID: %d", real_gid);
struct group *real_grp = getgrgid(real_gid);
if (real_grp) {
printf(" (%s)", real_grp->gr_name);
}
printf("\n");

printf("有效组ID: %d", effective_gid);
struct group *effective_grp = getgrgid(effective_gid);
if (effective_grp) {
printf(" (%s)", effective_grp->gr_name);
}
printf("\n\n");
}

/**
* 演示基础setregid使用方法
*/
int demo_setregid_basic() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID
int result;

printf("=== 基础setregid使用示例 ===\n");

// 显示原始组ID信息
printf("1. 原始组ID信息:\n");
show_group_ids();

original_real_gid = getgid();
original_effective_gid = getegid();

// 尝试设置组ID(需要适当权限)
printf("2. 尝试设置组ID:\n");
printf(" 尝试将真实组ID设置为: %d\n", test_gid);
printf(" 尝试将有效组ID设置为: %d\n", test_gid);

result = setregid(test_gid, test_gid);
if (result == 0) {
printf(" ✓ 成功设置组ID\n");
show_group_ids();

// 验证设置结果
if (getgid() == test_gid && getegid() == test_gid) {
printf(" ✓ 组ID设置正确\n");
} else {
printf(" ✗ 组ID设置可能有问题\n");
}
} else {
printf(" ✗ 设置组ID失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:权限不足,需要root权限或适当的组权限\n");
} else if (errno == EINVAL) {
printf(" 原因:组ID无效\n");
}

printf(" 注意:普通用户通常无法随意更改组ID\n");
}

// 尝试部分设置(使用-1)
printf("\n3. 尝试部分设置组ID:\n");
printf(" 保持真实组ID不变,仅设置有效组ID\n");

result = setregid(-1, original_effective_gid);
if (result == 0) {
printf(" ✓ 部分设置成功\n");
show_group_ids();
} else {
printf(" ✗ 部分设置失败: %s\n", strerror(errno));
}

// 恢复原始组ID
printf("\n4. 恢复原始组ID:\n");
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 成功恢复原始组ID\n");
show_group_ids();
} else {
printf(" ✗ 恢复原始组ID失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_setregid_basic();
}

示例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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>

/**
* 检查当前权限
*/
void check_permissions() {
uid_t uid = getuid();
uid_t euid = geteuid();
gid_t gid = getgid();
gid_t egid = getegid();

printf("=== 权限检查 ===\n");
printf("真实用户ID: %d", uid);
struct passwd *pwd = getpwuid(uid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf("有效用户ID: %d", euid);
pwd = getpwuid(euid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf("真实组ID: %d", gid);
struct group *grp = getgrgid(gid);
if (grp) {
printf(" (%s)", grp->gr_name);
}
printf("\n");

printf("有效组ID: %d", egid);
grp = getgrgid(egid);
if (grp) {
printf(" (%s)", grp->gr_name);
}
printf("\n");

if (uid == 0 || euid == 0) {
printf("✓ 当前具有root权限\n");
} else {
printf("✗ 当前没有root权限\n");
printf(" 提示:修改组ID通常需要root权限\n");
}
printf("\n");
}

/**
* 查找系统中的组
*/
void find_system_groups() {
struct group *grp;
int group_count = 0;

printf("=== 系统中的部分组信息 ===\n");

// 查找一些常见的组
const char *common_groups&#91;] = {"root", "daemon", "sys", "adm", "tty", "disk", NULL};

for (int i = 0; common_groups&#91;i]; i++) {
grp = getgrnam(common_groups&#91;i]);
if (grp) {
printf(" 组名: %-10s ID: %d\n", grp->gr_name, grp->gr_gid);
group_count++;
}
}

printf(" 找到 %d 个常用组\n\n", group_count);
}

/**
* 演示权限检查和组管理
*/
int demo_permission_check() {
gid_t test_groups&#91;3];
int num_groups = 0;

printf("=== 权限检查和组管理演示 ===\n");

// 检查当前权限
check_permissions();

// 查找系统组
find_system_groups();

// 准备测试组列表
struct group *grp;

// 尝试获取一些系统组ID
grp = getgrnam("daemon");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 daemon (ID: %d) 到测试列表\n", grp->gr_gid);
}

grp = getgrnam("sys");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 sys (ID: %d) 到测试列表\n", grp->gr_gid);
}

grp = getgrnam("adm");
if (grp) {
test_groups&#91;num_groups++] = grp->gr_gid;
printf("添加组 adm (ID: %d) 到测试列表\n", grp->gr_gid);
}

if (num_groups == 0) {
// 如果找不到系统组,使用示例ID
test_groups&#91;0] = 100;
test_groups&#91;1] = 200;
test_groups&#91;2] = 300;
num_groups = 3;
printf("使用示例组ID: 100, 200, 300\n");
}

// 显示当前组ID
printf("\n当前组ID信息:\n");
show_group_ids();

// 尝试设置组ID
printf("\n尝试设置组ID:\n");
printf(" 目标真实组ID: %d\n", test_groups&#91;0]);
printf(" 目标有效组ID: %d\n", test_groups&#91;1]);

int result = setregid(test_groups&#91;0], test_groups&#91;1]);
if (result == 0) {
printf("✓ 组ID设置成功\n");
show_group_ids();
} else {
printf("✗ 组ID设置失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 需要root权限\n");
}
}

// 演示部分设置
printf("\n演示部分设置 (仅改变有效组ID):\n");
printf(" 保持真实组ID: %d\n", getgid());
printf(" 新的有效组ID: %d\n", test_groups&#91;2]);

result = setregid(-1, test_groups&#91;2]);
if (result == 0) {
printf("✓ 部分设置成功\n");
show_group_ids();
} else {
printf("✗ 部分设置失败: %s\n", strerror(errno));
}

return 0;
}

// 辅助函数声明
void show_group_ids();

int main() {
return demo_permission_check();
}

示例3:组ID切换演示

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>
#include <fcntl.h>

/**
* 安全组切换演示
*/
int demo_secure_group_switching() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID

printf("=== 安全组切换演示 ===\n");

// 保存原始组ID
original_real_gid = getgid();
original_effective_gid = getegid();

printf("原始组ID信息:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 演示组切换的安全性考虑
printf("\n1. 组切换安全考虑:\n");
printf(" ✓ 始终保存原始组ID状态\n");
printf(" ✓ 在使用特殊权限后及时恢复\n");
printf(" ✓ 检查组切换操作的返回值\n");
printf(" ✓ 记录组切换日志\n");
printf(" ✓ 最小权限原则:只请求必需的权限\n");

// 演示临时组权限使用
printf("\n2. 模拟使用临时组权限:\n");

// 尝试切换到测试组
int result = setregid(test_gid, test_gid);
if (result == 0) {
printf(" ✓ 成功切换到组 %d\n", test_gid);
show_group_ids();

// 模拟使用组权限的操作
printf(" 使用新组权限进行操作...\n");
// 这里可以进行需要特定组权限的操作

// 恢复原始组ID
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 成功恢复原始组ID\n");
} else {
printf(" ✗ 恢复原始组ID失败: %s\n", strerror(errno));
}
} else {
printf(" ✗ 切换到组 %d 失败: %s\n", test_gid, strerror(errno));
if (errno == EPERM) {
printf(" 需要root权限或适当的组权限\n");
}
}

show_group_ids();

// 演示权限提升检测
printf("\n3. 权限提升检测:\n");
gid_t current_real_gid = getgid();
gid_t current_effective_gid = getegid();

if (current_real_gid != original_real_gid ||
current_effective_gid != original_effective_gid) {
printf(" 警告:组ID状态未正确恢复\n");
} else {
printf(" ✓ 组ID状态已正确恢复\n");
}

// 显示组安全相关信息
printf("\n4. 组安全相关信息:\n");
printf(" 组ID切换需要CAP_SETGID能力\n");
printf(" 真实组ID只能向有效组ID降低\n");
printf(" 避免在生产环境中频繁切换组权限\n");

return 0;
}

// 辅助函数声明
void show_group_ids();

int main() {
return demo_secure_group_switching();
}

示例4:进程组权限管理

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

/**
* 子进程函数
*/
void child_process(int child_id) {
printf("子进程 %d 启动 (PID: %d)\n", child_id, getpid());

// 显示子进程初始组ID
printf(" 子进程 %d 初始组ID:\n", child_id);
printf(" 真实组ID: %d\n", getgid());
printf(" 有效组ID: %d\n", getegid());

// 尝试修改组ID
printf(" 子进程 %d 尝试修改组ID:\n", child_id);

gid_t new_gid = 1000; // 测试组ID
int result = setregid(new_gid, new_gid);
if (result == 0) {
printf(" ✓ 子进程 %d 成功修改组ID\n", child_id);
printf(" 新真实组ID: %d\n", getgid());
printf(" 新有效组ID: %d\n", getegid());
} else {
printf(" ✗ 子进程 %d 修改组ID失败: %s\n", child_id, strerror(errno));
}

// 模拟一些工作
sleep(3);
printf("子进程 %d 结束\n", child_id);
exit(0);
}

/**
* 演示进程组权限管理
*/
int demo_process_group_permissions() {
pid_t child1_pid, child2_pid;
gid_t original_real_gid, original_effective_gid;

printf("=== 进程组权限管理演示 ===\n");

// 保存父进程原始组ID
original_real_gid = getgid();
original_effective_gid = getegid();

printf("父进程原始组ID信息:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 创建第一个子进程
printf("\n1. 创建第一个子进程:\n");
child1_pid = fork();
if (child1_pid == 0) {
child_process(1);
} else if (child1_pid > 0) {
printf(" 父进程创建子进程1: PID=%d\n", child1_pid);
} else {
perror("创建子进程1失败");
return -1;
}

// 创建第二个子进程
printf("\n2. 创建第二个子进程:\n");
child2_pid = fork();
if (child2_pid == 0) {
child_process(2);
} else if (child2_pid > 0) {
printf(" 父进程创建子进程2: PID=%d\n", child2_pid);
} else {
perror("创建子进程2失败");
// 清理已创建的子进程
kill(child1_pid, SIGKILL);
return -1;
}

// 父进程也尝试修改组ID
printf("\n3. 父进程尝试修改组ID:\n");
gid_t parent_new_gid = 2000; // 测试组ID
int result = setregid(parent_new_gid, parent_new_gid);
if (result == 0) {
printf(" ✓ 父进程成功修改组ID\n");
printf(" 新真实组ID: %d\n", getgid());
printf(" 新有效组ID: %d\n", getegid());
} else {
printf(" ✗ 父进程修改组ID失败: %s\n", strerror(errno));
}

// 等待子进程结束
printf("\n4. 等待子进程结束:\n");
int status;
pid_t finished_pid;

while ((finished_pid = wait(&status)) > 0) {
printf(" 子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
}

// 恢复父进程原始组ID
printf("\n5. 恢复父进程原始组ID:\n");
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf(" ✓ 父进程成功恢复原始组ID\n");
printf(" 最终真实组ID: %d\n", getgid());
printf(" 最终有效组ID: %d\n", getegid());
} else {
printf(" ✗ 父进程恢复原始组ID失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_process_group_permissions();
}

示例5:组权限安全测试

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>

/**
* 组权限安全测试
*/
int demo_group_security_testing() {
gid_t original_real_gid, original_effective_gid;
gid_t test_gid = 1000; // 测试组ID

printf("=== 组权限安全测试 ===\n");

// 保存原始状态
original_real_gid = getgid();
original_effective_gid = getegid();

printf("测试开始 - 原始组ID状态:\n");
printf(" 真实组ID: %d\n", original_real_gid);
printf(" 有效组ID: %d\n", original_effective_gid);

// 测试1: 正常组切换
printf("\n=== 测试1: 正常组切换 ===\n");
int result = setregid(test_gid, test_gid);
if (result == 0) {
printf("✓ 正常组切换成功\n");
printf(" 当前真实组ID: %d\n", getgid());
printf(" 当前有效组ID: %d\n", getegid());

// 恢复
result = setregid(original_real_gid, original_effective_gid);
if (result == 0) {
printf("✓ 成功恢复原始状态\n");
} else {
printf("✗ 恢复原始状态失败: %s\n", strerror(errno));
}
} else {
printf("✗ 正常组切换失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 预期结果:没有足够权限\n");
}
}

// 测试2: 部分组切换
printf("\n=== 测试2: 部分组切换 ===\n");
result = setregid(-1, test_gid);
if (result == 0) {
printf("✓ 部分组切换成功 (仅改变有效组ID)\n");
printf(" 当前真实组ID: %d\n", getgid());
printf(" 当前有效组ID: %d\n", getegid());

// 恢复有效组ID
result = setregid(-1, original_effective_gid);
if (result == 0) {
printf("✓ 成功恢复有效组ID\n");
} else {
printf("✗ 恢复有效组ID失败: %s\n", strerror(errno));
}
} else {
printf("✗ 部分组切换失败: %s\n", strerror(errno));
}

// 测试3: 无效组ID
printf("\n=== 测试3: 无效组ID ===\n");
gid_t invalid_gid = 999999; // 很可能不存在的组ID
result = setregid(invalid_gid, invalid_gid);
if (result == 0) {
printf("✗ 无效组ID设置成功 (这不应该发生)\n");
} else {
printf("✓ 无效组ID设置失败: %s\n", strerror(errno));
if (errno == EINVAL) {
printf(" 正确:组ID无效\n");
}
}

// 测试4: 权限验证
printf("\n=== 测试4: 权限验证 ===\n");
uid_t uid = getuid();
uid_t euid = geteuid();

printf("当前用户权限状态:\n");
printf(" 真实用户ID: %d", uid);
struct passwd *pwd = getpwuid(uid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

printf(" 有效用户ID: %d", euid);
pwd = getpwuid(euid);
if (pwd) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");

if (uid == 0 || euid == 0) {
printf("✓ 具有root权限,可以进行组切换测试\n");
} else {
printf("✗ 没有root权限,组切换可能受限\n");
}

// 测试5: 状态一致性检查
printf("\n=== 测试5: 状态一致性检查 ===\n");
gid_t final_real_gid = getgid();
gid_t final_effective_gid = getegid();

printf("最终组ID状态:\n");
printf(" 真实组ID: %d\n", final_real_gid);
printf(" 有效组ID: %d\n", final_effective_gid);

if (final_real_gid == original_real_gid &&
final_effective_gid == original_effective_gid) {
printf("✓ 组ID状态一致性检查通过\n");
} else {
printf("✗ 组ID状态不一致,可能存在安全风险\n");
}

// 显示安全建议
printf("\n=== 安全建议 ===\n");
printf("1. 始终验证setregid的返回值\n");
printf("2. 保存原始组ID状态以便恢复\n");
printf("3. 使用最小权限原则\n");
printf("4. 记录组权限变更日志\n");
printf("5. 避免频繁的组权限切换\n");
printf("6. 在特权操作后及时降权\n");

return 0;
}

int main() {
return demo_group_security_testing();
}

setregid 使用注意事项

系统要求:

内核版本: 支持组ID管理的Linux内核

权限要求: 需要CAP_SETGID能力或root权限

架构支持: 支持所有主流架构

参数规则:

-1表示不改变: rgid或egid为-1时保持原值

权限限制: 普通用户只能在允许范围内切换

真实组ID限制: 真实组ID只能向有效组ID降低

错误处理:

EPERM: 权限不足(需要CAP_SETGID或root权限)

EINVAL: 组ID无效

EAGAIN: 资源暂时不可用

EFAULT: 指针参数无效

安全考虑:

权限提升: 可能导致权限提升风险

审计日志: 建议记录权限变更操作

最小权限: 遵循最小权限原则

状态恢复: 及时恢复原始权限状态

最佳实践:

权限检查: 执行前检查是否具有足够权限

参数验证: 验证参数的有效性和安全性

错误处理: 妥善处理各种错误情况

状态保存: 保存原始状态以便恢复

日志记录: 记录权限变更操作

及时恢复: 使用完特殊权限后及时恢复

组ID类型说明

真实组ID (Real Group ID):

  • 含义: 进程的实际所有者组ID

  • 用途: 标识进程的真正归属

  • 修改限制: 只能向有效组ID降低

有效组ID (Effective Group ID):

  • 含义: 当前用于权限检查的组ID

  • 用途: 决定进程当前的权限

  • 修改限制: 可以设置为真实组ID或保存的组ID

保存的组ID (Saved Group ID):

  • 含义: 进程启动时的有效组ID副本

  • 用途: 用于权限恢复

  • 修改时机: 通常在exec时设置

常见使用场景

1. 服务权限管理:

1
2
3
// 服务启动时降低权限
setregid(service_gid, service_gid);

2. 安全沙箱:

1
2
3
// 创建受限环境时设置适当的组权限
setregid(restricted_gid, restricted_gid);

3. 权限切换:

1
2
3
4
5
// 临时获取特定组权限
setregid(-1, target_gid); // 仅改变有效组ID
// 执行需要权限的操作
setregid(-1, original_gid); // 恢复有效组ID

权限检查规则

setregid权限要求:

超级用户: 可以设置任意组ID

普通用户: 只能设置为以下值之一:

  • 当前真实组ID

  • 当前有效组ID

  • 保存的组ID

安全限制:

真实组ID: 只能向有效组ID降低

有效组ID: 可以自由设置(在允许范围内)

审计: 系统通常会记录权限变更

总结

setregid 是Linux系统中重要的组权限管理函数,提供了:

组权限控制: 精确控制进程的组权限

灵活配置: 支持同时设置真实和有效组ID

安全机制: 通过权限检查保证系统安全

标准兼容: 符合POSIX标准

通过合理使用 setregid,可以实现细粒度的权限控制,构建更加安全可靠的系统应用。在实际应用中,需要注意权限要求、错误处理和安全最佳实践。

https://www.calcguide.tech/2025/08/11/setregid系统调用及示例/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/

setresgid系统调用及示例setresgid/setresuid/setreuid系统调用及示例

setresgid/setresuid/setreuid 函数详解

  1. 函数介绍

这三个函数是Linux系统调用,用于设置进程的用户ID和组ID。它们提供了更灵活的权限控制机制,允许同时设置真实ID、有效ID和保存的ID。了解setresgid/setresuid/setreuid系统调用及示例,掌握Linux进程权限管理技巧。

关键词:setresgid setresuid setreuid 系统调用详解, setresgid setresuid setreuid 函数用法, linux setresgid setresuid setreuid 示例, 系统调用 setresgid setresuid setreuid 说明, setresgid setresuid setreuid 功能解释, linux 系统调用 setresgid setresuid setreuid, setresgid setresuid setreuid 使用教程, setresgid setresuid setreuid 参数说明, linux 中 setresgid setresuid setreuid 应用, 系统调用 setresgid setresuid setreuid 示例代码

  • setresuid: 设置真实用户ID、有效用户ID和保存的用户ID

  • setresgid: 设置真实组ID、有效组ID和保存的组ID

  • setreuid: 设置真实用户ID和有效用户ID(简化版本)

  1. 函数原型
1
2
3
4
5
6
7
8
9
10
#include <unistd.h>

// 设置用户ID
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setreuid(uid_t ruid, uid_t euid);

// 设置组ID
int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
int setregid(gid_t rgid, gid_t egid);

  1. 功能

这些函数允许进程精确控制其用户和组权限,支持复杂的权限切换场景,是构建安全应用程序的重要工具。

  1. 参数
  • ruid/rgid: 真实用户ID/组ID(-1表示不改变)

  • euid/egid: 有效用户ID/组ID(-1表示不改变)

  • suid/sgid: 保存的用户ID/组ID(-1表示不改变,仅setresuid/setresgid)

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

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • setuid/setgid: 设置有效ID

  • seteuid/setegid: 设置有效ID

  • getuid/getgid: 获取真实ID

  • geteuid/getegid: 获取有效ID

  1. 示例代码

示例1:基础setresuid/setresgid使用

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
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
* 显示用户和组ID信息
*/
void show_user_group_ids() {
uid_t ruid = getuid();
uid_t euid = geteuid();
gid_t rgid = getgid();
gid_t egid = getegid();

printf("=== 用户和组ID信息 ===\n");
printf("真实用户ID: %d", ruid);
struct passwd *pwd = getpwuid(ruid);
if (pwd) printf(" (%s)", pwd->pw_name);
printf("\n");

printf("有效用户ID: %d", euid);
pwd = getpwuid(euid);
if (pwd) printf(" (%s)", pwd->pw_name);
printf("\n");

printf("真实组ID: %d", rgid);
struct group *grp = getgrgid(rgid);
if (grp) printf(" (%s)", grp->gr_name);
printf("\n");

printf("有效组ID: %d", egid);
grp = getgrgid(egid);
if (grp) printf(" (%s)", grp->gr_name);
printf("\n\n");
}

/**
* 演示基础setresuid/setresgid使用
*/
int demo_basic_usage() {
uid_t orig_ruid, orig_euid;
gid_t orig_rgid, orig_egid;
uid_t test_uid = 1000;
gid_t test_gid = 1000;

printf("=== 基础setresuid/setresgid使用示例 ===\n");

// 保存原始ID
orig_ruid = getuid();
orig_euid = geteuid();
orig_rgid = getgid();
orig_egid = getegid();

printf("1. 原始ID信息:\n");
show_user_group_ids();

// 演示setresuid
printf("2. 演示setresuid:\n");
printf(" 尝试设置真实用户ID=%d, 有效用户ID=%d, 保存用户ID=%d\n",
test_uid, test_uid, test_uid);

int result = setresuid(test_uid, test_uid, test_uid);
if (result == 0) {
printf(" ✓ setresuid成功\n");
printf(" 新用户ID信息:\n");
show_user_group_ids();
} else {
printf(" ✗ setresuid失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要root权限\n");
}
}

// 演示setresgid
printf("3. 演示setresgid:\n");
printf(" 尝试设置真实组ID=%d, 有效组ID=%d, 保存组ID=%d\n",
test_gid, test_gid, test_gid);

result = setresgid(test_gid, test_gid, test_gid);
if (result == 0) {
printf(" ✓ setresgid成功\n");
printf(" 新组ID信息:\n");
show_user_group_ids();
} else {
printf(" ✗ setresgid失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要root权限\n");
}
}

// 恢复原始ID
printf("4. 恢复原始ID:\n");
result = setresuid(orig_ruid, orig_euid, orig_ruid);
if (result == 0) {
printf(" ✓ 恢复用户ID成功\n");
} else {
printf(" ✗ 恢复用户ID失败: %s\n", strerror(errno));
}

result = setresgid(orig_rgid, orig_egid, orig_rgid);
if (result == 0) {
printf(" ✓ 恢复组ID成功\n");
} else {
printf(" ✗ 恢复组ID失败: %s\n", strerror(errno));
}

printf(" 最终ID信息:\n");
show_user_group_ids();

return 0;
}

int main() {
return demo_basic_usage();
}

示例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
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
#include <fcntl.h>

/**
* 安全权限切换类
*/
typedef struct {
uid_t saved_ruid;
uid_t saved_euid;
uid_t saved_suid;
gid_t saved_rgid;
gid_t saved_egid;
gid_t saved_sgid;
int valid;
} security_context_t;

/**
* 保存当前安全上下文
*/
int save_security_context(security_context_t *ctx) {
ctx->saved_ruid = getuid();
ctx->saved_euid = geteuid();
ctx->saved_rgid = getgid();
ctx->saved_egid = getegid();

// 保存的ID通常需要通过其他方式获取
ctx->saved_suid = ctx->saved_euid;
ctx->saved_sgid = ctx->saved_egid;
ctx->valid = 1;

printf("保存安全上下文:\n");
printf(" 真实UID: %d, 有效UID: %d, 保存UID: %d\n",
ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
printf(" 真实GID: %d, 有效GID: %d, 保存GID: %d\n",
ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);

return 0;
}

/**
* 恢复安全上下文
*/
int restore_security_context(security_context_t *ctx) {
if (!ctx->valid) {
printf("无效的安全上下文\n");
return -1;
}

printf("恢复安全上下文:\n");
printf(" 目标UID: %d/%d/%d\n", ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
printf(" 目标GID: %d/%d/%d\n", ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);

int result = setresuid(ctx->saved_ruid, ctx->saved_euid, ctx->saved_suid);
if (result != 0) {
printf("恢复UID失败: %s\n", strerror(errno));
return -1;
}

result = setresgid(ctx->saved_rgid, ctx->saved_egid, ctx->saved_sgid);
if (result != 0) {
printf("恢复GID失败: %s\n", strerror(errno));
return -1;
}

printf("安全上下文恢复成功\n");
return 0;
}

/**
* 演示权限切换和恢复
*/
int demo_privilege_switching() {
security_context_t ctx = {0};
uid_t current_uid = getuid();

printf("=== 权限切换和恢复演示 ===\n");

// 保存当前安全上下文
if (save_security_context(&ctx) != 0) {
printf("保存安全上下文失败\n");
return -1;
}

// 显示当前状态
printf("\n当前用户权限状态:\n");
show_user_group_ids();

// 检查权限
if (current_uid == 0) {
printf("✓ 当前具有root权限,可以进行权限切换测试\n");
} else {
printf("✗ 当前没有root权限,权限切换可能受限\n");
}

// 演示临时权限提升
printf("\n=== 临时权限提升演示 ===\n");
if (current_uid == 0) {
// 如果已经是root,演示降权
uid_t target_uid = 1000; // 普通用户ID
gid_t target_gid = 1000; // 普通组ID

printf("尝试降权到用户 %d, 组 %d\n", target_uid, target_gid);

int result = setresuid(target_uid, target_uid, target_uid);
if (result == 0) {
printf("✓ 用户权限降级成功\n");
result = setresgid(target_gid, target_gid, target_gid);
if (result == 0) {
printf("✓ 组权限降级成功\n");
show_user_group_ids();

// 模拟降权后的工作
printf("执行降权后的操作...\n");
sleep(2);

// 恢复权限
printf("恢复原始权限:\n");
if (restore_security_context(&ctx) == 0) {
show_user_group_ids();
printf("✓ 权限成功恢复\n");
} else {
printf("✗ 权限恢复失败\n");
}
} else {
printf("✗ 组权限降级失败: %s\n", strerror(errno));
}
} else {
printf("✗ 用户权限降级失败: %s\n", strerror(errno));
}
} else {
printf("非root用户权限切换演示:\n");

// 演示部分权限切换(仅改变有效ID)
printf("尝试仅改变有效用户ID:\n");
int result = setreuid(-1, current_uid); // -1表示不改变真实ID
if (result == 0) {
printf("✓ 有效用户ID设置成功\n");
} else {
printf("✗ 有效用户ID设置失败: %s\n", strerror(errno));
}

show_user_group_ids();
}

// 验证最终状态
printf("\n=== 最终状态验证 ===\n");
uid_t final_ruid = getuid();
uid_t final_euid = geteuid();
gid_t final_rgid = getgid();
gid_t final_egid = getegid();

printf("最终ID状态:\n");
printf(" 真实UID: %d (原始: %d)\n", final_ruid, ctx.saved_ruid);
printf(" 有效UID: %d (原始: %d)\n", final_euid, ctx.saved_euid);
printf(" 真实GID: %d (原始: %d)\n", final_rgid, ctx.saved_rgid);
printf(" 有效GID: %d (原始: %d)\n", final_egid, ctx.saved_egid);

if (final_ruid == ctx.saved_ruid && final_euid == ctx.saved_euid &&
final_rgid == ctx.saved_rgid && final_egid == ctx.saved_egid) {
printf("✓ ID状态验证通过\n");
} else {
printf("✗ ID状态验证失败\n");
}

return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
return demo_privilege_switching();
}

示例3:setreuid/setregid使用

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>

/**
* 演示setreuid/setregid使用
*/
int demo_simplified_functions() {
uid_t orig_ruid, orig_euid;
gid_t orig_rgid, orig_egid;
uid_t test_uid = 1000;
gid_t test_gid = 1000;

printf("=== setreuid/setregid 使用演示 ===\n");

// 保存原始ID
orig_ruid = getuid();
orig_euid = geteuid();
orig_rgid = getgid();
orig_egid = getegid();

printf("原始ID信息:\n");
show_user_group_ids();

// 演示setreuid(仅设置真实和有效用户ID)
printf("\n1. 演示setreuid:\n");
printf(" 尝试设置真实用户ID=%d, 有效用户ID=%d\n", test_uid, test_uid);

int result = setreuid(test_uid, test_uid);
if (result == 0) {
printf(" ✓ setreuid成功\n");
printf(" 新用户ID信息:\n");
show_user_group_ids();
} else {
printf(" ✗ setreuid失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要适当权限\n");
}
}

// 演示setregid(仅设置真实和有效组ID)
printf("\n2. 演示setregid:\n");
printf(" 尝试设置真实组ID=%d, 有效组ID=%d\n", test_gid, test_gid);

result = setregid(test_gid, test_gid);
if (result == 0) {
printf(" ✓ setregid成功\n");
printf(" 新组ID信息:\n");
show_user_group_ids();
} else {
printf(" ✗ setregid失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要适当权限\n");
}
}

// 演示部分设置(使用-1)
printf("\n3. 演示部分设置:\n");
printf(" 保持真实用户ID不变,仅设置有效用户ID\n");

result = setreuid(-1, orig_euid);
if (result == 0) {
printf(" ✓ 部分设置成功\n");
show_user_group_ids();
} else {
printf(" ✗ 部分设置失败: %s\n", strerror(errno));
}

// 恢复原始ID
printf("\n4. 恢复原始ID:\n");
result = setreuid(orig_ruid, orig_euid);
if (result == 0) {
printf(" ✓ 恢复用户ID成功\n");
} else {
printf(" ✗ 恢复用户ID失败: %s\n", strerror(errno));
}

result = setregid(orig_rgid, orig_egid);
if (result == 0) {
printf(" ✓ 恢复组ID成功\n");
} else {
printf(" ✗ 恢复组ID失败: %s\n", strerror(errno));
}

printf(" 最终ID信息:\n");
show_user_group_ids();

return 0;
}

/**
* 子进程演示
*/
void child_process_demo(int child_id) {
printf("子进程 %d 启动 (PID: %d)\n", child_id, getpid());

printf("子进程 %d 初始ID信息:\n", child_id);
show_user_group_ids();

// 子进程尝试修改ID
uid_t new_uid = 1000;
gid_t new_gid = 1000;

printf("子进程 %d 尝试修改ID:\n", child_id);

int result = setreuid(new_uid, new_uid);
if (result == 0) {
printf(" 子进程 %d setreuid成功\n", child_id);
} else {
printf(" 子进程 %d setreuid失败: %s\n", child_id, strerror(errno));
}

result = setregid(new_gid, new_gid);
if (result == 0) {
printf(" 子进程 %d setregid成功\n", child_id);
} else {
printf(" 子进程 %d setregid失败: %s\n", child_id, strerror(errno));
}

printf("子进程 %d 最终ID信息:\n", child_id);
show_user_group_ids();

sleep(2);
printf("子进程 %d 结束\n", child_id);
exit(0);
}

/**
* 多进程ID管理演示
*/
int demo_multiprocess_id_management() {
pid_t child1_pid, child2_pid;

printf("=== 多进程ID管理演示 ===\n");

printf("父进程初始ID信息:\n");
show_user_group_ids();

// 创建子进程1
child1_pid = fork();
if (child1_pid == 0) {
child_process_demo(1);
} else if (child1_pid > 0) {
printf("创建子进程1: PID=%d\n", child1_pid);
} else {
perror("创建子进程1失败");
return -1;
}

// 创建子进程2
child2_pid = fork();
if (child2_pid == 0) {
child_process_demo(2);
} else if (child2_pid > 0) {
printf("创建子进程2: PID=%d\n", child2_pid);
} else {
perror("创建子进程2失败");
kill(child1_pid, SIGKILL);
return -1;
}

// 父进程也尝试修改ID
printf("\n父进程尝试修改ID:\n");
uid_t parent_new_uid = 2000;
gid_t parent_new_gid = 2000;

int result = setreuid(parent_new_uid, parent_new_uid);
if (result == 0) {
printf("父进程setreuid成功\n");
} else {
printf("父进程setreuid失败: %s\n", strerror(errno));
}

result = setregid(parent_new_gid, parent_new_gid);
if (result == 0) {
printf("父进程setregid成功\n");
} else {
printf("父进程setregid失败: %s\n", strerror(errno));
}

printf("父进程修改后ID信息:\n");
show_user_group_ids();

// 等待子进程结束
printf("等待子进程结束:\n");
int status;
pid_t finished_pid;

while ((finished_pid = wait(&status)) > 0) {
printf("子进程 %d 已结束,退出状态: %d\n", finished_pid, WEXITSTATUS(status));
}

return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
printf("=== setreuid/setregid vs setresuid/setresgid 比较 ===\n");
printf("setreuid/setregid: 简化版本,只设置真实和有效ID\n");
printf("setresuid/setresgid: 完整版本,设置真实、有效和保存的ID\n\n");

// 先运行简化函数演示
demo_simplified_functions();

printf("\n" "=" * 50 "\n");

// 再运行多进程演示
demo_multiprocess_id_management();

return 0;
}

示例4:安全权限管理

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
* 权限管理器
*/
typedef struct {
uid_t original_ruid;
uid_t original_euid;
uid_t original_suid;
gid_t original_rgid;
gid_t original_egid;
gid_t original_sgid;
int initialized;
} privilege_manager_t;

/**
* 初始化权限管理器
*/
int init_privilege_manager(privilege_manager_t *pm) {
pm->original_ruid = getuid();
pm->original_euid = geteuid();
pm->original_rgid = getgid();
pm->original_egid = getegid();

// 保存的ID通常等于有效ID(在程序启动时)
pm->original_suid = pm->original_euid;
pm->original_sgid = pm->original_egid;
pm->initialized = 1;

printf("权限管理器初始化完成\n");
printf("原始UID: %d/%d/%d\n", pm->original_ruid, pm->original_euid, pm->original_suid);
printf("原始GID: %d/%d/%d\n", pm->original_rgid, pm->original_egid, pm->original_sgid);

return 0;
}

/**
* 临时提升到root权限
*/
int escalate_to_root(privilege_manager_t *pm) {
if (!pm->initialized) {
printf("权限管理器未初始化\n");
return -1;
}

printf("尝试提升到root权限...\n");

// 使用setresuid设置所有ID为0(root)
int result = setresuid(0, 0, 0);
if (result == 0) {
printf("✓ 成功提升到root权限\n");
show_user_group_ids();

result = setresgid(0, 0, 0);
if (result == 0) {
printf("✓ 成功提升到root组权限\n");
} else {
printf("✗ 提升组权限失败: %s\n", strerror(errno));
// 尝试恢复用户权限
setresuid(pm->original_ruid, pm->original_euid, pm->original_suid);
}
} else {
printf("✗ 提升用户权限失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 当前没有足够权限进行提升\n");
}
}

return result;
}

/**
* 降权到指定用户
*/
int drop_to_user(privilege_manager_t *pm, uid_t target_uid, gid_t target_gid) {
if (!pm->initialized) {
printf("权限管理器未初始化\n");
return -1;
}

printf("尝试降权到用户 %d, 组 %d...\n", target_uid, target_gid);

int result = setresuid(target_uid, target_uid, pm->original_suid);
if (result == 0) {
printf("✓ 用户权限降级成功\n");

result = setresgid(target_gid, target_gid, pm->original_sgid);
if (result == 0) {
printf("✓ 组权限降级成功\n");
show_user_group_ids();
} else {
printf("✗ 组权限降级失败: %s\n", strerror(errno));
}
} else {
printf("✗ 用户权限降级失败: %s\n", strerror(errno));
}

return result;
}

/**
* 恢复原始权限
*/
int restore_original_privileges(privilege_manager_t *pm) {
if (!pm->initialized) {
printf("权限管理器未初始化\n");
return -1;
}

printf("恢复原始权限...\n");

int result = setresuid(pm->original_ruid, pm->original_euid, pm->original_suid);
if (result == 0) {
printf("✓ 用户权限恢复成功\n");

result = setresgid(pm->original_rgid, pm->original_egid, pm->original_sgid);
if (result == 0) {
printf("✓ 组权限恢复成功\n");
show_user_group_ids();
} else {
printf("✗ 组权限恢复失败: %s\n", strerror(errno));
}
} else {
printf("✗ 用户权限恢复失败: %s\n", strerror(errno));
}

return result;
}

/**
* 演示安全权限管理
*/
int demo_secure_privilege_management() {
privilege_manager_t pm = {0};
uid_t current_uid = getuid();

printf("=== 安全权限管理演示 ===\n");

// 初始化权限管理器
if (init_privilege_manager(&pm) != 0) {
printf("初始化权限管理器失败\n");
return -1;
}

printf("\n当前权限状态:\n");
show_user_group_ids();

// 根据当前权限级别演示不同场景
if (current_uid == 0) {
printf("✓ 当前运行在root权限下\n");

// 演示降权操作
printf("\n=== 降权操作演示 ===\n");
uid_t target_uid = 1000; // 普通用户
gid_t target_gid = 1000; // 普通组

if (drop_to_user(&pm, target_uid, target_gid) == 0) {
printf("降权操作成功\n");

// 模拟降权后的操作
printf("执行降权后的安全操作...\n");
sleep(2);

// 恢复原始权限
printf("\n=== 权限恢复演示 ===\n");
if (restore_original_privileges(&pm) == 0) {
printf("权限成功恢复到原始状态\n");
} else {
printf("权限恢复失败\n");
}
}
} else {
printf("✗ 当前运行在普通用户权限下\n");

// 演示权限检查和安全操作
printf("\n=== 权限检查演示 ===\n");

// 尝试提升权限(应该失败)
printf("尝试提升权限:\n");
int result = setresuid(0, 0, 0);
if (result == 0) {
printf("✓ 权限提升成功(意外情况)\n");
} else {
printf("✗ 权限提升失败: %s\n", strerror(errno));
printf(" 这是预期的行为\n");
}

// 演示部分权限切换
printf("\n演示部分权限切换:\n");
result = setreuid(-1, current_uid); // 仅改变有效ID
if (result == 0) {
printf("✓ 部分权限切换成功\n");
} else {
printf("✗ 部分权限切换失败: %s\n", strerror(errno));
}
}

// 安全审计
printf("\n=== 安全审计 ===\n");
uid_t final_ruid = getuid();
uid_t final_euid = geteuid();
gid_t final_rgid = getgid();
gid_t final_egid = getegid();

printf("最终权限状态:\n");
printf(" 真实UID: %d (原始: %d)\n", final_ruid, pm.original_ruid);
printf(" 有效UID: %d (原始: %d)\n", final_euid, pm.original_euid);
printf(" 真实GID: %d (原始: %d)\n", final_rgid, pm.original_rgid);
printf(" 有效GID: %d (原始: %d)\n", final_egid, pm.original_egid);

if (final_ruid == pm.original_ruid && final_euid == pm.original_euid &&
final_rgid == pm.original_rgid && final_egid == pm.original_egid) {
printf("✓ 权限状态审计通过\n");
} else {
printf("⚠ 权限状态发生变化,需要关注\n");
}

return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
return demo_secure_privilege_management();
}

示例5:权限切换最佳实践

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>

/**
* 权限切换安全检查
*/
typedef enum {
PRIV_CHECK_SUCCESS = 0,
PRIV_CHECK_PERMISSION_DENIED,
PRIV_CHECK_INVALID_ID,
PRIV_CHECK_OTHER_ERROR
} privilege_check_result_t;

/**
* 检查权限切换的可行性
*/
privilege_check_result_t check_privilege_switch_feasibility(uid_t target_uid, gid_t target_gid) {
uid_t current_ruid = getuid();
uid_t current_euid = geteuid();
gid_t current_rgid = getgid();
gid_t current_egid = getegid();

printf("权限切换可行性检查:\n");
printf(" 当前真实UID: %d, 有效UID: %d\n", current_ruid, current_euid);
printf(" 当前真实GID: %d, 有效GID: %d\n", current_rgid, current_egid);
printf(" 目标UID: %d, 目标GID: %d\n", target_uid, target_gid);

// 检查是否已经是目标权限
if (current_ruid == target_uid && current_euid == target_uid &&
current_rgid == target_gid && current_egid == target_gid) {
printf(" ✓ 已经是目标权限\n");
return PRIV_CHECK_SUCCESS;
}

// 检查是否具有足够权限
if (current_ruid == 0 || current_euid == 0) {
printf(" ✓ 具有root权限,可以切换到任意权限\n");
return PRIV_CHECK_SUCCESS;
}

// 检查普通用户的权限切换限制
if ((target_uid == current_ruid || target_uid == current_euid) &&
(target_gid == current_rgid || target_gid == current_egid)) {
printf(" ✓ 可以切换到目标权限(在允许范围内)\n");
return PRIV_CHECK_SUCCESS;
}

printf(" ✗ 权限不足,无法切换到目标权限\n");
return PRIV_CHECK_PERMISSION_DENIED;
}

/**
* 安全的权限切换函数
*/
int safe_privilege_switch(uid_t target_uid, gid_t target_gid, int preserve_saved_ids) {
uid_t current_ruid = getuid();
uid_t current_euid = geteuid();
gid_t current_rgid = getgid();
gid_t current_egid = getegid();

printf("执行安全权限切换:\n");
printf(" 从 UID %d/%d, GID %d/%d\n",
current_ruid, current_euid, current_rgid, current_egid);
printf(" 切换到 UID %d, GID %d\n", target_uid, target_gid);

// 使用setresuid/setresgid进行完整切换
uid_t new_ruid = target_uid;
uid_t new_euid = target_uid;
uid_t new_suid = preserve_saved_ids ? -1 : target_uid;

gid_t new_rgid = target_gid;
gid_t new_egid = target_gid;
gid_t new_sgid = preserve_saved_ids ? -1 : target_gid;

int result;

// 先切换组ID(通常更安全)
result = setresgid(new_rgid, new_egid, new_sgid);
if (result != 0) {
printf(" ✗ 组权限切换失败: %s\n", strerror(errno));
return -1;
}

// 再切换用户ID
result = setresuid(new_ruid, new_euid, new_suid);
if (result != 0) {
printf(" ✗ 用户权限切换失败: %s\n", strerror(errno));
// 尝试恢复组ID
setresgid(current_rgid, current_egid, -1);
return -1;
}

printf(" ✓ 权限切换成功\n");
show_user_group_ids();

return 0;
}

/**
* 权限切换回滚机制
*/
typedef struct {
uid_t saved_ruid;
uid_t saved_euid;
gid_t saved_rgid;
gid_t saved_egid;
int valid;
} rollback_info_t;

/**
* 保存回滚信息
*/
int save_rollback_info(rollback_info_t *rb) {
rb->saved_ruid = getuid();
rb->saved_euid = geteuid();
rb->saved_rgid = getgid();
rb->saved_egid = getegid();
rb->valid = 1;

printf("保存回滚信息: UID %d/%d, GID %d/%d\n",
rb->saved_ruid, rb->saved_euid, rb->saved_rgid, rb->saved_egid);

return 0;
}

/**
* 执行回滚
*/
int perform_rollback(rollback_info_t *rb) {
if (!rb->valid) {
printf("无效的回滚信息\n");
return -1;
}

printf("执行权限回滚:\n");
printf(" 目标 UID %d/%d, GID %d/%d\n",
rb->saved_ruid, rb->saved_euid, rb->saved_rgid, rb->saved_egid);

// 恢复权限
int result = setresuid(rb->saved_ruid, rb->saved_euid, -1);
if (result != 0) {
printf(" ✗ 用户权限回滚失败: %s\n", strerror(errno));
return -1;
}

result = setresgid(rb->saved_rgid, rb->saved_egid, -1);
if (result != 0) {
printf(" ✗ 组权限回滚失败: %s\n", strerror(errno));
return -1;
}

printf(" ✓ 权限回滚成功\n");
show_user_group_ids();

return 0;
}

/**
* 演示权限切换最佳实践
*/
int demo_best_practices() {
rollback_info_t rb = {0};
uid_t current_uid = getuid();

printf("=== 权限切换最佳实践演示 ===\n");

// 1. 保存当前状态用于回滚
printf("1. 保存当前权限状态\n");
if (save_rollback_info(&rb) != 0) {
printf("保存回滚信息失败\n");
return -1;
}

// 2. 检查权限切换可行性
printf("\n2. 检查权限切换可行性\n");
privilege_check_result_t check_result = check_privilege_switch_feasibility(1000, 1000);
if (check_result != PRIV_CHECK_SUCCESS) {
printf("权限切换不可行,跳过演示\n");
return 0;
}

// 3. 执行安全权限切换
printf("\n3. 执行安全权限切换\n");
if (current_uid == 0) {
// 如果是root,演示降权
printf("当前为root权限,演示安全降权:\n");
if (safe_privilege_switch(1000, 1000, 1) == 0) {
printf("✓ 安全降权成功\n");

// 模拟降权后的操作
printf("执行降权后的安全操作...\n");
sleep(2);

// 4. 执行权限回滚
printf("\n4. 执行权限回滚\n");
if (perform_rollback(&rb) == 0) {
printf("✓ 权限成功回滚到原始状态\n");
} else {
printf("✗ 权限回滚失败\n");
}
} else {
printf("✗ 安全降权失败\n");
}
} else {
// 如果是普通用户,演示权限检查
printf("当前为普通用户权限,演示权限检查:\n");

// 尝试切换到相同权限(应该成功)
if (safe_privilege_switch(current_uid, getgid(), 1) == 0) {
printf("✓ 相同权限切换成功\n");
}

// 尝试切换到不同权限(应该失败)
printf("尝试切换到不同权限:\n");
int result = setresuid(0, 0, 0); // 尝试切换到root
if (result == 0) {
printf("✗ 意外获得root权限\n");
} else {
printf("✓ 权限切换被正确拒绝: %s\n", strerror(errno));
}
}

// 5. 显示最佳实践总结
printf("\n=== 权限切换最佳实践总结 ===\n");
printf("1. ✓ 始终保存原始权限状态用于回滚\n");
printf("2. ✓ 在切换前检查权限可行性\n");
printf("3. ✓ 使用setresuid/setresgid进行完整控制\n");
printf("4. ✓ 先切换组权限,再切换用户权限\n");
printf("5. ✓ 及时回滚到原始权限状态\n");
printf("6. ✓ 记录权限切换操作日志\n");
printf("7. ✓ 遵循最小权限原则\n");
printf("8. ✓ 在特权操作后立即降权\n");

// 6. 安全审计
printf("\n=== 最终安全审计 ===\n");
uid_t final_ruid = getuid();
uid_t final_euid = geteuid();
gid_t final_rgid = getgid();
gid_t final_egid = getegid();

printf("最终权限状态:\n");
printf(" 真实UID: %d (原始: %d)\n", final_ruid, rb.saved_ruid);
printf(" 有效UID: %d (原始: %d)\n", final_euid, rb.saved_euid);
printf(" 真实GID: %d (原始: %d)\n", final_rgid, rb.saved_rgid);
printf(" 有效GID: %d (原始: %d)\n", final_egid, rb.saved_egid);

if (final_ruid == rb.saved_ruid && final_euid == rb.saved_euid &&
final_rgid == rb.saved_rgid && final_egid == rb.saved_egid) {
printf("✓ 权限状态已正确恢复\n");
} else {
printf("⚠ 权限状态异常,需要手动检查\n");
}

return 0;
}

// 辅助函数声明
void show_user_group_ids();

int main() {
return demo_best_practices();
}

使用注意事项

系统要求:

内核版本: 支持完整权限管理的Linux内核

权限要求: 需要相应权限才能执行权限切换

架构支持: 支持所有主流架构

权限规则:

超级用户: 可以设置任意ID

普通用户: 只能设置为允许的值

保存的ID: 通常在exec时设置

错误处理:

EPERM: 权限不足

EINVAL: ID值无效

EAGAIN: 资源暂时不可用

安全考虑:

权限提升: 可能导致安全风险

审计日志: 建议记录权限变更

最小权限: 遵循最小权限原则

状态恢复: 及时恢复原始状态

最佳实践:

保存状态: 执行前保存原始权限状态

检查权限: 验证是否有足够权限

错误处理: 妥善处理各种错误情况

及时恢复: 使用完特权后及时恢复

日志记录: 记录权限变更操作

函数对比

函数参数数量功能适用场景setreuid2个设置真实和有效用户ID简单权限切换setregid2个设置真实和有效组ID简单权限切换setresuid3个设置真实、有效和保存用户ID完整权限控制setresgid3个设置真实、有效和保存组ID完整权限控制

总结

setresuid/setresgid/setreuid/setregid 提供了灵活的权限管理机制:

精确控制: 可以精确控制进程的权限状态

安全机制: 通过权限检查保证系统安全

灵活使用: 支持部分参数设置(使用-1)

标准兼容: 符合POSIX标准

在实际应用中,需要:

  • 遵循安全最佳实践

  • 妥善处理错误情况

  • 及时恢复原始权限状态

  • 记录权限变更日志

https://www.calcguide.tech/2025/08/11/setresgid系统调用及示例/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/