Linux调度器函数详解

Linux调度器函数详解

  1. 概述

sched_* 函数族是 Linux 系统中用于进程调度控制的一系列系统调用。可以把调度器想象成”CPU 时间片的分配管理员”——它决定哪个进程什么时候获得 CPU 时间,就像交通警察决定哪辆车什么时候可以通过路口一样。

这些函数提供了对进程调度策略、优先级、CPU 亲和性等方面的精细控制,是实现高性能、实时应用的重要工具。

  1. sched_* 函数列表

2.1 基础调度函数

  • sched_yield: 让出当前 CPU 时间片

  • sched_getscheduler: 获取进程调度策略

  • sched_setscheduler: 设置进程调度策略

  • sched_getparam: 获取进程调度参数

  • sched_setparam: 设置进程调度参数

2.2 CPU 亲和性函数

  • sched_getaffinity: 获取进程 CPU 亲和性

  • sched_setaffinity: 设置进程 CPU 亲和性

2.3 优先级函数

  • sched_get_priority_min: 获取指定策略的最小优先级

  • sched_get_priority_max: 获取指定策略的最大优先级

  • sched_rr_get_interval: 获取轮转调度的时间片间隔

2.4 CPU 信息函数

  • sched_getcpu: 获取当前 CPU 编号
  1. 调度策略详解

3.1 调度策略类型

策略值说明SCHED_OTHER0默认分时调度策略(CFS)SCHED_FIFO1先进先出实时调度策略SCHED_RR2轮转实时调度策略SCHED_BATCH3批处理调度策略SCHED_IDLE5空闲调度策略SCHED_DEADLINE6截止时间调度策略

3.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
#include <sched.h>
#include <stdio.h>

void show_scheduling_policies() {
printf("=== 调度策略特点 ===\n");
printf("SCHED_OTHER (默认):\n");
printf(" • 用于普通进程\n");
printf(" • 完全公平调度器 (CFS)\n");
printf(" • 动态优先级调整\n");
printf(" • 适合交互式应用\n\n");

printf("SCHED_FIFO (实时 FIFO):\n");
printf(" • 实时调度策略\n");
printf(" • 高优先级进程一直运行直到阻塞或主动让出\n");
printf(" • 不会时间片轮转\n");
printf(" • 需要 root 权限\n\n");

printf("SCHED_RR (实时轮转):\n");
printf(" • 实时调度策略\n");
printf(" • 时间片轮转调度\n");
printf(" • 相同优先级进程轮流执行\n");
printf(" • 需要 root 权限\n\n");

printf("SCHED_BATCH (批处理):\n");
printf(" • 用于批处理任务\n");
printf(" • 减少唤醒频率\n");
printf(" • 适合长时间运行的非交互任务\n\n");

printf("SCHED_IDLE (空闲):\n");
printf(" • 用于极低优先级任务\n");
printf(" • 只在系统空闲时运行\n");
printf(" • 不影响其他进程\n\n");

printf("SCHED_DEADLINE (截止时间):\n");
printf(" • 基于截止时间的调度\n");
printf(" • 确保任务按时完成\n");
printf(" • 需要特殊配置\n");
printf(" • Linux 3.14+\n\n");
}

  1. 函数详细介绍

4.1 sched_yield - 让出 CPU 时间片

函数原型

1
2
3
#include <sched.h>
int sched_yield(void);

功能

让出当前进程的 CPU 时间片,允许其他同优先级的进程运行。

参数

无参数

返回值

  • 成功: 返回 0

  • 失败: 返回 -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
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <time.h>
#include <errno.h>

// 消耗 CPU 的函数
void consume_cpu(int seconds) {
time_t start = time(NULL);
volatile long sum = 0;

while (time(NULL) - start < seconds) {
for (long i = 0; i < 1000000; i++) {
sum += i;
}
}
}

int main() {
printf("=== sched_yield 示例 ===\n\n");

// 获取当前进程 ID 和 CPU 信息
printf("进程 ID: %d\n", getpid());
printf("父进程 ID: %d\n", getppid());

// 获取当前 CPU 编号(如果有支持)
int current_cpu = sched_getcpu();
if (current_cpu != -1) {
printf("当前 CPU: %d\n", current_cpu);
} else {
printf("无法获取当前 CPU 信息\n");
}

printf("\n1. 不使用 sched_yield 的 CPU 消耗:\n");
printf(" 开始消耗 CPU 时间...\n");

time_t start_time = time(NULL);
consume_cpu(3); // 消耗 3 秒 CPU 时间
time_t end_time = time(NULL);

printf(" 消耗完成,用时 %ld 秒\n", end_time - start_time);

printf("\n2. 使用 sched_yield 的 CPU 消耗:\n");
printf(" 开始消耗 CPU 时间并让出时间片...\n");

start_time = time(NULL);
time_t yield_start = time(NULL);

while (time(NULL) - yield_start < 3) {
// 消耗一些 CPU 时间
volatile long sum = 0;
for (long i = 0; i < 500000; i++) {
sum += i;
}

// 让出 CPU 时间片
if (sched_yield() == -1) {
perror("sched_yield 失败");
}
}

end_time = time(NULL);
printf(" 消耗完成,用时 %ld 秒\n", end_time - start_time);

printf("\n3. sched_yield 的实际效果:\n");
printf(" • 允许其他同优先级进程运行\n");
printf(" • 改善系统响应性\n");
printf(" • 减少饥饿现象\n");
printf(" • 适合协作式多任务\n");

printf("\n=== sched_yield 使用场景 ===\n");
printf("1. 长时间运行的循环\n");
printf("2. 忙等待循环\n");
printf("3. 协作式多任务\n");
printf("4. 实时应用中的主动让出\n");
printf("5. 负载均衡\n");

printf("\n=== 注意事项 ===\n");
printf("1. 不保证立即切换到其他进程\n");
printf("2. 只影响同优先级进程\n");
printf("3. 过度使用可能影响性能\n");
printf("4. 不是强制调度切换\n");
printf("5. 应该谨慎使用\n");

return 0;
}

4.2 sched_getscheduler/sched_setscheduler - 调度策略控制

函数原型

1
2
3
4
5
#include <sched.h>

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

sched_param 结构体

1
2
3
4
5
struct sched_param {
int sched_priority; // 调度优先级
// 对于 SCHED_DEADLINE,还有额外字段
};

功能

  • sched_getscheduler: 获取指定进程的调度策略

  • sched_setscheduler: 设置指定进程的调度策略和参数

参数

  • pid: 进程 ID(0 表示当前进程)

  • policy: 调度策略

  • param: 指向调度参数的指针

返回值

  • sched_getscheduler: 成功返回调度策略,失败返回 -1

  • sched_setscheduler: 成功返回 0,失败返回 -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
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>

// 将调度策略转换为字符串
const char* policy_to_string(int policy) {
switch (policy) {
case SCHED_OTHER: return "SCHED_OTHER (默认)";
case SCHED_FIFO: return "SCHED_FIFO (实时FIFO)";
case SCHED_RR: return "SCHED_RR (实时轮转)";
case SCHED_BATCH: return "SCHED_BATCH (批处理)";
case SCHED_IDLE: return "SCHED_IDLE (空闲)";
case SCHED_DEADLINE: return "SCHED_DEADLINE (截止时间)";
default: return "未知策略";
}
}

// 显示进程调度信息
void show_process_scheduling_info(pid_t pid, const char *description) {
printf("=== %s ===\n", description);

if (pid == 0) {
printf("进程: 当前进程 (PID: %d)\n", getpid());
} else {
printf("进程: PID %d\n", pid);
}

// 获取调度策略
int policy = sched_getscheduler(pid);
if (policy == -1) {
perror("获取调度策略失败");
return;
}

printf("调度策略: %s\n", policy_to_string(policy));

// 获取调度参数
struct sched_param param;
if (sched_getparam(pid, &param) == 0) {
printf("调度优先级: %d\n", param.sched_priority);

// 显示优先级范围
int min_priority = sched_get_priority_min(policy);
int max_priority = sched_get_priority_max(policy);

if (min_priority != -1 && max_priority != -1) {
printf("优先级范围: %d - %d\n", min_priority, max_priority);

if (param.sched_priority < min_priority || param.sched_priority > max_priority) {
printf("⚠ 当前优先级超出范围\n");
}
}
} else {
perror("获取调度参数失败");
}

// 获取进程优先级
errno = 0;
int nice_value = getpriority(PRIO_PROCESS, pid);
if (errno == 0) {
printf("Nice 值: %d\n", nice_value);
}

printf("\n");
}

// 设置调度策略
int set_process_scheduling_policy(pid_t pid, int policy, int priority) {
struct sched_param param;
param.sched_priority = priority;

printf("设置进程调度策略:\n");
printf(" 进程 ID: %d\n", pid ? pid : getpid());
printf(" 调度策略: %s\n", policy_to_string(policy));
printf(" 调度优先级: %d\n", priority);

if (sched_setscheduler(pid, policy, &param) == 0) {
printf("✓ 调度策略设置成功\n");
return 0;
} else {
switch (errno) {
case EPERM:
printf("✗ 权限不足: 需要 root 权限设置实时策略\n");
break;
case EINVAL:
printf("✗ 参数无效: 策略或优先级无效\n");
break;
case ESRCH:
printf("✗ 进程不存在\n");
break;
default:
printf("✗ 设置失败: %s\n", strerror(errno));
break;
}
return -1;
}
}

int main() {
printf("=== sched_getscheduler/sched_setscheduler 示例 ===\n\n");

// 显示当前用户信息
printf("用户信息:\n");
printf(" UID: %d\n", getuid());
printf(" EUID: %d\n", geteuid());
printf(" GID: %d\n", getgid());
printf(" EGID: %d\n", getegid());
printf("\n");

// 显示初始调度信息
show_process_scheduling_info(0, "初始调度信息");

// 显示各种调度策略的优先级范围
printf("=== 各种调度策略的优先级范围 ===\n");

int policies&#91;] = {SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_IDLE};
int num_policies = sizeof(policies) / sizeof(policies&#91;0]);

for (int i = 0; i < num_policies; i++) {
int min_priority = sched_get_priority_min(policies&#91;i]);
int max_priority = sched_get_priority_max(policies&#91;i]);

printf("%-20s: 最小优先级 %3d, 最大优先级 %3d\n",
policy_to_string(policies&#91;i]), min_priority, max_priority);
}
printf("\n");

// 演示调度策略设置(需要 root 权限)
printf("=== 调度策略设置演示 ===\n");

// 1. 尝试设置 SCHED_FIFO(需要 root 权限)
printf("1. 尝试设置 SCHED_FIFO 策略:\n");
if (set_process_scheduling_policy(0, SCHED_FIFO, 10) == 0) {
show_process_scheduling_info(0, "设置 SCHED_FIFO 后");
} else {
printf(" 说明: SCHED_FIFO 需要 root 权限\n");
}

// 2. 尝试设置 SCHED_RR(需要 root 权限)
printf("\n2. 尝试设置 SCHED_RR 策略:\n");
if (set_process_scheduling_policy(0, SCHED_RR, 15) == 0) {
show_process_scheduling_info(0, "设置 SCHED_RR 后");
} else {
printf(" 说明: SCHED_RR 需要 root 权限\n");
}

// 3. 尝试设置 SCHED_BATCH
printf("\n3. 尝试设置 SCHED_BATCH 策略:\n");
if (set_process_scheduling_policy(0, SCHED_BATCH, 0) == 0) {
show_process_scheduling_info(0, "设置 SCHED_BATCH 后");
} else {
printf(" 说明: 可能需要适当权限\n");
}

// 4. 恢复默认策略
printf("\n4. 恢复默认 SCHED_OTHER 策略:\n");
struct sched_param default_param = {0};
if (sched_setscheduler(0, SCHED_OTHER, &default_param) == 0) {
printf("✓ 成功恢复默认调度策略\n");
show_process_scheduling_info(0, "恢复默认策略后");
} else {
printf("✗ 恢复默认策略失败: %s\n", strerror(errno));
}

printf("\n=== 调度策略使用建议 ===\n");
printf("选择原则:\n");
printf("1. 普通应用: 使用 SCHED_OTHER(默认)\n");
printf("2. 实时应用: 使用 SCHED_FIFO 或 SCHED_RR\n");
printf("3. 批处理任务: 使用 SCHED_BATCH\n");
printf("4. 后台任务: 使用 SCHED_IDLE\n");
printf("5. 截止时间任务: 使用 SCHED_DEADLINE\n");
printf("\n");

printf("权限要求:\n");
printf("1. SCHED_OTHER/SCHED_BATCH/SCHED_IDLE: 普通权限\n");
printf("2. SCHED_FIFO/SCHED_RR: 需要 root 权限\n");
printf("3. SCHED_DEADLINE: 需要特殊配置\n");
printf("\n");

printf("性能影响:\n");
printf("1. 实时策略: 更高优先级,更低延迟\n");
printf("2. 批处理策略: 更低唤醒频率,更好吞吐\n");
printf("3. 空闲策略: 不影响其他进程\n");
printf("4. 默认策略: 平衡性能和公平性\n");

return 0;
}

4.3 sched_getaffinity/sched_setaffinity - CPU 亲和性控制

函数原型

1
2
3
4
5
6
#define _GNU_SOURCE
#include <sched.h>

int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);

CPU 集合操作宏

1
2
3
4
5
6
void CPU_ZERO(cpu_set_t *set);              // 清空 CPU 集合
void CPU_SET(int cpu, cpu_set_t *set); // 设置 CPU
void CPU_CLR(int cpu, cpu_set_t *set); // 清除 CPU
int CPU_ISSET(int cpu, cpu_set_t *set); // 检查 CPU 是否设置
int CPU_COUNT(cpu_set_t *set); // 计算设置的 CPU 数量

功能

  • sched_getaffinity: 获取进程的 CPU 亲和性掩码

  • sched_setaffinity: 设置进程的 CPU 亲和性掩码

参数

  • pid: 进程 ID(0 表示当前进程)

  • cpusetsize: CPU 集合的大小

  • mask: 指向 CPU 集合的指针

返回值

  • 成功: 返回 0

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

示例代码

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

// 显示 CPU 集合信息
void show_cpu_set_info(const cpu_set_t *cpu_set, const char *description) {
printf("=== %s ===\n", description);

int cpu_count = CPU_COUNT((cpu_set_t*)cpu_set);
printf("CPU 数量: %d\n", cpu_count);

printf("CPU 列表: ");
int printed = 0;
for (int i = 0; i < CPU_SETSIZE; i++) {
if (CPU_ISSET(i, (cpu_set_t*)cpu_set)) {
if (printed > 0) printf(", ");
printf("%d", i);
printed++;
}
}
if (printed == 0) {
printf("无");
}
printf("\n\n");
}

// 获取系统 CPU 信息
void show_system_cpu_info() {
printf("=== 系统 CPU 信息 ===\n");

// 获取在线 CPU 数量
int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
printf("在线 CPU 数量: %d\n", online_cpus);

// 获取配置的 CPU 数量
int conf_cpus = sysconf(_SC_NPROCESSORS_CONF);
printf("配置 CPU 数量: %d\n", conf_cpus);

// 显示当前进程的 CPU 亲和性
cpu_set_t current_set;
CPU_ZERO(&current_set);

if (sched_getaffinity(0, sizeof(current_set), &current_set) == 0) {
show_cpu_set_info(&current_set, "当前进程 CPU 亲和性");
} else {
perror("获取 CPU 亲和性失败");
}
}

// 设置 CPU 亲和性
int set_process_cpu_affinity(pid_t pid, const int *cpu_list, int cpu_count) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);

printf("设置 CPU 亲和性:\n");
printf(" 进程 ID: %d\n", pid ? pid : getpid());
printf(" CPU 列表: ");

for (int i = 0; i < cpu_count; i++) {
if (i > 0) printf(", ");
printf("%d", cpu_list&#91;i]);
CPU_SET(cpu_list&#91;i], &cpu_set);
}
printf("\n");

if (sched_setaffinity(pid, sizeof(cpu_set), &cpu_set) == 0) {
printf("✓ CPU 亲和性设置成功\n");
return 0;
} else {
printf("✗ CPU 亲和性设置失败: %s\n", strerror(errno));
return -1;
}
}

// 创建 CPU 绑定的测试线程
void* cpu_bound_worker(void *arg) {
int worker_id = *(int*)arg;

printf("工作线程 %d 启动\n", worker_id);

// 获取线程的 CPU 信息
int current_cpu = sched_getcpu();
if (current_cpu != -1) {
printf(" 工作线程 %d 运行在 CPU %d 上\n", worker_id, current_cpu);
}

// 执行一些 CPU 密集型任务
volatile long sum = 0;
for (long i = 0; i < 10000000; i++) {
sum += i;

// 偶尔检查 CPU 变化
if (i % 1000000 == 0) {
int new_cpu = sched_getcpu();
if (new_cpu != current_cpu && new_cpu != -1) {
printf(" 工作线程 %d 从 CPU %d 切换到 CPU %d\n",
worker_id, current_cpu, new_cpu);
current_cpu = new_cpu;
}
}
}

printf("工作线程 %d 完成\n", worker_id);
return NULL;
}

int main() {
printf("=== sched_getaffinity/sched_setaffinity 示例 ===\n\n");

// 显示系统信息
show_system_cpu_info();

// 1. 获取当前进程的 CPU 亲和性
printf("1. 获取当前进程 CPU 亲和性:\n");
cpu_set_t initial_set;
CPU_ZERO(&initial_set);

if (sched_getaffinity(0, sizeof(initial_set), &initial_set) == 0) {
show_cpu_set_info(&initial_set, "初始 CPU 亲和性");
} else {
perror("获取初始 CPU 亲和性失败");
}

// 2. 设置 CPU 亲和性(绑定到特定 CPU)
printf("2. 设置 CPU 亲和性:\n");

// 获取系统 CPU 数量
int online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
printf("系统在线 CPU 数量: %d\n", online_cpus);

if (online_cpus > 1) {
// 绑定到第一个 CPU
int single_cpu&#91;] = {0};
if (set_process_cpu_affinity(0, single_cpu, 1) == 0) {
cpu_set_t new_set;
CPU_ZERO(&new_set);

if (sched_getaffinity(0, sizeof(new_set), &new_set) == 0) {
show_cpu_set_info(&new_set, "设置后 CPU 亲和性");
}
}

// 绑定到前两个 CPU
printf("绑定到前两个 CPU:\n");
int two_cpus&#91;] = {0, 1};
if (set_process_cpu_affinity(0, two_cpus, 2) == 0) {
cpu_set_t new_set;
CPU_ZERO(&new_set);

if (sched_getaffinity(0, sizeof(new_set), &new_set) == 0) {
show_cpu_set_info(&new_set, "绑定到前两个 CPU 后");
}
}
} else {
printf("系统只有一个 CPU,跳过 CPU 绑定测试\n");
}

// 3. 恢复初始 CPU 亲和性
printf("3. 恢复初始 CPU 亲和性:\n");
if (sched_setaffinity(0, sizeof(initial_set), &initial_set) == 0) {
printf("✓ 成功恢复初始 CPU 亲和性\n");

cpu_set_t restored_set;
CPU_ZERO(&restored_set);

if (sched_getaffinity(0, sizeof(restored_set), &restored_set) == 0) {
show_cpu_set_info(&restored_set, "恢复后 CPU 亲和性");
}
} else {
printf("✗ 恢复初始 CPU 亲和性失败: %s\n", strerror(errno));
}

// 4. 显示 CPU 亲和性的好处
printf("=== CPU 亲和性的好处 ===\n");
printf("性能优化:\n");
printf("1. 减少 CPU 缓存失效\n");
printf("2. 提高缓存命中率\n");
printf("3. 降低上下文切换开销\n");
printf("4. 改善 NUMA 访问模式\n");
printf("5. 提高多核应用性能\n");
printf("\n");

printf("应用场景:\n");
printf("1. 高性能计算应用\n");
printf("2. 实时系统\n");
printf("3. 数据库服务器\n");
printf("4. 游戏服务器\n");
printf("5. 科学计算\n");
printf("6. 音视频处理\n");
printf("\n");

printf("注意事项:\n");
printf("1. 过度限制可能影响负载均衡\n");
printf("2. NUMA 架构需要考虑内存亲和性\n");
printf("3. 应该根据应用特点合理设置\n");
printf("4. 避免与其他进程争抢 CPU\n");
printf("5. 监控系统整体性能\n");

return 0;
}

4.4 sched_get_priority_min/sched_get_priority_max - 优先级范围查询

函数原型

1
2
3
4
5
#include <sched.h>

int sched_get_priority_min(int policy);
int sched_get_priority_max(int policy);

功能

获取指定调度策略的最小和最大优先级值。

参数

  • policy: 调度策略

返回值

  • 成功: 返回对应的优先级值

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

示例代码

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

// 显示调度策略优先级范围
void show_policy_priority_range(int policy, const char *policy_name) {
int min_priority = sched_get_priority_min(policy);
int max_priority = sched_get_priority_max(policy);

printf("%-20s: ", policy_name);

if (min_priority == -1 || max_priority == -1) {
printf("不支持 (%s)\n", strerror(errno));
} else {
printf("最小优先级 %3d, 最大优先级 %3d", min_priority, max_priority);

if (min_priority == max_priority) {
printf(" (固定优先级)");
} else if (min_priority < max_priority) {
printf(" (动态优先级范围)");
}
printf("\n");
}
}

// 显示所有调度策略的优先级范围
void show_all_policy_ranges() {
printf("=== 所有调度策略优先级范围 ===\n");

struct {
int policy;
const char *name;
} policies&#91;] = {
{SCHED_OTHER, "SCHED_OTHER"},
{SCHED_FIFO, "SCHED_FIFO"},
{SCHED_RR, "SCHED_RR"},
{SCHED_BATCH, "SCHED_BATCH"},
{SCHED_IDLE, "SCHED_IDLE"},
{SCHED_DEADLINE, "SCHED_DEADLINE"},
{-1, NULL}
};

for (int i = 0; policies&#91;i].name; i++) {
show_policy_priority_range(policies&#91;i].policy, policies&#91;i].name);
}

printf("\n");
}

// 优先级验证函数
int validate_priority_for_policy(int policy, int priority) {
int min_priority = sched_get_priority_min(policy);
int max_priority = sched_get_priority_max(policy);

if (min_priority == -1 || max_priority == -1) {
return -1; // 策略不支持
}

if (priority >= min_priority && priority <= max_priority) {
return 0; // 优先级有效
} else {
return 1; // 优先级无效
}
}

// 显示优先级验证结果
void show_priority_validation(int policy, int priority) {
const char *policy_name = NULL;
switch (policy) {
case SCHED_OTHER: policy_name = "SCHED_OTHER"; break;
case SCHED_FIFO: policy_name = "SCHED_FIFO"; break;
case SCHED_RR: policy_name = "SCHED_RR"; break;
case SCHED_BATCH: policy_name = "SCHED_BATCH"; break;
case SCHED_IDLE: policy_name = "SCHED_IDLE"; break;
case SCHED_DEADLINE: policy_name = "SCHED_DEADLINE"; break;
default: policy_name = "未知策略"; break;
}

printf("验证 %s 策略优先级 %d: ", policy_name, priority);

int result = validate_priority_for_policy(policy, priority);
switch (result) {
case -1:
printf("策略不支持\n");
break;
case 0:
printf("✓ 有效\n");
break;
case 1:
printf("✗ 无效\n");
break;
}
}

int main() {
printf("=== sched_get_priority_min/max 示例 ===\n\n");

// 显示所有策略的优先级范围
show_all_policy_ranges();

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

// 获取当前进程调度策略
int current_policy = sched_getscheduler(0);
if (current_policy != -1) {
printf(" 当前调度策略: %d ", current_policy);
switch (current_policy) {
case SCHED_OTHER: printf("(SCHED_OTHER)\n"); break;
case SCHED_FIFO: printf("(SCHED_FIFO)\n"); break;
case SCHED_RR: printf("(SCHED_RR)\n"); break;
case SCHED_BATCH: printf("(SCHED_BATCH)\n"); break;
case SCHED_IDLE: printf("(SCHED_IDLE)\n"); break;
case SCHED_DEADLINE: printf("(SCHED_DEADLINE)\n"); break;
default: printf("(未知)\n"); break;
}

// 获取当前进程优先级
struct sched_param current_param;
if (sched_getparam(0, &current_param) == 0) {
printf(" 当前优先级: %d\n", current_param.sched_priority);

// 验证当前优先级
show_priority_validation(current_policy, current_param.sched_priority);
}
} else {
perror("获取当前调度策略失败");
}

printf("\n");

// 验证不同策略的优先级
printf("=== 优先级验证示例 ===\n");

// SCHED_OTHER 策略
printf("SCHED_OTHER 策略:\n");
show_priority_validation(SCHED_OTHER, 0); // 有效
show_priority_validation(SCHED_OTHER, -1); // 无效
show_priority_validation(SCHED_OTHER, 10); // 无效

// SCHED_FIFO 策略
printf("\nSCHED_FIFO 策略:\n");
show_priority_validation(SCHED_FIFO, 10); // 可能有效
show_priority_validation(SCHED_FIFO, 50); // 可能有效
show_priority_validation(SCHED_FIFO, 99); // 可能有效
show_priority_validation(SCHED_FIFO, 100); // 可能无效

// SCHED_RR 策略
printf("\nSCHED_RR 策略:\n");
show_priority_validation(SCHED_RR, 10); // 可能有效
show_priority_validation(SCHED_RR, 50); // 可能有效
show_priority_validation(SCHED_RR, 99); // 可能有效
show_priority_validation(SCHED_RR, 100); // 可能无效

// SCHED_BATCH 策略
printf("\nSCHED_BATCH 策略:\n");
show_priority_validation(SCHED_BATCH, 0); // 有效
show_priority_validation(SCHED_BATCH, -1); // 无效
show_priority_validation(SCHED_BATCH, 10); // 无效

// SCHED_IDLE 策略
printf("\nSCHED_IDLE 策略:\n");
show_priority_validation(SCHED_IDLE, 0); // 有效
show_priority_validation(SCHED_IDLE, -1); // 无效
show_priority_validation(SCHED_IDLE, 10); // 无效

printf("\n=== 优先级使用建议 ===\n");
printf("优先级设置原则:\n");
printf("1. 了解策略的优先级范围\n");
printf("2. 合理分配优先级值\n");
printf("3. 避免优先级反转\n");
printf("4. 考虑系统整体平衡\n");
printf("5. 验证优先级的有效性\n");
printf("\n");

printf("实时策略优先级:\n");
printf("1. SCHED_FIFO/SCHED_RR: 1-99 (通常)\n");
printf("2. 数值越大优先级越高\n");
printf("3. 需要 root 权限\n");
printf("4. 谨慎使用高优先级\n");
printf("5. 避免独占 CPU\n");
printf("\n");

printf("普通策略优先级:\n");
printf("1. SCHED_OTHER: 通常为 0\n");
printf("2. 通过 nice 值调整\n");
printf("3. 使用动态优先级\n");
printf("4. 平衡系统负载\n");
printf("5. 适合交互式应用\n");

return 0;
}

4.5 sched_rr_get_interval - 轮转调度时间片

函数原型

1
2
3
4
#include <sched.h>

int sched_rr_get_interval(pid_t pid, struct timespec *tp);

功能

获取 SCHED_RR 策略的时间片长度。

参数

  • pid: 进程 ID(0 表示当前进程)

  • tp: 指向 timespec 结构体的指针,用于存储时间片长度

返回值

  • 成功: 返回 0

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

示例代码

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

// 显示时间片信息
void show_timeslice_info(const struct timespec *interval, const char *description) {
printf("=== %s ===\n", description);

if (interval->tv_sec == 0 && interval->tv_nsec == 0) {
printf("时间片长度: 未设置\n");
} else {
printf("时间片长度: %ld.%09ld 秒\n",
(long)interval->tv_sec, (long)interval->tv_nsec);
printf("时间片长度: %.3f 毫秒\n",
(double)interval->tv_sec * 1000 + (double)interval->tv_nsec / 1000000);
printf("时间片长度: %.3f 微秒\n",
(double)interval->tv_sec * 1000000 + (double)interval->tv_nsec / 1000);
printf("时间片长度: %ld 纳秒\n",
(long)interval->tv_sec * 1000000000 + (long)interval->tv_nsec);
}

printf("\n");
}

// 获取 RR 调度时间片
int get_rr_timeslice(pid_t pid) {
struct timespec interval;

printf("获取 SCHED_RR 时间片长度:\n");
printf(" 进程 ID: %d\n", pid ? pid : getpid());

if (sched_rr_get_interval(pid, &interval) == 0) {
show_timeslice_info(&interval, "RR 调度时间片");
return 0;
} else {
switch (errno) {
case EINVAL:
printf("✗ 进程不是 SCHED_RR 策略\n");
break;
case ESRCH:
printf("✗ 进程不存在\n");
break;
default:
printf("✗ 获取时间片失败: %s\n", strerror(errno));
break;
}
return -1;
}
}

// 比较不同策略的时间片
void compare_policy_intervals() {
printf("=== 不同策略时间片比较 ===\n");

// 获取当前进程时间片(假设是 SCHED_OTHER)
struct timespec current_interval;
if (sched_rr_get_interval(0, &current_interval) == 0) {
printf("当前进程时间片: %ld.%09ld 秒\n",
(long)current_interval.tv_sec, (long)current_interval.tv_nsec);
} else {
printf("当前进程时间片: 无法获取 (%s)\n", strerror(errno));
}

// 显示系统时间片配置
printf("\n系统时间片配置:\n");

// 读取内核参数
FILE *fp = fopen("/proc/sys/kernel/sched_rr_timeslice_ms", "r");
if (fp) {
char buffer&#91;64];
if (fgets(buffer, sizeof(buffer), fp)) {
printf(" RR 时间片 (ms): %s", buffer);
}
fclose(fp);
} else {
printf(" RR 时间片: 无法读取系统配置\n");
}

// 显示其他相关配置
printf("其他相关配置:\n");
system("cat /proc/sys/kernel/sched_child_runs_first 2>/dev/null || echo ' 无法读取 sched_child_runs_first'");
system("cat /proc/sys/kernel/sched_autogroup_enabled 2>/dev/null || echo ' 无法读取 sched_autogroup_enabled'");

printf("\n");
}

int main() {
printf("=== sched_rr_get_interval 示例 ===\n\n");

// 显示系统信息
printf("系统信息:\n");
printf(" 内核版本: ");
system("uname -r | tr -d '\\n'");
printf("\n");
printf(" CPU 架构: ");
system("uname -m | tr -d '\\n'");
printf("\n");
printf(" 进程 ID: %d\n", getpid());
printf(" 父进程 ID: %d\n", getppid());
printf("\n");

// 1. 获取当前进程时间片
printf("1. 获取当前进程时间片:\n");
get_rr_timeslice(0);

// 2. 尝试获取不存在进程的时间片
printf("2. 尝试获取不存在进程的时间片:\n");
if (sched_rr_get_interval(999999, &(struct timespec){0}) == -1) {
if (errno == ESRCH) {
printf("✓ 正确处理不存在进程: ESRCH\n");
} else {
printf("✗ 意外错误: %s\n", strerror(errno));
}
}

// 3. 尝试获取非 RR 策略进程的时间片
printf("3. 尝试获取非 RR 策略进程的时间片:\n");
if (sched_rr_get_interval(0, &(struct timespec){0}) == -1) {
if (errno == EINVAL) {
printf("✓ 正确处理非 RR 策略进程: EINVAL\n");
printf(" 说明: 当前进程使用默认 SCHED_OTHER 策略\n");
} else {
printf("✗ 其他错误: %s\n", strerror(errno));
}
}

// 4. 显示系统时间片配置
compare_policy_intervals();

// 5. 显示时间片对性能的影响
printf("=== 时间片对性能的影响 ===\n");
printf("时间片长度的影响:\n");
printf("1. 较短时间片:\n");
printf(" • 更好的响应性\n");
printf(" • 更多上下文切换\n");
printf(" • 更低吞吐量\n");
printf(" • 更高延迟\n");
printf("\n");

printf("2. 较长时间片:\n");
printf(" • 更低响应性\n");
printf(" • 更少上下文切换\n");
printf(" • 更高吞吐量\n");
printf(" • 更低延迟\n");
printf("\n");

printf("3. 默认时间片:\n");
printf(" • 平衡响应性和吞吐量\n");
printf(" • 通常为 10-100ms\n");
printf(" • 适应大多数应用\n");
printf(" • 可配置调整\n");
printf("\n");

printf("应用场景:\n");
printf("1. 实时系统: 需要短时间片保证响应性\n");
printf("2. 批处理系统: 需要长时间片提高吞吐量\n");
printf("3. 交互式应用: 需要适中时间片平衡各方面\n");
printf("4. 服务器应用: 根据负载动态调整\n");
printf("5. 嵌入式系统: 根据硬件特性优化\n");
printf("\n");

printf("配置建议:\n");
printf("1. 监控上下文切换频率\n");
printf("2. 调整时间片适应应用特点\n");
printf("3. 考虑系统整体负载\n");
printf("4. 测试不同配置的性能\n");
printf("5. 避免极端配置影响系统稳定性\n");

return 0;
}

4.6 sched_getcpu - 获取当前 CPU 编号

函数原型

1
2
3
4
5
#define _GNU_SOURCE
#include <sched.h>

int sched_getcpu(void);

功能

获取调用线程当前运行的 CPU 编号。

参数

无参数

返回值

  • 成功: 返回 CPU 编号(从 0 开始)

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

示例代码

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/syscall.h>

// 获取 CPU 信息的辅助函数
void show_cpu_info() {
printf("=== CPU 信息 ===\n");

// 获取当前 CPU 编号
int current_cpu = sched_getcpu();
if (current_cpu != -1) {
printf("当前 CPU 编号: %d\n", current_cpu);
} else {
printf("无法获取当前 CPU 编号: %s\n", strerror(errno));
}

// 获取系统 CPU 数量
long online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
long conf_cpus = sysconf(_SC_NPROCESSORS_CONF);

printf("在线 CPU 数量: %ld\n", online_cpus);
printf("配置 CPU 数量: %ld\n", conf_cpus);

// 显示 CPU 信息
printf("CPU 详细信息:\n");
system("lscpu | head -10 2>/dev/null || echo '无法获取 CPU 详细信息'");

printf("\n");
}

// 线程函数,显示线程的 CPU 信息
void* thread_cpu_info(void* arg) {
int thread_id = *(int*)arg;
int initial_cpu, current_cpu;

// 获取初始 CPU
initial_cpu = sched_getcpu();

printf("线程 %d:\n", thread_id);
printf(" 初始 CPU: %d\n", initial_cpu);

// 执行一些工作并检查 CPU 变化
volatile long sum = 0;
for (long i = 0; i < 10000000; i++) {
sum += i;

// 偶尔检查 CPU 变化
if (i % 1000000 == 0) {
current_cpu = sched_getcpu();
if (current_cpu != initial_cpu && current_cpu != -1) {
printf(" 线程 %d 从 CPU %d 切换到 CPU %d\n",
thread_id, initial_cpu, current_cpu);
initial_cpu = current_cpu;
}
}
}

// 最终 CPU
current_cpu = sched_getcpu();
printf(" 最终 CPU: %d\n", current_cpu);
printf(" 线程 %d 完成\n", thread_id);

return NULL;
}

// CPU 绑定测试
void test_cpu_binding() {
printf("=== CPU 绑定测试 ===\n");

// 获取系统 CPU 数量
long online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
if (online_cpus <= 1) {
printf("系统只有一个 CPU,跳过绑定测试\n");
return;
}

// 创建多个线程
pthread_t threads&#91;4];
int thread_ids&#91;4] = {1, 2, 3, 4};

printf("创建 4 个线程进行 CPU 绑定测试:\n");

for (int i = 0; i < 4; i++) {
if (pthread_create(&threads&#91;i], NULL, thread_cpu_info, &thread_ids&#91;i]) != 0) {
perror("创建线程失败");
return;
}
}

// 等待所有线程完成
for (int i = 0; i < 4; i++) {
pthread_join(threads&#91;i], NULL);
}

printf("CPU 绑定测试完成\n\n");
}

int main() {
printf("=== sched_getcpu 示例 ===\n\n");

// 显示系统信息
show_cpu_info();

// 显示当前进程信息
printf("进程信息:\n");
printf(" 进程 ID: %d\n", getpid());
printf(" 父进程 ID: %d\n", getppid());
printf(" 线程 ID: %ld\n", syscall(SYS_gettid));

// 获取当前 CPU
int current_cpu = sched_getcpu();
if (current_cpu != -1) {
printf(" 当前运行 CPU: %d\n", current_cpu);
} else {
printf(" 无法获取 CPU 信息: %s\n", strerror(errno));
}

printf("\n");

// 连续获取 CPU 信息观察变化
printf("连续获取 CPU 信息 (执行密集计算):\n");
volatile long sum = 0;
int initial_cpu = sched_getcpu();

printf(" 初始 CPU: %d\n", initial_cpu);

for (long i = 0; i < 50000000; i++) {
sum += i;

// 每隔一定次数检查 CPU
if (i % 10000000 == 0) {
int current_cpu = sched_getcpu();
if (current_cpu != -1) {
printf(" 计算 %ld 次后 CPU: %d\n", i, current_cpu);
}
}
}

int final_cpu = sched_getcpu();
printf(" 最终 CPU: %d\n", final_cpu);

if (initial_cpu != final_cpu && initial_cpu != -1 && final_cpu != -1) {
printf(" ✓ CPU 在计算过程中发生了切换\n");
} else {
printf(" CPU 在计算过程中保持不变\n");
}

printf("\n");

// CPU 绑定测试
test_cpu_binding();

// 显示 CPU 调度信息
printf("=== CPU 调度信息 ===\n");
printf("CPU 调度相关概念:\n");
printf("1. CPU 亲和性: 进程可以运行的 CPU 集合\n");
printf("2. 负载均衡: 系统在 CPU 间分配负载\n");
printf("3. 上下文切换: 进程在 CPU 间的切换\n");
printf("4. 缓存局部性: 数据在 CPU 缓存中的位置\n");
printf("5. NUMA 拓扑: 非统一内存访问架构\n");
printf("\n");

printf("sched_getcpu 的用途:\n");
printf("1. 性能分析: 监控线程 CPU 使用情况\n");
printf("2. 负载均衡: 了解 CPU 分配情况\n");
printf("3. 调试工具: 分析程序执行行为\n");
printf("4. 实时系统: 监控实时性约束\n");
printf("5. 优化建议: 识别性能瓶颈\n");
printf("\n");

printf("使用建议:\n");
printf("1. 结合 CPU 亲和性使用\n");
printf("2. 监控 CPU 切换频率\n");
printf("3. 分析热点 CPU\n");
printf("4. 优化缓存局部性\n");
printf("5. 避免过度绑定 CPU\n");

return 0;
}

  1. 编译和运行说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 编译所有示例程序
gcc -o pread_example pread_example.c
gcc -o affinity_example affinity_example.c
gcc -o priority_example priority_example.c
gcc -o rr_interval_example rr_interval_example.c
gcc -o getcpu_example getcpu_example.c -lpthread

# 运行示例程序
./pread_example
./affinity_example
./priority_example
./rr_interval_example
./getcpu_example

# 需要 root 权限的测试
sudo ./priority_example
sudo ./rr_interval_example

  1. 系统要求检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 检查内核版本
uname -r

# 检查系统调用支持
grep -E "(pread|pwrite)" /usr/include/asm/unistd_64.h

# 检查 CPU 信息
lscpu

# 检查调度器信息
cat /proc/sched_debug 2>/dev/null || echo "无法读取调度器调试信息"

# 检查实时调度支持
grep -i realtime /boot/config-$(uname -r)

# 检查 NUMA 支持
numactl --hardware 2>/dev/null || echo "系统不支持 NUMA"

  1. 重要注意事项

5.1 权限要求

  • 普通用户: 可以使用 pread/pwrite 等基本 I/O 操作

  • root 用户: 需要设置实时调度策略 (SCHED_FIFO/SCHED_RR)

  • CAP_SYS_NICE: 某些操作需要此能力

5.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
// 安全的系统调用封装
int safe_pread(int fd, void *buf, size_t count, off_t offset) {
if (fd < 0 || !buf || count == 0) {
errno = EINVAL;
return -1;
}

ssize_t result;
do {
result = pread(fd, buf, count, offset);
} while (result == -1 && errno == EINTR);

return result;
}

// 安全的 CPU 亲和性设置
int safe_sched_setaffinity(pid_t pid, const cpu_set_t *mask) {
if (!mask) {
errno = EINVAL;
return -1;
}

return sched_setaffinity(pid, sizeof(cpu_set_t), mask);
}

5.3 性能考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
// 性能优化建议
void performance_optimization_tips() {
printf("性能优化建议:\n");
printf("1. 批量操作: 使用 preadv/pwritev 减少系统调用次数\n");
printf("2. 缓冲区大小: 合理设置缓冲区大小避免频繁调用\n");
printf("3. CPU 亲和性: 合理绑定 CPU 提高缓存命中率\n");
printf("4. 调度策略: 根据应用特点选择合适的调度策略\n");
printf("5. 优先级设置: 避免过度使用高优先级影响系统稳定性\n");
printf("6. 时间片调整: 根据应用需求调整时间片长度\n");
printf("7. 资源限制: 合理设置进程资源限制\n");
printf("8. 内存管理: 避免内存碎片和频繁分配\n");
}

5.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
// 完整的 I/O 操作最佳实践
typedef struct {
int fd;
size_t buffer_size;
int use_positioned_io;
int use_scatter_gather;
cpu_set_t cpu_affinity;
int has_cpu_affinity;
} io_context_t;

// 初始化 I/O 上下文
int init_io_context(io_context_t *ctx, const char *filename) {
ctx->fd = open(filename, O_RDWR | O_CREAT, 0644);
if (ctx->fd == -1) {
return -1;
}

ctx->buffer_size = 4096;
ctx->use_positioned_io = 1;
ctx->use_scatter_gather = 0;
ctx->has_cpu_affinity = 0;

return 0;
}

// 清理 I/O 上下文
void cleanup_io_context(io_context_t *ctx) {
if (ctx->fd != -1) {
close(ctx->fd);
ctx->fd = -1;
}
}

  1. 实际应用场景

6.1 数据库系统

1
2
3
4
5
6
7
// 数据库页 I/O 操作
int db_page_io(int fd, off_t page_offset, void *page_data, size_t page_size) {
// 使用 pread/pwrite 进行页级别的随机访问
ssize_t bytes_read = pread(fd, page_data, page_size, page_offset);
return (bytes_read == (ssize_t)page_size) ? 0 : -1;
}

6.2 实时系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 实时应用调度设置
int setup_realtime_scheduling(int priority) {
struct sched_param param;
param.sched_priority = priority;

// 设置实时调度策略
if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
return -1;
}

// 设置 CPU 亲和性
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0, &cpu_set); // 绑定到 CPU 0

return sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
}

6.3 网络服务器

1
2
3
4
5
6
// 网络服务器 I/O 处理
int handle_network_io(int client_fd, struct iovec *iov, int iovcnt) {
// 使用 preadv/pwritev 处理网络数据包
return pwritev(client_fd, iov, iovcnt, 0);
}

  1. 总结

7.1 核心概念回顾

Linux 调度器函数族为进程调度控制提供了精细化的管理能力:

sched_yield: 让出当前 CPU 时间片,允许同优先级进程运行

sched_getscheduler/sched_setscheduler: 获取和设置进程调度策略

sched_getaffinity/sched_setaffinity: 获取和设置 CPU 亲和性

sched_get_priority_min/sched_get_priority_max: 获取调度策略优先级范围

sched_rr_get_interval: 获取轮转调度时间片长度

sched_getcpu: 获取当前运行的 CPU 编号

prlimit64: 获取和设置进程资源限制

7.2 调度策略详解

五种主要调度策略:

  • SCHED_OTHER: 默认分时调度(CFS),适合普通应用

  • SCHED_FIFO: 实时先进先出,高优先级进程持续运行

  • SCHED_RR: 实时轮转调度,相同优先级进程时间片轮转

  • SCHED_BATCH: 批处理优化,减少上下文切换

  • SCHED_IDLE: 空闲任务,只在系统空闲时运行

7.3 性能优化要点

调度策略选择:

  • 普通应用:使用 SCHED_OTHER(默认)

  • 实时系统:使用 SCHED_FIFO 或 SCHED_RR

  • 批处理任务:使用 SCHED_BATCH

  • 后台任务:使用 SCHED_IDLE

CPU 亲和性优化:

  • 减少 CPU 缓存失效

  • 提高缓存命中率

  • 降低上下文切换开销

  • 改善 NUMA 访问模式

7.4 安全和权限管理

权限要求:

  • 普通用户:可使用 SCHED_OTHER/SCHED_BATCH/SCHED_IDLE

  • root 用户:可使用所有调度策略

  • CAP_SYS_NICE:允许修改调度策略和优先级

  • CAP_SYS_ADMIN:允许使用 prlimit64 设置资源限制

安全考虑:

1
2
3
4
5
6
7
8
9
10
11
// 权限检查示例
int check_scheduling_permissions() {
if (geteuid() == 0) {
return 1; // root 权限
}

// 检查 CAP_SYS_NICE 能力
// 使用 libcap-ng 库进行能力检查
return 0; // 普通权限
}

7.5 实际应用场景

适用场景:

实时系统:音视频处理、工业控制(SCHED_FIFO/SCHED_RR)

高性能计算:科学计算、数据分析(CPU 亲和性绑定)

服务器应用:Web 服务、数据库(合理的调度策略)

系统监控:性能分析、资源管理(sched_getcpu)

容器技术:资源限制、进程隔离(prlimit64)

7.6 最佳实践

调度策略设置:

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
// 安全的调度策略设置
int safe_set_scheduler(pid_t pid, int policy, int priority) {
struct sched_param param;

// 验证参数
if (priority < sched_get_priority_min(policy) ||
priority > sched_get_priority_max(policy)) {
errno = EINVAL;
return -1;
}

param.sched_priority = priority;

// 设置调度策略
int result = sched_setscheduler(pid, policy, &param);

if (result == -1) {
switch (errno) {
case EPERM:
fprintf(stderr, "权限不足,需要适当权限\n");
break;
case EINVAL:
fprintf(stderr, "无效的策略或优先级\n");
break;
case ESRCH:
fprintf(stderr, "进程不存在\n");
break;
}
}

return result;
}

CPU 亲和性管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 智能 CPU 绑定
int smart_cpu_binding(pid_t pid, int cpu_count) {
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);

// 根据系统 CPU 数量智能绑定
int available_cpus = sysconf(_SC_NPROCESSORS_ONLN);
int bind_count = (cpu_count < available_cpus) ? cpu_count : available_cpus;

for (int i = 0; i < bind_count; i++) {
CPU_SET(i, &cpu_set);
}

return sched_setaffinity(pid, sizeof(cpu_set), &cpu_set);
}

资源限制控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 安全的资源限制设置
int safe_set_resource_limit(int resource, rlim_t soft_limit, rlim_t hard_limit) {
struct rlimit64 limit;
limit.rlim_cur = soft_limit;
limit.rlim_max = hard_limit;

int result = prlimit64(0, resource, &limit, NULL);

if (result == -1) {
switch (errno) {
case EPERM:
fprintf(stderr, "权限不足,需要 CAP_SYS_RESOURCE 能力\n");
break;
case EINVAL:
fprintf(stderr, "无效的资源类型或限制值\n");
break;
}
}

return result;
}

7.7 学习建议

掌握路径:

入门阶段:理解基本调度概念和 sched_yield 使用

进阶阶段:掌握调度策略和 CPU 亲和性

高级阶段:精通资源限制和性能优化

专家阶段:实现复杂的调度控制系统

实践要点:

  • 从简单示例开始逐步复杂化

  • 重点关注权限管理和错误处理

  • 实际项目中验证调度效果

  • 持续关注实时系统发展

这些调度器函数是 Linux 系统编程的重要组成部分,正确掌握和使用它们对于开发高性能、实时性要求高的应用程序至关重要。通过系统的学习和实践,开发者可以充分发挥 Linux 调度系统的强大功能。

mq_timedreceive系统调用及示例

mq_timedreceive函数详解

  1. 函数介绍

mq_timedreceive函数是Linux系统中用于在指定时间内从POSIX消息队列接收消息的函数。它是mq_receive函数的增强版本,支持超时控制。可以把mq_timedreceive想象成一个”限时消息接收器”,它能够在指定的时间内尝试接收消息,如果超时则返回错误。

这个函数特别适用于需要控制接收等待时间的场景,比如实时系统、服务器应用或需要避免无限期阻塞的程序。

使用场景:

  • 实时系统的消息接收

  • 服务器程序的请求处理

  • 避免无限期阻塞的接收操作

  • 超时控制的网络应用

  • 高可用性系统中的消息处理

  1. 函数原型
1
2
3
4
5
6
#include <mqueue.h>
#include <time.h>

ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len,
unsigned int *msg_prio, const struct timespec *abs_timeout);

  1. 功能

mq_timedreceive函数的主要功能是在指定的绝对超时时间内从消息队列接收消息。如果队列为空且在超时时间内没有消息到达,则返回错误。

  1. 参数

mqdes: 消息队列描述符

  • 类型:mqd_t

  • 含义:已打开的消息队列描述符

msg_ptr: 消息缓冲区指针

  • 类型:char*

  • 含义:指向存储接收消息的缓冲区

msg_len: 缓冲区大小

  • 类型:size_t

  • 含义:消息缓冲区的大小(字节数)

msg_prio: 消息优先级指针

  • 类型:unsigned int*

  • 含义:指向存储消息优先级的变量(可为NULL)

abs_timeout: 绝对超时时间

  • 类型:const struct timespec*

  • 含义:绝对超时时间(基于CLOCK_REALTIME)

  1. 返回值
  • 成功: 返回接收到的消息字节数

失败: 返回-1,并设置errno错误码

  • EAGAIN:超时时间内没有消息可接收

  • EBADF:无效的消息队列描述符

  • EINTR:被信号中断

  • EINVAL:参数无效

  • EMSGSIZE:缓冲区太小

  • ETIMEDOUT:超时

  1. 相似函数或关联函数
  • mq_receive(): 接收消息(阻塞)

  • mq_send(): 发送消息

  • mq_timedsend(): 限时发送消息

  • clock_gettime(): 获取当前时间

  • pthread_cond_timedwait(): 限时条件等待

  1. 示例代码

示例1:基础mq_timedreceive使用 - 超时控制接收

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <time.h>

// 创建消息队列
mqd_t create_test_queue(const char* name) {
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = 10,
.mq_msgsize = 256,
.mq_curmsgs = 0
};

mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("创建消息队列失败");
return -1;
}

printf("创建测试队列: %s\n", name);
return mq;
}

// 计算绝对超时时间
int calculate_absolute_timeout(struct timespec* abs_timeout, int seconds) {
if (clock_gettime(CLOCK_REALTIME, abs_timeout) == -1) {
perror("获取当前时间失败");
return -1;
}

abs_timeout->tv_sec += seconds;
return 0;
}

int main() {
printf("=== 基础mq_timedreceive使用示例 ===\n");

const char* queue_name = "/receive_test_queue";

// 创建测试队列
mqd_t mq = create_test_queue(queue_name);
if (mq == -1) {
exit(EXIT_FAILURE);
}

// 演示mq_timedreceive超时(空队列)
printf("1. 演示空队列超时接收:\n");

struct timespec abs_timeout;
if (calculate_absolute_timeout(&abs_timeout, 3) == -1) { // 3秒超时
mq_close(mq);
mq_unlink(queue_name);
exit(EXIT_FAILURE);
}

char buffer&#91;256];
unsigned int priority;

printf("从空队列接收消息(3秒超时):\n");
clock_t start_time = clock();
ssize_t result = mq_timedreceive(mq, buffer, sizeof(buffer), &priority, &abs_timeout);
clock_t end_time = clock();

double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;

if (result == -1) {
if (errno == ETIMEDOUT) {
printf("✗ 接收超时 (耗时: %.2f 秒)\n", elapsed_time);
} else if (errno == EAGAIN) {
printf("✗ 暂无消息: %s\n", strerror(errno));
} else {
printf("✗ 接收失败: %s\n", strerror(errno));
}
} else {
buffer&#91;result] = '\0';
printf("✓ 接收到消息: %s (优先级: %u)\n", buffer, priority);
}

// 发送一些测试消息
printf("\n2. 发送测试消息:\n");
const char* messages&#91;] = {
"第一条测试消息",
"第二条测试消息",
"第三条测试消息"
};

for (int i = 0; i < 3; i++) {
unsigned int priorities&#91;] = {1, 5, 3};
if (mq_send(mq, messages&#91;i], strlen(messages&#91;i]), priorities&#91;i]) == -1) {
perror("发送消息失败");
} else {
printf("发送: %s (优先级: %u)\n", messages&#91;i], priorities&#91;i]);
}
}

// 演示成功的mq_timedreceive
printf("\n3. 演示成功接收消息:\n");

if (calculate_absolute_timeout(&abs_timeout, 5) == 0) { // 5秒超时
printf("接收消息(队列有消息):\n");

// 接收所有消息
while (1) {
ssize_t bytes_received = mq_timedreceive(mq, buffer, sizeof(buffer), &priority, &abs_timeout);
if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
printf("✓ 接收到消息: %s (优先级: %u)\n", buffer, priority);
} else {
if (errno == ETIMEDOUT || errno == EAGAIN) {
printf("无更多消息可接收\n");
break;
} else {
printf("接收失败: %s\n", strerror(errno));
break;
}
}
}
}

// 演示缓冲区大小处理
printf("\n4. 缓冲区大小处理演示:\n");

// 发送一条长消息
char long_message&#91;200];
memset(long_message, 'A', sizeof(long_message) - 1);
long_message&#91;sizeof(long_message) - 1] = '\0';

if (mq_send(mq, long_message, strlen(long_message), 0) == 0) {
printf("发送长消息成功\n");

// 使用过小的缓冲区接收
char small_buffer&#91;50];
if (calculate_absolute_timeout(&abs_timeout, 2) == 0) {
ssize_t bytes_received = mq_timedreceive(mq, small_buffer, sizeof(small_buffer), NULL, &abs_timeout);
if (bytes_received == -1) {
if (errno == EMSGSIZE) {
printf("✗ 缓冲区太小 (预期错误)\n");
} else {
printf("✗ 其他错误: %s\n", strerror(errno));
}
} else {
small_buffer&#91;bytes_received] = '\0';
printf("✓ 接收到截断消息: %s (%zd 字节)\n", small_buffer, bytes_received);
}
}

// 使用足够大的缓冲区接收
char large_buffer&#91;256];
if (calculate_absolute_timeout(&abs_timeout, 2) == 0) {
ssize_t bytes_received = mq_timedreceive(mq, large_buffer, sizeof(large_buffer), NULL, &abs_timeout);
if (bytes_received > 0) {
large_buffer&#91;bytes_received] = '\0';
printf("✓ 接收到完整消息 (%zd 字节)\n", bytes_received);
}
}
}

// 演示优先级接收
printf("\n5. 优先级接收演示:\n");

// 发送不同优先级的消息
struct {
const char* message;
unsigned int priority;
} priority_messages&#91;] = {
{"低优先级消息", 1},
{"中优先级消息", 5},
{"高优先级消息", 10},
{"最高优先级消息", 15}
};

for (int i = 0; i < 4; i++) {
if (mq_send(mq, priority_messages&#91;i].message, strlen(priority_messages&#91;i].message),
priority_messages&#91;i].priority) == 0) {
printf("发送: %s (优先级: %u)\n", priority_messages&#91;i].message, priority_messages&#91;i].priority);
}
}

// 接收消息(应该按优先级顺序接收)
printf("按优先级顺序接收消息:\n");
if (calculate_absolute_timeout(&abs_timeout, 3) == 0) {
for (int i = 0; i < 4; i++) {
ssize_t bytes_received = mq_timedreceive(mq, buffer, sizeof(buffer), &priority, &abs_timeout);
if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
printf("接收: %s (优先级: %u)\n", buffer, priority);
}
}
}

// 清理资源
printf("\n6. 清理资源:\n");
mq_close(mq);
mq_unlink(queue_name);
printf("队列已清理\n");

printf("\n=== 基础mq_timedreceive演示完成 ===\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
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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>

#define MAX_REQUESTS 100
#define REQUEST_SIZE 512

volatile sig_atomic_t server_running = 1;

// 服务器请求结构
typedef struct {
char client_id&#91;32];
char request_data&#91;256];
time_t timestamp;
int request_id;
} server_request_t;

// 服务器响应结构
typedef struct {
int request_id;
char response_data&#91;256];
int status;
time_t timestamp;
} server_response_t;

// 信号处理函数
void signal_handler(int sig) {
printf("服务器收到停止信号 %d\n", sig);
server_running = 0;
}

// 创建服务器队列
mqd_t create_server_queues(const char* request_queue, const char* response_queue) {
struct mq_attr request_attr = {
.mq_flags = 0,
.mq_maxmsg = 20,
.mq_msgsize = sizeof(server_request_t),
.mq_curmsgs = 0
};

struct mq_attr response_attr = {
.mq_flags = 0,
.mq_maxmsg = 20,
.mq_msgsize = sizeof(server_response_t),
.mq_curmsgs = 0
};

// 创建请求队列
mqd_t req_mq = mq_open(request_queue, O_CREAT | O_RDONLY, 0644, &request_attr);
if (req_mq == (mqd_t)-1) {
perror("创建请求队列失败");
return -1;
}

// 创建响应队列
mqd_t resp_mq = mq_open(response_queue, O_CREAT | O_WRONLY, 0644, &response_attr);
if (resp_mq == (mqd_t)-1) {
perror("创建响应队列失败");
mq_close(req_mq);
return -1;
}

printf("服务器队列创建成功:\n");
printf(" 请求队列: %s\n", request_queue);
printf(" 响应队列: %s\n", response_queue);

return req_mq; // 返回请求队列描述符
}

// 计算相对超时时间
int calculate_relative_timeout(struct timespec* abs_timeout, int milliseconds) {
if (clock_gettime(CLOCK_REALTIME, abs_timeout) == -1) {
perror("获取当前时间失败");
return -1;
}

long seconds = milliseconds / 1000;
long nanoseconds = (milliseconds % 1000) * 1000000;

abs_timeout->tv_sec += seconds;
abs_timeout->tv_nsec += nanoseconds;

if (abs_timeout->tv_nsec >= 1000000000) {
abs_timeout->tv_sec++;
abs_timeout->tv_nsec -= 1000000000;
}

return 0;
}

// 处理服务器请求
void process_server_requests(mqd_t req_mq, mqd_t resp_mq) {
printf("服务器开始处理请求...\n");

int processed_requests = 0;
time_t last_status_time = time(NULL);

while (server_running) {
struct timespec abs_timeout;
if (calculate_relative_timeout(&abs_timeout, 1000) == -1) { // 1秒超时
continue;
}

server_request_t request;
unsigned int priority;

ssize_t bytes_received = mq_timedreceive(req_mq, (char*)&request, sizeof(request), &priority, &abs_timeout);

if (bytes_received > 0) {
// 处理请求
printf("处理请求 #%d 来自客户端 %s\n", request.request_id, request.client_id);

// 模拟处理时间
usleep(100000); // 0.1秒

// 构造响应
server_response_t response;
response.request_id = request.request_id;
snprintf(response.response_data, sizeof(response.response_data),
"请求 #%d 已处理完成", request.request_id);
response.status = 200;
response.timestamp = time(NULL);

// 发送响应
if (mq_send(resp_mq, (char*)&response, sizeof(response), priority) == 0) {
printf("响应已发送: 请求 #%d\n", request.request_id);
processed_requests++;
} else {
printf("发送响应失败: %s\n", strerror(errno));
}
} else if (errno == ETIMEDOUT || errno == EAGAIN) {
// 超时或无消息,继续循环
} else {
printf("接收请求失败: %s\n", strerror(errno));
if (errno != EINTR) {
break;
}
}

// 定期显示状态
time_t current_time = time(NULL);
if (current_time - last_status_time >= 5) {
printf("服务器状态: 已处理 %d 个请求\n", processed_requests);
last_status_time = current_time;
}
}

printf("服务器停止,总共处理 %d 个请求\n", processed_requests);
}

// 客户端模拟器
void client_simulator(const char* request_queue, const char* response_queue, int client_id) {
printf("客户端 %d 启动\n", client_id);

// 打开队列
mqd_t req_mq = mq_open(request_queue, O_WRONLY);
mqd_t resp_mq = mq_open(response_queue, O_RDONLY);

if (req_mq == (mqd_t)-1 || resp_mq == (mqd_t)-1) {
perror("客户端打开队列失败");
if (req_mq != (mqd_t)-1) mq_close(req_mq);
if (resp_mq != (mqd_t)-1) mq_close(resp_mq);
exit(EXIT_FAILURE);
}

srand(time(NULL) + client_id);

// 发送请求
for (int i = 0; i < 5; i++) {
server_request_t request;
snprintf(request.client_id, sizeof(request.client_id), "Client_%d", client_id);
snprintf(request.request_data, sizeof(request.request_data), "请求数据_%d", i + 1);
request.timestamp = time(NULL);
request.request_id = client_id * 100 + i + 1;

unsigned int priority = rand() % 10;

if (mq_send(req_mq, (char*)&request, sizeof(request), priority) == 0) {
printf("客户端 %d 发送请求 #%d\n", client_id, request.request_id);
} else {
printf("客户端 %d 发送请求失败: %s\n", client_id, strerror(errno));
continue;
}

// 等待响应
struct timespec abs_timeout;
if (calculate_relative_timeout(&abs_timeout, 3000) == 0) { // 3秒超时
server_response_t response;
ssize_t bytes_received = mq_timedreceive(resp_mq, (char*)&response, sizeof(response), NULL, &abs_timeout);

if (bytes_received > 0) {
printf("客户端 %d 收到响应: %s (状态: %d)\n",
client_id, response.response_data, response.status);
} else if (errno == ETIMEDOUT) {
printf("客户端 %d 等待响应超时\n", client_id);
} else {
printf("客户端 %d 接收响应失败: %s\n", client_id, strerror(errno));
}
}

sleep(1); // 客户端间隔
}

mq_close(req_mq);
mq_close(resp_mq);
printf("客户端 %d 完成\n", client_id);
}

int main() {
printf("=== 服务器应用超时消息处理示例 ===\n");

const char* request_queue = "/server_requests";
const char* response_queue = "/server_responses";

// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

// 启动服务器进程
pid_t server_pid = fork();
if (server_pid == 0) {
// 服务器进程
mqd_t req_mq = mq_open(request_queue, O_RDONLY);
mqd_t resp_mq = mq_open(response_queue, O_WRONLY);

if (req_mq == (mqd_t)-1 || resp_mq == (mqd_t)-1) {
perror("服务器打开队列失败");
exit(EXIT_FAILURE);
}

process_server_requests(req_mq, resp_mq);

mq_close(req_mq);
mq_close(resp_mq);
exit(EXIT_SUCCESS);
}

// 等待服务器启动
sleep(1);

// 启动多个客户端进程
pid_t clients&#91;3];
for (int i = 0; i < 3; i++) {
clients&#91;i] = fork();
if (clients&#91;i] == 0) {
client_simulator(request_queue, response_queue, i + 1);
exit(EXIT_SUCCESS);
}
}

// 等待客户端完成
for (int i = 0; i < 3; i++) {
waitpid(clients&#91;i], NULL, 0);
}

// 停止服务器
server_running = 0;
sleep(2);
waitpid(server_pid, NULL, 0);

// 清理队列
mq_unlink(request_queue);
mq_unlink(response_queue);

printf("\n=== 服务器应用演示完成 ===\n");

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 编译示例(需要链接实时库)
gcc -o mq_unlink_example1 mq_unlink_example1.c -lrt
gcc -o mq_timedsend_example1 mq_timedsend_example1.c -lrt
gcc -o mq_timedsend_example2 mq_timedsend_example2.c -lrt
gcc -o mq_timedreceive_example1 mq_timedreceive_example1.c -lrt
gcc -o mq_timedreceive_example2 mq_timedreceive_example2.c -lrt

# 运行示例
./mq_unlink_example1
./mq_timedsend_example1
./mq_timedsend_example2
./mq_timedreceive_example1
./mq_timedreceive_example2

重要注意事项

权限要求: 需要适当的文件系统权限来创建和访问消息队列

名称规范: 消息队列名称必须以’/‘开头

超时时间: 使用绝对时间而非相对时间

资源管理: 必须正确关闭队列描述符和删除队列

错误处理: 必须检查返回值并处理各种错误情况

线程安全: 消息队列操作是线程安全的

系统限制: 受系统消息队列数量和大小限制

最佳实践

资源清理: 及时关闭队列描述符和删除不需要的队列

超时设置: 合理设置超时时间以避免无限期等待

错误处理: 完善的错误处理和恢复机制

优先级使用: 合理使用消息优先级

缓冲区管理: 确保缓冲区大小足够

信号处理: 正确处理信号中断

性能监控: 监控队列性能和系统资源使用

通过这些示例,你可以理解POSIX消息队列相关函数在进程间通信方面的强大功能,它们为Linux系统提供了高效、可靠的IPC机制,特别适用于实时系统、服务器应用和分布式系统。

mq_timedsend系统调用及示例

mq_timedsend函数详解

  1. 函数介绍

mq_timedsend函数是Linux系统中用于在指定时间内发送消息到POSIX消息队列的函数。它是mq_send函数的增强版本,支持超时控制。可以把mq_timedsend想象成一个”限时消息发送器”,它能够在指定的时间内尝试发送消息,如果超时则返回错误。

这个函数特别适用于需要控制发送等待时间的场景,比如实时系统或需要避免无限期阻塞的应用程序。

使用场景:

  • 实时系统的消息发送

  • 避免无限期阻塞的发送操作

  • 超时控制的网络应用

  • 高可用性系统中的消息处理

  1. 函数原型
1
2
3
4
5
6
#include <mqueue.h>
#include <time.h>

int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len,
unsigned int msg_prio, const struct timespec *abs_timeout);

  1. 功能

mq_timedsend函数的主要功能是在指定的绝对超时时间内发送消息到消息队列。如果队列已满且在超时时间内无法发送,则返回错误。

  1. 参数

mqdes: 消息队列描述符

  • 类型:mqd_t

  • 含义:已打开的消息队列描述符

msg_ptr: 消息内容指针

  • 类型:const char*

  • 含义:指向要发送的消息内容

msg_len: 消息长度

  • 类型:size_t

  • 含义:消息内容的字节数

msg_prio: 消息优先级

  • 类型:unsigned int

  • 含义:消息的优先级(0-32767)

abs_timeout: 绝对超时时间

  • 类型:const struct timespec*

  • 含义:绝对超时时间(基于CLOCK_REALTIME)

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

失败: 返回-1,并设置errno错误码

  • EAGAIN:超时时间内无法发送消息

  • EBADF:无效的消息队列描述符

  • EINTR:被信号中断

  • EINVAL:参数无效

  • EMSGSIZE:消息大小超过队列限制

  • ETIMEDOUT:超时

  1. 相似函数或关联函数
  • mq_send(): 发送消息(阻塞)

  • mq_receive(): 接收消息

  • mq_timedreceive(): 限时接收消息

  • clock_gettime(): 获取当前时间

  • pthread_cond_timedwait(): 限时条件等待

  1. 示例代码

示例1:基础mq_timedsend使用 - 超时控制发送

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <time.h>

// 创建带限制的消息队列
mqd_t create_limited_queue(const char* name) {
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = 2, // 很小的队列容量
.mq_msgsize = 128,
.mq_curmsgs = 0
};

mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("创建消息队列失败");
return -1;
}

printf("创建限制队列: %s (容量: %ld)\n", name, attr.mq_maxmsg);
return mq;
}

// 计算绝对超时时间
int calculate_absolute_timeout(struct timespec* abs_timeout, int seconds) {
if (clock_gettime(CLOCK_REALTIME, abs_timeout) == -1) {
perror("获取当前时间失败");
return -1;
}

abs_timeout->tv_sec += seconds;
return 0;
}

int main() {
printf("=== 基础mq_timedsend使用示例 ===\n");

const char* queue_name = "/timed_queue";

// 创建限制队列
mqd_t mq = create_limited_queue(queue_name);
if (mq == -1) {
exit(EXIT_FAILURE);
}

// 填满队列
printf("1. 填满队列:\n");
for (int i = 0; i < 2; i++) {
char message&#91;64];
snprintf(message, sizeof(message), "填充消息 %d", i + 1);

if (mq_send(mq, message, strlen(message), 0) == -1) {
perror("发送填充消息失败");
} else {
printf("发送: %s\n", message);
}
}

// 显示队列状态
struct mq_attr attr;
if (mq_getattr(mq, &attr) == 0) {
printf("队列当前消息数: %ld/%ld\n", attr.mq_curmsgs, attr.mq_maxmsg);
}

// 演示mq_timedsend超时
printf("\n2. 演示mq_timedsend超时:\n");

struct timespec abs_timeout;
if (calculate_absolute_timeout(&abs_timeout, 3) == -1) { // 3秒超时
mq_close(mq);
mq_unlink(queue_name);
exit(EXIT_FAILURE);
}

char test_message&#91;] = "超时测试消息";
printf("尝试发送消息(队列已满,3秒超时):\n");

clock_t start_time = clock();
int result = mq_timedsend(mq, test_message, strlen(test_message), 0, &abs_timeout);
clock_t end_time = clock();

double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;

if (result == 0) {
printf("✓ 消息发送成功\n");
} else {
if (errno == ETIMEDOUT) {
printf("✗ 发送超时 (耗时: %.2f 秒)\n", elapsed_time);
} else if (errno == EAGAIN) {
printf("✗ 队列满,无法发送: %s\n", strerror(errno));
} else {
printf("✗ 发送失败: %s\n", strerror(errno));
}
}

// 演示成功的mq_timedsend
printf("\n3. 演示成功的mq_timedsend:\n");

// 先接收一条消息,为发送腾出空间
char buffer&#91;128];
ssize_t bytes_received = mq_receive(mq, buffer, sizeof(buffer), NULL);
if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
printf("接收消息为发送腾出空间: %s\n", buffer);
}

// 现在队列有空间了
if (calculate_absolute_timeout(&abs_timeout, 5) == 0) { // 5秒超时
char success_message&#91;] = "成功发送的消息";
printf("发送消息(队列有空间):\n");

if (mq_timedsend(mq, success_message, strlen(success_message), 5, &abs_timeout) == 0) {
printf("✓ 消息发送成功 (优先级: 5)\n");
} else {
printf("✗ 发送失败: %s\n", strerror(errno));
}
}

// 演示不同超时时间的效果
printf("\n4. 不同超时时间演示:\n");

// 立即超时(过去的时间)
struct timespec past_time = {0, 0};
char immediate_message&#91;] = "立即超时消息";
printf("使用过去时间作为超时(立即返回):\n");
if (mq_timedsend(mq, immediate_message, strlen(immediate_message), 0, &past_time) == -1) {
if (errno == ETIMEDOUT) {
printf("✓ 立即超时 (预期行为)\n");
} else {
printf("✗ 其他错误: %s\n", strerror(errno));
}
}

// 长时间超时
if (calculate_absolute_timeout(&abs_timeout, 10) == 0) { // 10秒超时
char long_timeout_message&#91;] = "长超时消息";
printf("使用长超时时间:\n");
if (mq_timedsend(mq, long_timeout_message, strlen(long_timeout_message), 1, &abs_timeout) == 0) {
printf("✓ 长超时发送成功\n");
} else {
printf("✗ 长超时发送失败: %s\n", strerror(errno));
}
}

// 清理资源
printf("\n5. 清理资源:\n");
mq_close(mq);
mq_unlink(queue_name);
printf("队列已清理\n");

printf("\n=== 基础mq_timedsend演示完成 ===\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
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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>

#define MAX_MESSAGES 10
#define MESSAGE_SIZE 256

volatile sig_atomic_t stop_flag = 0;

// 信号处理函数
void signal_handler(int sig) {
printf("收到信号 %d,准备停止...\n", sig);
stop_flag = 1;
}

// 创建实时消息队列
mqd_t create_realtime_queue(const char* name) {
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = 5,
.mq_msgsize = MESSAGE_SIZE,
.mq_curmsgs = 0
};

mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("创建实时队列失败");
return -1;
}

printf("创建实时队列: %s\n", name);
return mq;
}

// 计算相对超时时间
int calculate_relative_timeout(struct timespec* abs_timeout, int milliseconds) {
if (clock_gettime(CLOCK_REALTIME, abs_timeout) == -1) {
perror("获取当前时间失败");
return -1;
}

// 转换毫秒到秒和纳秒
long seconds = milliseconds / 1000;
long nanoseconds = (milliseconds % 1000) * 1000000;

abs_timeout->tv_sec += seconds;
abs_timeout->tv_nsec += nanoseconds;

// 处理纳秒溢出
if (abs_timeout->tv_nsec >= 1000000000) {
abs_timeout->tv_sec++;
abs_timeout->tv_nsec -= 1000000000;
}

return 0;
}

// 实时消息发送器
void realtime_message_sender(mqd_t mq, const char* sender_name) {
printf("实时发送器 %s 启动\n", sender_name);

srand(time(NULL));

int message_count = 0;
while (!stop_flag && message_count < MAX_MESSAGES) {
char message&#91;MESSAGE_SIZE];
snprintf(message, sizeof(message), "%s: 实时消息 %d", sender_name, message_count + 1);

// 随机超时时间(10-100毫秒)
int timeout_ms = 10 + rand() % 91;

struct timespec abs_timeout;
if (calculate_relative_timeout(&abs_timeout, timeout_ms) == -1) {
continue;
}

// 使用mq_timedsend发送消息
unsigned int priority = rand() % 10;
int result = mq_timedsend(mq, message, strlen(message), priority, &abs_timeout);

if (result == 0) {
printf("&#91;%s] 发送成功: %s (优先级: %u, 超时: %dms)\n",
sender_name, message, priority, timeout_ms);
} else {
if (errno == ETIMEDOUT) {
printf("&#91;%s] 发送超时: %s (超时: %dms)\n",
sender_name, message, timeout_ms);
} else if (errno == EAGAIN) {
printf("&#91;%s] 队列满,发送失败: %s\n", sender_name, message);
} else {
printf("&#91;%s] 发送错误: %s (%s)\n",
sender_name, message, strerror(errno));
}
}

message_count++;
usleep(500000); // 0.5秒间隔
}

printf("实时发送器 %s 完成\n", sender_name);
}

// 消息接收器
void message_receiver(mqd_t mq, const char* receiver_name) {
printf("消息接收器 %s 启动\n", receiver_name);

char buffer&#91;MESSAGE_SIZE];
ssize_t bytes_received;
unsigned int priority;
int received_count = 0;

while (!stop_flag && received_count < MAX_MESSAGES * 2) {
struct timespec abs_timeout;
if (calculate_relative_timeout(&abs_timeout, 2000) == -1) { // 2秒超时
continue;
}

bytes_received = mq_timedreceive(mq, buffer, sizeof(buffer), &priority, &abs_timeout);

if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
printf("&#91;%s] 接收: %s (优先级: %u)\n", receiver_name, buffer, priority);
received_count++;
} else if (errno == ETIMEDOUT) {
printf("&#91;%s] 接收超时\n", receiver_name);
} else if (errno == EAGAIN) {
printf("&#91;%s] 暂无消息\n", receiver_name);
usleep(100000); // 0.1秒后重试
} else {
printf("&#91;%s] 接收错误: %s\n", receiver_name, strerror(errno));
break;
}
}

printf("消息接收器 %s 完成,接收 %d 条消息\n", receiver_name, received_count);
}

int main() {
printf("=== 实时系统超时消息发送示例 ===\n");

const char* queue_name = "/realtime_queue";

// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

// 创建实时队列
mqd_t mq = create_realtime_queue(queue_name);
if (mq == -1) {
exit(EXIT_FAILURE);
}

// 启动发送器和接收器
pid_t sender1 = fork();
if (sender1 == 0) {
realtime_message_sender(mq, "发送器1");
exit(EXIT_SUCCESS);
}

pid_t sender2 = fork();
if (sender2 == 0) {
realtime_message_sender(mq, "发送器2");
exit(EXIT_SUCCESS);
}

pid_t receiver = fork();
if (receiver == 0) {
message_receiver(mq, "接收器");
exit(EXIT_SUCCESS);
}

// 主进程等待一段时间后发送停止信号
printf("系统运行中... 按Ctrl+C停止或等待30秒\n");

int elapsed = 0;
while (elapsed < 30 && !stop_flag) {
sleep(1);
elapsed++;

// 定期显示队列状态
if (elapsed % 5 == 0) {
struct mq_attr attr;
if (mq_getattr(mq, &attr) == 0) {
printf("队列状态: %ld/%ld 消息\n", attr.mq_curmsgs, attr.mq_maxmsg);
}
}
}

// 发送停止信号
stop_flag = 1;
printf("发送停止信号...\n");

// 等待所有子进程完成
waitpid(sender1, NULL, 0);
waitpid(sender2, NULL, 0);
waitpid(receiver, NULL, 0);

// 清理资源
mq_close(mq);
mq_unlink(queue_name);
printf("系统已停止,资源已清理\n");

printf("\n=== 实时系统演示完成 ===\n");

return 0;
}

mq_unlink系统调用及示例

mq_unlink函数详解

  1. 函数介绍

mq_unlink函数是Linux系统中用于删除POSIX消息队列的函数。可以把mq_unlink想象成一个”消息队列删除器”,它能够从系统中移除指定名称的消息队列。

POSIX消息队列是进程间通信(IPC)的一种机制,允许不同进程通过队列发送和接收消息。mq_unlink的作用类似于文件系统的unlink函数,它删除消息队列的名称,但不会立即销毁队列本身。只有当所有打开该队列的进程都关闭了队列描述符后,队列才会被真正销毁。

使用场景:

  • 进程间通信系统的清理

  • 服务器程序的资源管理

  • 系统维护和清理脚本

  • 消息队列生命周期管理

  1. 函数原型
1
2
3
4
#include <mqueue.h>

int mq_unlink(const char *name);

  1. 功能

mq_unlink函数的主要功能是删除指定名称的POSIX消息队列。它从系统中移除队列的名称,使得后续无法通过该名称打开队列,但已打开的队列描述符仍然有效。

  1. 参数

name: 消息队列名称

  • 类型:const char*

  • 含义:要删除的消息队列名称

  • 名称必须以’/‘开头,如”/my_queue”

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

失败: 返回-1,并设置errno错误码

  • EACCES:权限不足

  • ENOENT:指定名称的消息队列不存在

  • EINVAL:名称无效

  1. 相似函数或关联函数
  • mq_open(): 打开或创建消息队列

  • mq_close(): 关闭消息队列描述符

  • mq_send(): 发送消息

  • mq_receive(): 接收消息

  • mq_getattr(): 获取队列属性

  • mq_setattr(): 设置队列属性

  • unlink(): 删除文件

  1. 示例代码

示例1:基础mq_unlink使用 - 简单队列删除

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

// 创建消息队列
mqd_t create_message_queue(const char* name) {
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = 10,
.mq_msgsize = 256,
.mq_curmsgs = 0
};

mqd_t mq = mq_open(name, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("创建消息队列失败");
return -1;
}

printf("创建消息队列: %s (描述符: %d)\n", name, (int)mq);
return mq;
}

// 显示消息队列属性
void show_queue_attributes(mqd_t mq, const char* name) {
struct mq_attr attr;
if (mq_getattr(mq, &attr) == -1) {
perror("获取队列属性失败");
return;
}

printf("队列 %s 属性:\n", name);
printf(" 最大消息数: %ld\n", attr.mq_maxmsg);
printf(" 最大消息大小: %ld\n", attr.mq_msgsize);
printf(" 当前消息数: %ld\n", attr.mq_curmsgs);
printf(" 标志: %ld\n", attr.mq_flags);
}

int main() {
printf("=== 基础mq_unlink使用示例 ===\n");

const char* queue_name = "/test_queue";

// 创建消息队列
printf("1. 创建消息队列:\n");
mqd_t mq = create_message_queue(queue_name);
if (mq == -1) {
exit(EXIT_FAILURE);
}

show_queue_attributes(mq, queue_name);

// 发送一些测试消息
printf("\n2. 发送测试消息:\n");
const char* messages&#91;] = {
"第一条测试消息",
"第二条测试消息",
"第三条测试消息"
};

for (int i = 0; i < 3; i++) {
if (mq_send(mq, messages&#91;i], strlen(messages&#91;i]), 0) == -1) {
perror("发送消息失败");
} else {
printf("发送消息: %s\n", messages&#91;i]);
}
}

show_queue_attributes(mq, queue_name);

// 使用mq_unlink删除队列名称
printf("\n3. 使用mq_unlink删除队列名称:\n");
if (mq_unlink(queue_name) == 0) {
printf("✓ 成功删除队列名称: %s\n", queue_name);
printf("注意: 队列本身仍然存在,因为还有打开的描述符\n");
} else {
printf("✗ 删除队列名称失败: %s\n", strerror(errno));
}

// 验证队列名称已被删除
printf("\n4. 验证队列名称删除效果:\n");
mqd_t mq2 = mq_open(queue_name, O_RDONLY);
if (mq2 == -1) {
printf("✓ 无法通过名称重新打开队列 (预期行为): %s\n", strerror(errno));
} else {
printf("✗ 仍然可以通过名称打开队列\n");
mq_close(mq2);
}

// 原有描述符仍然可以使用
printf("\n5. 原有描述符仍然有效:\n");
char buffer&#91;256];
ssize_t bytes_received;
unsigned int priority;

while ((bytes_received = mq_receive(mq, buffer, sizeof(buffer), &priority)) > 0) {
buffer&#91;bytes_received] = '\0';
printf("接收到消息: %s (优先级: %u)\n", buffer, priority);
}

// 关闭队列描述符(此时队列才会被真正销毁)
printf("\n6. 关闭队列描述符:\n");
if (mq_close(mq) == 0) {
printf("✓ 队列描述符已关闭,队列被真正销毁\n");
} else {
perror("关闭队列描述符失败");
}

printf("\n=== 基础mq_unlink演示完成 ===\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
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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define MAX_MESSAGES 5
#define MESSAGE_SIZE 256

// 生产者进程
void producer_process(const char* queue_name, int producer_id) {
printf("生产者 %d 启动\n", producer_id);

// 打开已存在的队列
mqd_t mq = mq_open(queue_name, O_WRONLY);
if (mq == (mqd_t)-1) {
perror("生产者打开队列失败");
exit(EXIT_FAILURE);
}

srand(time(NULL) + producer_id);

// 发送消息
for (int i = 0; i < MAX_MESSAGES; i++) {
char message&#91;MESSAGE_SIZE];
snprintf(message, sizeof(message), "生产者%d的消息%d", producer_id, i + 1);

// 随机优先级
unsigned int priority = rand() % 10;

if (mq_send(mq, message, strlen(message), priority) == -1) {
perror("发送消息失败");
} else {
printf("生产者 %d 发送: %s (优先级: %u)\n", producer_id, message, priority);
}

sleep(1); // 模拟处理时间
}

printf("生产者 %d 完成\n", producer_id);
mq_close(mq);
}

// 消费者进程
void consumer_process(const char* queue_name, int consumer_id) {
printf("消费者 %d 启动\n", consumer_id);

// 打开已存在的队列
mqd_t mq = mq_open(queue_name, O_RDONLY);
if (mq == (mqd_t)-1) {
perror("消费者打开队列失败");
exit(EXIT_FAILURE);
}

// 接收消息
char buffer&#91;MESSAGE_SIZE];
ssize_t bytes_received;
unsigned int priority;
int message_count = 0;

while (message_count < MAX_MESSAGES * 2) { // 期望接收所有生产者的消息
bytes_received = mq_receive(mq, buffer, sizeof(buffer), &priority);
if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
printf("消费者 %d 接收: %s (优先级: %u)\n", consumer_id, buffer, priority);
message_count++;
} else if (errno == EAGAIN) {
// 非阻塞模式下没有消息
printf("消费者 %d: 暂无消息\n", consumer_id);
sleep(1);
} else {
perror("接收消息失败");
break;
}
}

printf("消费者 %d 完成,接收 %d 条消息\n", consumer_id, message_count);
mq_close(mq);
}

// 管理进程
void manager_process(const char* queue_name) {
printf("管理进程启动\n");

// 创建消息队列
struct mq_attr attr = {
.mq_flags = 0,
.mq_maxmsg = 20,
.mq_msgsize = MESSAGE_SIZE,
.mq_curmsgs = 0
};

mqd_t mq = mq_open(queue_name, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("管理进程创建队列失败");
exit(EXIT_FAILURE);
}

printf("管理进程创建队列: %s\n", queue_name);

// 启动生产者和消费者进程
pid_t producers&#91;2], consumers&#91;2];

// 启动生产者
for (int i = 0; i < 2; i++) {
producers&#91;i] = fork();
if (producers&#91;i] == 0) {
producer_process(queue_name, i + 1);
exit(EXIT_SUCCESS);
}
}

// 启动消费者
for (int i = 0; i < 2; i++) {
consumers&#91;i] = fork();
if (consumers&#91;i] == 0) {
consumer_process(queue_name, i + 1);
exit(EXIT_SUCCESS);
}
}

// 等待生产者完成
printf("管理进程等待生产者完成...\n");
for (int i = 0; i < 2; i++) {
waitpid(producers&#91;i], NULL, 0);
}

printf("所有生产者已完成\n");

// 模拟一段时间让消费者处理完消息
sleep(3);

// 删除队列名称(但队列仍存在,因为消费者还在使用)
printf("管理进程删除队列名称...\n");
if (mq_unlink(queue_name) == 0) {
printf("✓ 队列名称已删除,但队列仍存在(消费者仍在使用)\n");
} else {
printf("✗ 删除队列名称失败: %s\n", strerror(errno));
}

// 等待消费者完成
printf("管理进程等待消费者完成...\n");
for (int i = 0; i < 2; i++) {
waitpid(consumers&#91;i], NULL, 0);
}

printf("所有消费者已完成\n");

// 现在队列才会被真正销毁(所有描述符都已关闭)
printf("队列已被真正销毁\n");
mq_close(mq);

printf("管理进程完成\n");
}

int main() {
printf("=== 多进程共享队列删除管理示例 ===\n");

const char* queue_name = "/shared_queue";

// 启动管理进程
pid_t manager = fork();
if (manager == 0) {
manager_process(queue_name);
exit(EXIT_SUCCESS);
}

// 父进程等待管理进程完成
waitpid(manager, NULL, 0);

// 验证队列是否已被删除
printf("\n验证队列删除效果:\n");
mqd_t mq = mq_open(queue_name, O_RDONLY);
if (mq == -1) {
printf("✓ 队列已成功删除: %s\n", strerror(errno));
} else {
printf("✗ 队列仍然存在\n");
mq_close(mq);
}

printf("\n=== 多进程队列管理演示完成 ===\n");

return 0;
}

POSIX 消息队列 (mq_*) 函数详解

POSIX 消息队列 (mq_*) 函数详解

  1. 函数介绍

POSIX 消息队列是一组用于进程间通信(IPC)的函数,提供了一种可靠的、基于消息的通信机制。可以把消息队列想象成”邮局系统”——发送者将消息放入邮箱(队列),接收者从邮箱中取出消息,就像现实中的邮政服务一样。

与传统的 System V 消息队列相比,POSIX 消息队列具有更好的可移植性和更简洁的 API。它们支持优先级消息、持久化、以及通过文件系统路径名进行命名。

  1. 核心函数原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

// 核心函数
mqd_t mq_open(const char *name, int oflag, ...);
int mq_close(mqd_t mqdes);
int mq_unlink(const char *name);
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
int mq_notify(mqd_t mqdes, const struct sigevent *notification);

  1. 功能

POSIX 消息队列提供以下功能:

  • 创建和打开消息队列

  • 发送和接收消息

  • 设置和获取队列属性

  • 异步通知机制

  • 持久化支持

  • 优先级消息支持

  1. 核心结构体

struct mq_attr

1
2
3
4
5
6
7
struct mq_attr {
long mq_flags; /* 消息队列标志 */
long mq_maxmsg; /* 最大消息数 */
long mq_msgsize; /* 最大消息大小 */
long mq_curmsgs; /* 当前消息数 */
};

struct sigevent (用于通知)

1
2
3
4
5
6
7
8
struct sigevent {
int sigev_notify; /* 通知类型 */
int sigev_signo; /* 信号编号 */
union sigval sigev_value; /* 传递给处理函数的数据 */
void (*sigev_notify_function)(union sigval); /* 线程函数 */
pthread_attr_t *sigev_notify_attributes; /* 线程属性 */
};

  1. 消息队列名称
  • 名称必须以 ‘/’ 开头

  • 长度限制为 NAME_MAX (通常 255 字符)

  • 示例:“/my_queue”, “/app/messages”

  1. 打开标志 (oflag)

标志说明O_RDONLY只读打开O_WRONLY只写打开O_RDWR读写打开O_CREAT不存在时创建O_EXCL与 O_CREAT 一起使用,如果存在则失败O_NONBLOCK非阻塞模式

  1. 返回值
  • mq_open: 成功返回消息队列描述符,失败返回 (mqd_t)-1

  • 其他函数: 成功返回 0,失败返回 -1

  1. 相关函数
  • pthread: 多线程支持

  • signal: 信号处理

  • fcntl: 文件控制

  • unlink: 删除文件

  1. 示例代码

示例1:基础用法 - 简单的消息发送和接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#define QUEUE_NAME "/example_queue"
#define MAX_MSG_SIZE 256
#define MAX_MSGS 10

int main() {
mqd_t mq;
struct mq_attr attr;
char send_buffer&#91;MAX_MSG_SIZE];
char recv_buffer&#91;MAX_MSG_SIZE];
ssize_t bytes_read;
unsigned int priority;

printf("=== POSIX 消息队列基础示例 ===\n\n");

// 设置消息队列属性
attr.mq_flags = 0;
attr.mq_maxmsg = MAX_MSGS;
attr.mq_msgsize = MAX_MSG_SIZE;
attr.mq_curmsgs = 0;

// 创建并打开消息队列
printf("创建消息队列: %s\n", QUEUE_NAME);
mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
printf("✓ 消息队列创建成功\n\n");

// 获取并显示队列属性
printf("消息队列属性:\n");
if (mq_getattr(mq, &attr) == 0) {
printf(" 最大消息数: %ld\n", attr.mq_maxmsg);
printf(" 最大消息大小: %ld 字节\n", attr.mq_msgsize);
printf(" 当前消息数: %ld\n", attr.mq_curmsgs);
printf(" 标志: %ld\n", attr.mq_flags);
}
printf("\n");

// 发送消息
printf("发送消息:\n");
const char* messages&#91;] = {
"第一条消息: Hello, World!",
"第二条消息: 欢迎使用 POSIX 消息队列",
"第三条消息: 这是优先级消息",
"第四条消息: 最后一条测试消息"
};
int priorities&#91;] = {0, 0, 10, 0}; // 优先级 (数值越大优先级越高)

for (int i = 0; i < 4; i++) {
if (mq_send(mq, messages&#91;i], strlen(messages&#91;i]) + 1, priorities&#91;i]) == 0) {
printf(" ✓ 发送消息 %d (优先级 %d): %s\n", i + 1, priorities&#91;i], messages&#91;i]);
} else {
perror(" ✗ mq_send 失败");
}
}

// 显示发送后队列状态
if (mq_getattr(mq, &attr) == 0) {
printf("\n发送后队列状态: %ld 条消息\n", attr.mq_curmsgs);
}

// 接收消息
printf("\n接收消息 (按优先级顺序):\n");
for (int i = 0; i < 4; i++) {
bytes_read = mq_receive(mq, recv_buffer, MAX_MSG_SIZE, &priority);
if (bytes_read != -1) {
printf(" ✓ 接收消息 %d (优先级 %d, 长度 %zd): %s\n",
i + 1, priority, bytes_read, recv_buffer);
} else {
if (errno == EAGAIN) {
printf(" ⚠ 队列为空\n");
break;
} else {
perror(" ✗ mq_receive 失败");
break;
}
}
}

// 显示接收后队列状态
if (mq_getattr(mq, &attr) == 0) {
printf("\n接收后队列状态: %ld 条消息\n", attr.mq_curmsgs);
}

// 关闭消息队列
if (mq_close(mq) == 0) {
printf("✓ 消息队列关闭成功\n");
} else {
perror("✗ mq_close 失败");
}

// 删除消息队列
if (mq_unlink(QUEUE_NAME) == 0) {
printf("✓ 消息队列删除成功\n");
} else {
perror("✗ mq_unlink 失败");
}

printf("\n=== 消息队列特点 ===\n");
printf("1. 支持优先级消息 (数值越大优先级越高)\n");
printf("2. 消息大小可配置\n");
printf("3. 消息数量有限制\n");
printf("4. 支持持久化 (直到显式删除)\n");
printf("5. 可通过文件系统路径访问\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
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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>

#define QUEUE_NAME "/producer_consumer_queue"
#define MAX_MSG_SIZE 256
#define MAX_MSGS 20
#define NUM_MESSAGES 10

// 全局变量
mqd_t mq;
int producer_count = 0;
int consumer_count = 0;
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;

// 生产者线程函数
void* producer_thread(void* arg) {
int producer_id = *(int*)arg;
char message&#91;MAX_MSG_SIZE];
time_t now;

printf("生产者 %d 启动\n", producer_id);

for (int i = 0; i < NUM_MESSAGES; i++) {
// 构造消息
time(&now);
snprintf(message, sizeof(message),
"P%d-MSG%d-TIME:%s", producer_id, i + 1, ctime(&now));

// 发送消息 (交替使用不同优先级)
unsigned int priority = (i % 3 == 0) ? 5 : 1; // 每第3条高优先级

if (mq_send(mq, message, strlen(message) + 1, priority) == 0) {
pthread_mutex_lock(&count_mutex);
producer_count++;
pthread_mutex_unlock(&count_mutex);

printf("生产者 %d 发送消息: %s (优先级 %u)\n",
producer_id, message, priority);
} else {
perror("生产者发送失败");
}

// 随机延迟
usleep((rand() % 100 + 1) * 1000); // 1-100ms
}

printf("生产者 %d 完成\n", producer_id);
return NULL;
}

// 消费者线程函数
void* consumer_thread(void* arg) {
int consumer_id = *(int*)arg;
char message&#91;MAX_MSG_SIZE];
ssize_t bytes_read;
unsigned int priority;

printf("消费者 %d 启动\n", consumer_id);

while (1) {
// 接收消息
bytes_read = mq_receive(mq, message, MAX_MSG_SIZE, &priority);
if (bytes_read != -1) {
pthread_mutex_lock(&count_mutex);
consumer_count++;
int current_count = consumer_count;
pthread_mutex_unlock(&count_mutex);

printf("消费者 %d 接收消息 %d (优先级 %u): %s",
consumer_id, current_count, priority, message);

// 检查是否接收完所有消息
if (current_count >= NUM_MESSAGES * 2) { // 2个生产者
break;
}
} else {
if (errno == EAGAIN) {
// 非阻塞模式下队列为空
usleep(10000); // 10ms
continue;
} else {
perror("消费者接收失败");
break;
}
}

// 随机延迟
usleep((rand() % 50 + 1) * 1000); // 1-50ms
}

printf("消费者 %d 完成\n", consumer_id);
return NULL;
}

int main() {
pthread_t producers&#91;2];
pthread_t consumers&#91;3];
int producer_ids&#91;2] = {1, 2};
int consumer_ids&#91;3] = {1, 2, 3};
struct mq_attr attr;

printf("=== 生产者-消费者消息队列示例 ===\n\n");

// 初始化随机数种子
srand(time(NULL) + getpid());

// 设置消息队列属性
attr.mq_flags = 0; // 阻塞模式
attr.mq_maxmsg = MAX_MSGS;
attr.mq_msgsize = MAX_MSG_SIZE;
attr.mq_curmsgs = 0;

// 创建消息队列
printf("创建消息队列: %s\n", QUEUE_NAME);
mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR | O_NONBLOCK, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
printf("✓ 消息队列创建成功\n\n");

// 创建生产者线程
printf("创建生产者线程...\n");
for (int i = 0; i < 2; i++) {
if (pthread_create(&producers&#91;i], NULL, producer_thread, &producer_ids&#91;i]) != 0) {
perror("创建生产者线程失败");
exit(1);
}
}

// 创建消费者线程
printf("创建消费者线程...\n");
for (int i = 0; i < 3; i++) {
if (pthread_create(&consumers&#91;i], NULL, consumer_thread, &consumer_ids&#91;i]) != 0) {
perror("创建消费者线程失败");
exit(1);
}
}

// 等待生产者完成
printf("等待生产者完成...\n");
for (int i = 0; i < 2; i++) {
pthread_join(producers&#91;i], NULL);
}

// 等待消费者完成
printf("等待消费者完成...\n");
for (int i = 0; i < 3; i++) {
pthread_join(consumers&#91;i], NULL);
}

// 显示统计信息
printf("\n=== 统计信息 ===\n");
printf("生产消息数: %d\n", producer_count);
printf("消费消息数: %d\n", consumer_count);

// 显示最终队列状态
if (mq_getattr(mq, &attr) == 0) {
printf("队列中剩余消息: %ld\n", attr.mq_curmsgs);
}

// 清理资源
if (mq_close(mq) == 0) {
printf("✓ 消息队列关闭成功\n");
}
if (mq_unlink(QUEUE_NAME) == 0) {
printf("✓ 消息队列删除成功\n");
}

printf("\n=== 生产者-消费者模型特点 ===\n");
printf("1. 解耦: 生产者和消费者独立运行\n");
printf("2. 异步: 生产和消费可以不同步进行\n");
printf("3. 缓冲: 消息队列提供缓冲作用\n");
printf("4. 负载均衡: 多个消费者可以并行处理\n");
printf("5. 可靠性: 消息持久化存储\n");

return 0;
}

示例3:完整的消息队列管理系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <time.h>

// 配置结构体
struct mq_config {
char *queue_name;
int max_messages;
int max_message_size;
int create_queue;
int delete_queue;
int show_info;
int send_message;
int receive_message;
int list_queues;
int priority;
char *message_content;
int non_blocking;
int verbose;
};

// 全局变量
volatile sig_atomic_t running = 1;

// 信号处理函数
void signal_handler(int sig) {
printf("\n收到信号 %d,准备退出...\n", sig);
running = 0;
}

// 设置信号处理
void setup_signal_handlers() {
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;

sigaction(SIGINT, &sa, NULL); // Ctrl+C
sigaction(SIGTERM, &sa, NULL); // 终止信号
}

// 显示消息队列信息
void show_queue_info(mqd_t mq) {
struct mq_attr attr;

if (mq_getattr(mq, &attr) == 0) {
printf("消息队列属性:\n");
printf(" 最大消息数: %ld\n", attr.mq_maxmsg);
printf(" 最大消息大小: %ld 字节\n", attr.mq_msgsize);
printf(" 当前消息数: %ld\n", attr.mq_curmsgs);
printf(" 标志: %s\n", (attr.mq_flags & O_NONBLOCK) ? "非阻塞" : "阻塞");
} else {
perror("获取队列属性失败");
}
}

// 列出所有消息队列
void list_all_queues() {
printf("=== 系统消息队列列表 ===\n");
printf("注意: POSIX 消息队列通常在 /dev/mqueue/ 目录下\n");

// 尝试列出 /dev/mqueue/ 目录
if (access("/dev/mqueue", F_OK) == 0) {
printf("系统消息队列目录存在\n");
system("ls -la /dev/mqueue/ 2>/dev/null || echo '无法访问 /dev/mqueue/'");
} else {
printf("系统消息队列目录不存在\n");
}
printf("\n");
}

// 发送消息
int send_message_to_queue(mqd_t mq, const char *message, int priority, int non_blocking) {
struct mq_attr attr;

// 检查消息大小
if (mq_getattr(mq, &attr) == 0) {
if (strlen(message) + 1 > (size_t)attr.mq_msgsize) {
fprintf(stderr, "错误: 消息大小 (%zu) 超过队列限制 (%ld)\n",
strlen(message) + 1, attr.mq_msgsize);
return -1;
}
}

// 发送消息
if (mq_send(mq, message, strlen(message) + 1, priority) == 0) {
printf("✓ 消息发送成功 (优先级 %d): %s\n", priority, message);
return 0;
} else {
if (errno == EAGAIN && non_blocking) {
printf("⚠ 队列已满,非阻塞模式下发送失败\n");
} else {
perror("✗ 消息发送失败");
}
return -1;
}
}

// 接收消息
int receive_message_from_queue(mqd_t mq, int non_blocking) {
char *buffer;
struct mq_attr attr;
ssize_t bytes_read;
unsigned int priority;

// 获取队列属性以确定缓冲区大小
if (mq_getattr(mq, &attr) != 0) {
perror("获取队列属性失败");
return -1;
}

buffer = malloc(attr.mq_msgsize);
if (!buffer) {
perror("内存分配失败");
return -1;
}

// 接收消息
bytes_read = mq_receive(mq, buffer, attr.mq_msgsize, &priority);
if (bytes_read != -1) {
printf("✓ 消息接收成功 (优先级 %u, 长度 %zd): %s",
priority, bytes_read, buffer);
free(buffer);
return 0;
} else {
if (errno == EAGAIN && non_blocking) {
printf("⚠ 队列为空,非阻塞模式下接收失败\n");
} else {
perror("✗ 消息接收失败");
}
free(buffer);
return -1;
}
}

// 创建消息队列
mqd_t create_message_queue(const char *name, int max_msgs, int max_size, int non_blocking) {
struct mq_attr attr;
int flags = O_CREAT | O_RDWR;

if (non_blocking) {
flags |= O_NONBLOCK;
}

attr.mq_flags = non_blocking ? O_NONBLOCK : 0;
attr.mq_maxmsg = max_msgs;
attr.mq_msgsize = max_size;
attr.mq_curmsgs = 0;

mqd_t mq = mq_open(name, flags, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("创建消息队列失败");
return (mqd_t)-1;
}

printf("✓ 消息队列创建成功: %s\n", name);
return mq;
}

// 打开现有消息队列
mqd_t open_existing_queue(const char *name, int non_blocking) {
int flags = O_RDWR;

if (non_blocking) {
flags |= O_NONBLOCK;
}

mqd_t mq = mq_open(name, flags);
if (mq == (mqd_t)-1) {
perror("打开消息队列失败");
return (mqd_t)-1;
}

printf("✓ 消息队列打开成功: %s\n", name);
return mq;
}

// 显示帮助信息
void show_help(const char *program_name) {
printf("用法: %s &#91;选项]\n", program_name);
printf("\n选项:\n");
printf(" -n, --name=NAME 消息队列名称 (以 / 开头)\n");
printf(" -c, --create 创建消息队列\n");
printf(" -d, --delete 删除消息队列\n");
printf(" -i, --info 显示队列信息\n");
printf(" -l, --list 列出所有队列\n");
printf(" -s, --send=MESSAGE 发送消息\n");
printf(" -r, --receive 接收消息\n");
printf(" -p, --priority=NUM 消息优先级 (默认 0)\n");
printf(" -m, --max-msgs=NUM 最大消息数 (创建时使用)\n");
printf(" -z, --max-size=NUM 最大消息大小 (创建时使用)\n");
printf(" -b, --non-blocking 非阻塞模式\n");
printf(" -v, --verbose 详细输出\n");
printf(" -h, --help 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -n /myqueue -c -m 10 -z 256 # 创建队列\n", program_name);
printf(" %s -n /myqueue -s \"Hello World\" # 发送消息\n", program_name);
printf(" %s -n /myqueue -r # 接收消息\n", program_name);
printf(" %s -n /myqueue -i # 显示队列信息\n", program_name);
printf(" %s -n /myqueue -d # 删除队列\n", program_name);
printf(" %s -l # 列出所有队列\n", program_name);
}

int main(int argc, char *argv&#91;]) {
struct mq_config config = {
.queue_name = NULL,
.max_messages = 10,
.max_message_size = 256,
.create_queue = 0,
.delete_queue = 0,
.show_info = 0,
.send_message = 0,
.receive_message = 0,
.list_queues = 0,
.priority = 0,
.message_content = NULL,
.non_blocking = 0,
.verbose = 0
};

printf("=== POSIX 消息队列管理系统 ===\n\n");

// 解析命令行参数
static struct option long_options&#91;] = {
{"name", required_argument, 0, 'n'},
{"create", no_argument, 0, 'c'},
{"delete", no_argument, 0, 'd'},
{"info", no_argument, 0, 'i'},
{"list", no_argument, 0, 'l'},
{"send", required_argument, 0, 's'},
{"receive", no_argument, 0, 'r'},
{"priority", required_argument, 0, 'p'},
{"max-msgs", required_argument, 0, 'm'},
{"max-size", required_argument, 0, 'z'},
{"non-blocking", no_argument, 0, 'b'},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};

int opt;
while ((opt = getopt_long(argc, argv, "n:cdils:rp:m:z:bvh", long_options, NULL)) != -1) {
switch (opt) {
case 'n':
config.queue_name = optarg;
break;
case 'c':
config.create_queue = 1;
break;
case 'd':
config.delete_queue = 1;
break;
case 'i':
config.show_info = 1;
break;
case 'l':
config.list_queues = 1;
break;
case 's':
config.send_message = 1;
config.message_content = optarg;
break;
case 'r':
config.receive_message = 1;
break;
case 'p':
config.priority = atoi(optarg);
break;
case 'm':
config.max_messages = atoi(optarg);
break;
case 'z':
config.max_message_size = atoi(optarg);
break;
case 'b':
config.non_blocking = 1;
break;
case 'v':
config.verbose = 1;
break;
case 'h':
show_help(argv&#91;0]);
return 0;
default:
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv&#91;0]);
return 1;
}
}

// 设置信号处理
setup_signal_handlers();

// 显示系统信息
if (config.verbose) {
printf("系统信息:\n");
printf(" 当前用户 UID: %d\n", getuid());
printf(" 当前进程 PID: %d\n", getpid());
printf(" 消息队列支持: ");
system("ls /dev/mqueue/ >/dev/null 2>&1 && echo '是' || echo '否'");
printf("\n");
}

// 列出所有队列
if (config.list_queues) {
list_all_queues();
if (!config.queue_name && !config.create_queue && !config.delete_queue &&
!config.show_info && !config.send_message && !config.receive_message) {
return 0;
}
}

// 如果没有指定队列名称且需要操作队列
if (!config.queue_name && (config.create_queue || config.delete_queue ||
config.show_info || config.send_message ||
config.receive_message)) {
fprintf(stderr, "错误: 需要指定消息队列名称\n");
fprintf(stderr, "使用 '%s --help' 查看帮助信息\n", argv&#91;0]);
return 1;
}

// 处理队列操作
mqd_t mq = (mqd_t)-1;

if (config.create_queue) {
mq = create_message_queue(config.queue_name, config.max_messages,
config.max_message_size, config.non_blocking);
if (mq == (mqd_t)-1) {
return 1;
}

if (config.show_info) {
show_queue_info(mq);
}
} else if (config.queue_name) {
// 打开现有队列
mq = open_existing_queue(config.queue_name, config.non_blocking);
if (mq == (mqd_t)-1) {
return 1;
}
}

// 显示队列信息
if (config.show_info && mq != (mqd_t)-1) {
show_queue_info(mq);
}

// 发送消息
if (config.send_message && config.message_content && mq != (mqd_t)-1) {
send_message_to_queue(mq, config.message_content,
config.priority, config.non_blocking);
}

// 接收消息
if (config.receive_message && mq != (mqd_t)-1) {
if (config.non_blocking) {
receive_message_from_queue(mq, config.non_blocking);
} else {
printf("等待接收消息 (按 Ctrl+C 退出)...\n");
while (running) {
if (receive_message_from_queue(mq, config.non_blocking) == -1) {
if (errno != EAGAIN) {
break;
}
}
if (!config.non_blocking) {
sleep(1); // 阻塞模式下定期检查
}
}
}
}

// 删除队列
if (config.delete_queue && config.queue_name) {
if (mq_unlink(config.queue_name) == 0) {
printf("✓ 消息队列删除成功: %s\n", config.queue_name);
} else {
perror("✗ 消息队列删除失败");
}
}

// 关闭队列
if (mq != (mqd_t)-1) {
if (mq_close(mq) == 0) {
if (config.verbose) {
printf("✓ 消息队列关闭成功\n");
}
} else {
perror("✗ 消息队列关闭失败");
}
}

// 显示使用建议
printf("\n=== POSIX 消息队列使用建议 ===\n");
printf("适用场景:\n");
printf("1. 进程间通信 (IPC)\n");
printf("2. 生产者-消费者模式\n");
printf("3. 异步消息处理\n");
printf("4. 系统服务通信\n");
printf("5. 微服务架构\n");
printf("\n");
printf("优势:\n");
printf("1. 可靠性: 消息持久化存储\n");
printf("2. 优先级: 支持消息优先级\n");
printf("3. 可移植: POSIX 标准\n");
printf("4. 灵活性: 支持阻塞和非阻塞模式\n");
printf("5. 安全性: 通过文件系统权限控制\n");
printf("\n");
printf("注意事项:\n");
printf("1. 需要链接实时库: -lrt\n");
printf("2. 队列名称必须以 / 开头\n");
printf("3. 消息大小和数量有限制\n");
printf("4. 需要适当权限才能创建/删除队列\n");
printf("5. 应该及时关闭和清理队列资源\n");

return 0;
}

编译和运行说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 编译示例程序(需要链接实时库)
gcc -o mq_example1 example1.c -lrt
gcc -o mq_example2 example2.c -lrt -lpthread
gcc -o mq_example3 example3.c -lrt -lpthread

# 运行示例
./mq_example1
./mq_example2
./mq_example3 --help

# 基本操作示例
./mq_example3 -n /test_queue -c -m 5 -z 128
./mq_example3 -n /test_queue -s "Hello, Message Queue!"
./mq_example3 -n /test_queue -r
./mq_example3 -n /test_queue -i
./mq_example3 -n /test_queue -d

# 列出所有队列
./mq_example3 -l

系统要求检查

1
2
3
4
5
6
7
8
9
10
11
12
13
# 检查系统支持
ls /dev/mqueue/ 2>/dev/null || echo "消息队列目录不存在"

# 检查内核配置
grep -i mq /boot/config-$(uname -r)

# 检查库支持
ldd --version

# 查看系统限制
ulimit -a | grep -i msg
cat /proc/sys/fs/mqueue/

重要注意事项

编译要求: 需要链接实时库 -lrt

权限要求: 创建/删除队列通常需要适当权限

名称规范: 队列名称必须以 ‘/’ 开头

资源限制: 受系统消息队列限制约束

清理责任: 应该及时关闭和删除队列

线程安全: 消息队列描述符在多线程间共享是安全的

实际应用场景

微服务通信: 服务间异步消息传递

日志系统: 异步日志记录

任务队列: 后台任务处理

事件驱动: 事件通知和处理

数据流: 实时数据处理管道

系统监控: 状态变更通知

最佳实践

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
// 安全的消息队列操作函数
mqd_t safe_mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr) {
mqd_t mq = mq_open(name, oflag, mode, attr);
if (mq == (mqd_t)-1) {
switch (errno) {
case EACCES:
fprintf(stderr, "权限不足访问队列: %s\n", name);
break;
case EEXIST:
fprintf(stderr, "队列已存在: %s\n", name);
break;
case ENOENT:
fprintf(stderr, "队列不存在: %s\n", name);
break;
case EINVAL:
fprintf(stderr, "无效的队列名称或参数: %s\n", name);
break;
default:
perror("mq_open 失败");
break;
}
}
return mq;
}

// 可靠的消息发送函数
int reliable_mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len,
unsigned msg_prio, int timeout_seconds) {
struct timespec timeout;
int result;

if (timeout_seconds > 0) {
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += timeout_seconds;
result = mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, &timeout);
} else {
result = mq_send(mqdes, msg_ptr, msg_len, msg_prio);
}

return result;
}

// 带重试的消息接收函数
ssize_t retry_mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len,
unsigned *msg_prio, int max_retries) {
ssize_t result;
int retries = 0;

while (retries < max_retries) {
result = mq_receive(mqdes, msg_ptr, msg_len, msg_prio);
if (result != -1) {
return result; // 成功接收
}

if (errno == EAGAIN) {
retries++;
usleep(100000); // 100ms 延迟后重试
} else {
break; // 其他错误,不再重试
}
}

return result;
}

这些示例展示了 POSIX 消息队列的各种使用方法,从基础的消息发送接收到完整的管理系统,帮助你全面掌握 Linux 系统中的消息队列机制。

shmget共享内存系统调用及示例

shmget共享内存系统调用及示例

这次我们介绍 shmget, shmat, shmdt, 和 shmctl 这一组函数,它们构成了 System V 共享内存 (System V Shared Memory) IPC(进程间通信)机制的核心部分。

注意: 虽然 System V IPC 是历史悠久且广泛支持的标准,但在现代 Linux 编程中,POSIX 共享内存 (shm_open, mmap) 和 POSIX 消息队列 通常被认为是更现代、更可移植的选择。不过,理解 System V IPC 仍然很重要,因为它在许多遗留系统和特定场景中仍在使用。

1. 函数介绍

这四个函数共同工作,用于创建、访问、连接、分离和控制 System V 共享内存段。

  • shmget (Shared Memory Get): 创建一个新的共享内存段,或者获取一个已存在的共享内存段的标识符 (ID)。这个 ID 是后续操作该共享内存段的关键。

  • shmat (Shared Memory Attach): 将一个由 shmget 获取的共享内存段连接(或附加)到调用进程的虚拟地址空间中。连接成功后,进程就可以像访问普通内存一样访问这块共享内存。

  • shmdt (Shared Memory Detach): 将一个 previously attached 的共享内存段从调用进程的地址空间中分离(或去附加)。分离后,进程不能再通过之前返回的地址访问该共享内存段。

  • shmctl (Shared Memory Control): 对共享内存段执行控制操作,如获取其状态信息 (IPC_STAT)、设置其权限 (IPC_SET) 或销毁 (IPC_RMID) 该共享内存段。

你可以把共享内存想象成一个公共的“白板”:

shmget: 申请或找到一个特定的白板(通过 ID 标识)。

shmat: 把这个白板挂到你(进程)的墙上,这样你就能在上面写字或看别人写的字了。

shmdt: 把白板从你墙上取下来,你不能再访问它了(但白板本身还在,别人可能还在用)。

shmctl: 检查白板的状态(谁在用,什么时候创建的),修改谁能用它,或者直接把白板撕掉(销毁)。

2. 函数原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <sys/types.h>  // 通常需要
#include <sys/ipc.h> // 必需,包含 IPC_* 常量
#include <sys/shm.h> // 必需,包含 shm* 函数和 shmid_ds 结构

// 获取共享内存段标识符
int shmget(key_t key, size_t size, int shmflg);

// 连接共享内存段到进程地址空间
void *shmat(int shmid, const void *shmaddr, int shmflg);

// 从进程地址空间分离共享内存段
int shmdt(const void *shmaddr);

// 控制共享内存段
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

3. 功能

  • shmget: 根据一个键 (key) 创建或获取一个共享内存段,并返回其唯一标识符 (shmid)。

  • shmat: 将由 shmid 标识的共享内存段映射到调用进程的虚拟内存中,并返回映射后的虚拟地址。

  • shmdt: 将由 shmaddr 指定的共享内存段从调用进程的地址空间中断开连接。

  • shmctl: 根据 cmd 命令对由 shmid 标识的共享内存段执行各种控制操作。

4. 参数详解

shmget

key_t key: 一个键值,用于标识一个全局唯一的共享内存段。

  • 特殊键: IPC_PRIVATE (通常定义为 0) 是一个特殊键,它总是创建一个新的、唯一的共享内存段。

  • 生成键: 通常使用 ftok 函数根据一个路径名和一个项目 ID 来生成一个唯一的 key_t 值。key_t ftok(const char *pathname, int proj_id);

size_t size: 请求的共享内存段的大小(以字节为单位)。

  • 如果是创建新段(IPC_CREAT 被设置且该键尚不存在),则 size 指定新段的大小。

  • 如果是获取已存在的段,则 size 可以为 0,或者必须小于或等于已存在段的大小。

int shmflg: 指定创建标志和权限。
创建标志:

  • IPC_CREAT: 如果指定的 key 不存在,则创建一个新的共享内存段。

  • IPC_EXCL: 与 IPC_CREAT 一起使用时,如果 key 已经存在,则 shmget 调用失败。这可以用来确保创建的是一个全新的段。

权限: 低 9 位用于指定访问权限,格式与文件权限相同(例如 0666 表示所有者、组、其他用户都可读写)。实际权限还会受到进程 umask 的影响。

shmat

  • int shmid: 由 shmget 返回的共享内存段标识符。

const void *shmaddr: 指定共享内存段应连接到进程地址空间的期望地址。

  • NULL (推荐): 让内核选择一个合适的地址。这是最常用也是最安全的方式。

  • 非 NULL: 指定一个具体地址。这需要非常小心,因为可能导致地址冲突或对齐问题。通常需要设置 shmflg 中的 SHM_RND 标志来指示地址可以被调整。

int shmflg: 控制连接行为的标志。

  • SHM_RND: 如果 shmaddr 非 NULL,则将连接地址向下舍入到 SHMLBA(共享内存低端边界)的整数倍。

  • SHM_RDONLY: 将共享内存段连接为只读。如果未设置,则连接为可读可写。

shmdt

  • const void *shmaddr: 由之前成功的 shmat 调用返回的连接地址。

shmctl

  • int shmid: 由 shmget 返回的共享内存段标识符。

int cmd: 指定要执行的控制命令。

  • IPC_STAT: 将共享内存段的当前状态信息复制到 buf 指向的 struct shmid_ds 结构中。

  • IPC_SET: 根据 buf 指向的 struct shmid_ds 结构中的 shm_perm 成员来设置共享内存段的权限和所有者。

  • IPC_RMID: 立即销毁共享内存段。只有当所有进程都已将其分离(shmdt)后,内存才会真正被释放。如果仍有进程 attached,销毁操作会被标记,待所有进程 detach 后才执行。

struct shmid_ds *buf: 一个指向 struct shmid_ds 结构的指针,用于传递或接收共享内存段的状态信息。struct shmid_ds 包含了许多关于共享内存段的元数据,例如:struct shmid_ds { struct ipc_perm shm_perm; // 操作权限 size_t shm_segsz; // 段大小 (字节) time_t shm_atime; // 最后 attach 时间 time_t shm_dtime; // 最后 detach 时间 time_t shm_ctime; // 最后 change 时间 pid_t shm_cpid; // 创建者 PID pid_t shm_lpid; // 最后操作者 PID shmatt_t shm_nattch; // 当前连接的进程数 // … 可能还有其他字段 … };

5. 返回值

shmget:

  • 成功: 返回一个正整数,即共享内存段的标识符 (shmid)。

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

shmat:

  • 成功: 返回共享内存段连接到进程地址空间的虚拟地址。

  • 失败: 返回 (void *) -1 (即 MAP_FAILED,与 mmap 相同),并设置 errno。

shmdt:

  • 成功: 返回 0。

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

shmctl:

  • 成功: 对于 IPC_RMID, IPC_SET 返回 0;对于 IPC_STAT 返回 0 并填充 buf。

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

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

  • POSIX 共享内存: shm_open, shm_unlink, mmap, munmap。这是更现代、更推荐的共享内存方式。

  • System V 消息队列: msgget, msgsnd, msgrcv, msgctl。

  • System V 信号量: semget, semop, semctl。

  • ftok: 用于生成 shmget 所需的 key_t 键值。

  • mmap / munmap: 另一种实现共享内存的方式(通过映射同一文件或使用 MAP_SHARED)。

7. 示例代码

示例 1:父子进程通过 System V 共享内存通信

这个经典的例子演示了如何使用 System V 共享内存在父子进程之间传递数据。

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

#define SHM_SIZE 1024 // 共享内存段大小

int main() {
key_t key;
int shmid;
char *data;
pid_t pid;

// 1. 生成一个唯一的 key (使用 ftok)
// 注意:确保 "/tmp" 存在且可访问
key = ftok("/tmp", 'R'); // 'R' 是项目 ID
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
printf("Generated key: %d\n", (int)key);

// 2. 创建共享内存段 (如果不存在则创建)
shmid = shmget(key, SHM_SIZE, 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
printf("Shared memory segment created/retrieved with ID: %d\n", shmid);

// 3. fork 创建子进程
pid = fork();
if (pid == -1) {
perror("fork");
// 尝试清理已创建的共享内存
shmctl(shmid, IPC_RMID, NULL);
exit(EXIT_FAILURE);
}

if (pid == 0) {
// --- 子进程 ---
printf("Child process (PID: %d) started.\n", getpid());

// 4a. 子进程连接共享内存
data = (char *)shmat(shmid, (void *)0, 0);
if (data == (char *)(-1)) {
perror("shmat in child");
_exit(EXIT_FAILURE);
}
printf("Child: Shared memory attached at address: %p\n", (void *)data);

// 5a. 子进程读取数据
printf("Child: Reading from shared memory: %s\n", data);

// 6a. 子进程修改数据
strncpy(data, "Hello from CHILD process!", SHM_SIZE - 1);
data&#91;SHM_SIZE - 1] = '\0'; // 确保字符串结束
printf("Child: Written to shared memory.\n");

// 7a. 子进程分离共享内存
if (shmdt(data) == -1) {
perror("shmdt in child");
_exit(EXIT_FAILURE);
}
printf("Child: Shared memory detached.\n");

_exit(EXIT_SUCCESS);

} else {
// --- 父进程 ---
printf("Parent process (PID: %d) started.\n", getpid());

// 4b. 父进程连接共享内存
data = (char *)shmat(shmid, (void *)0, 0);
if (data == (char *)(-1)) {
perror("shmat in parent");
// 清理
shmctl(shmid, IPC_RMID, NULL);
exit(EXIT_FAILURE);
}
printf("Parent: Shared memory attached at address: %p\n", (void *)data);

// 5b. 父进程写入初始数据
strncpy(data, "Hello from PARENT process!", SHM_SIZE - 1);
data&#91;SHM_SIZE - 1] = '\0';
printf("Parent: Written initial data to shared memory.\n");

// 等待子进程完成
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
printf("Parent: Child exited with status %d.\n", WEXITSTATUS(status));
} else {
printf("Parent: Child did not exit normally.\n");
}

// 6b. 父进程读取子进程修改后的数据
printf("Parent: Reading modified data from shared memory: %s\n", data);

// 7b. 父进程分离共享内存
if (shmdt(data) == -1) {
perror("shmdt in parent");
// 仍然尝试清理
}
printf("Parent: Shared memory detached.\n");

// 8. 父进程销毁共享内存段
// 只有当所有进程都 detach 后,IPC_RMID 才会真正释放内存
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Parent: Shared memory segment destroyed.\n");
}

return 0;
}

代码解释:

使用 ftok(“/tmp”, ‘R’) 生成一个唯一的 key_t 键。/tmp 是一个通常存在的目录,’R’ 是项目 ID(0-255)。

调用 shmget(key, SHM_SIZE, 0666 | IPC_CREAT) 创建或获取共享内存段。0666 设置了读写权限。

调用 fork() 创建子进程。

父子进程:

  • 都调用 shmat(shmid, NULL, 0) 将共享内存段连接到自己的地址空间。NULL 让内核选择地址。

  • 检查 shmat 的返回值是否为 (char *)-1。

父进程:

  • 先向共享内存写入初始数据。

  • 调用 waitpid 等待子进程结束。

  • 子进程结束后,读取子进程写入的数据。

  • 调用 shmdt 分离共享内存。

  • 调用 shmctl(shmid, IPC_RMID, NULL) 销毁共享内存段。因为此时子进程已经 detach,所以内存会被立即释放。

子进程:

  • 读取父进程写入的初始数据。

  • 向共享内存写入自己的数据。

  • 调用 shmdt 分离共享内存。

  • 使用 _exit 退出。

示例 2:检查共享内存段状态

这个例子演示了如何使用 shmctl 的 IPC_STAT 命令来获取共享内存段的详细信息。

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
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int main() {
key_t key;
int shmid;
struct shmid_ds shmid_struct;

// 1. 生成 key
key = ftok(".", 'S'); // 使用当前目录
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}

// 2. 创建共享内存段
shmid = shmget(key, 2048, 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
printf("Shared memory segment created with ID: %d\n", shmid);

// 3. 获取并打印共享内存段状态
if (shmctl(shmid, IPC_STAT, &shmid_struct) == -1) {
perror("shmctl IPC_STAT");
shmctl(shmid, IPC_RMID, NULL); // 清理
exit(EXIT_FAILURE);
}

printf("\n--- Shared Memory Segment Status ---\n");
printf("Key: %d\n", (int)shmid_struct.shm_perm.__key); // 注意:成员名可能因系统而异
printf("ID: %d\n", shmid);
printf("Size: %zu bytes\n", shmid_struct.shm_segsz);
printf("Creator UID: %d\n", shmid_struct.shm_perm.uid);
printf("Creator GID: %d\n", shmid_struct.shm_perm.gid);
printf("Permissions: %o\n", shmid_struct.shm_perm.mode & 0777);
printf("Current number of attached processes: %lu\n", (unsigned long)shmid_struct.shm_nattch);
// 注意:时间字段可能需要 #define _GNU_SOURCE 和正确的包含
// printf("Last attach time: %s", ctime(&shmid_struct.shm_atime));
// printf("Last detach time: %s", ctime(&shmid_struct.shm_dtime));
// printf("Last change time: %s", ctime(&shmid_struct.shm_ctime));
printf("Creator PID: %d\n", shmid_struct.shm_cpid);
printf("Last operator PID: %d\n", shmid_struct.shm_lpid);
printf("------------------------------------\n");

// 4. 简单使用共享内存 (连接、写入、分离)
char *data = (char *)shmat(shmid, NULL, 0);
if (data != (char *)-1) {
snprintf(data, 100, "Data written by process %d", getpid());
printf("Written to shared memory: %s\n", data);
shmdt(data);
} else {
perror("shmat for usage");
}

// 5. 再次检查状态 (连接数应该变为 1 然后又变回 0)
// 这里简化处理,实际连接和分离是瞬间的
if (shmctl(shmid, IPC_STAT, &shmid_struct) == -1) {
perror("shmctl IPC_STAT 2");
} else {
printf("Current number of attached processes (after usage): %lu\n", (unsigned long)shmid_struct.shm_nattch);
}

// 6. 销毁共享内存段
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Shared memory segment destroyed.\n");

return 0;
}

代码解释:

使用 ftok 生成键,并用 shmget 创建一个共享内存段。

定义一个 struct shmid_ds 类型的变量 shmid_struct。

调用 shmctl(shmid, IPC_STAT, &shmid_struct) 获取共享内存段的状态信息,并填充到 shmid_struct 中。

打印 shmid_struct 中的各种字段,如大小、权限、创建者 UID/GID、连接进程数等。

简单地连接、使用(写入数据)、分离共享内存段。

再次调用 shmctl IPC_STAT 查看状态变化(主要是 shm_nattch)。

最后调用 shmctl(shmid, IPC_RMID, NULL) 销毁共享内存段。

重要提示与注意事项:

清理: 使用 System V IPC 资源(共享内存、消息队列、信号量)后,务必调用相应的 ctl 函数(如 shmctl)并使用 IPC_RMID 命令进行销毁。否则,这些资源会一直存在于系统中,直到系统重启或手动使用 ipcrm 命令删除。

ftok 的可靠性: ftok 生成的键依赖于文件的 inode 和 mtime。如果文件被删除后重新创建,即使路径名相同,生成的键也可能不同。确保用作 ftok 参数的文件是稳定存在的。

错误处理: 始终检查这些函数的返回值,并进行适当的错误处理。

权限: 共享内存段的权限模型与文件系统类似,但检查是在 shmget, shmat 等调用时进行的。

与 mmap 的比较: System V 共享内存是内核管理的 IPC 对象,而通过 mmap 和 MAP_SHARED 实现的共享内存更像是一种内存映射文件的方式。POSIX 共享内存 (shm_open) 则结合了两者的优点,提供了命名的、基于文件描述符的共享内存机制。

总结:

shmget, shmat, shmdt, shmctl 这一组函数提供了 System V 共享内存 IPC 机制。虽然在现代编程中可能不如 POSIX 共享内存流行,但理解它们对于维护遗留代码和在特定系统环境中工作仍然至关重要。掌握它们的用法和生命周期管理是进行 Linux 进程间通信编程的基础之一。

accept系统调用及示例

我们继续学习 Linux 系统编程中的重要函数。这次我们介绍 accept 函数,它是 TCP 服务器用来接受客户端连接请求的核心系统调用。

1. 函数介绍

accept 是一个 Linux 系统调用,专门用于TCP 服务器(使用 SOCK_STREAM 套接字)。它的主要功能是从监听套接字(通过 listen 设置的套接字)的未决连接队列(pending connection queue)中取出第一个连接请求,并为这个新连接创建一个全新的、独立的套接字文件描述符。

你可以把 accept 想象成总机接线员:

有很多电话(客户端连接请求)打进来,响铃并排队在总机(监听套接字)那里。

接线员(accept 调用)拿起一个响铃的电话。

接线员把这条线路接到一个新的、专用的电话线(新的套接字文件描述符)上。

接线员可以继续去接下一个电话(下一次 accept 调用),而第一个通话(与第一个客户端的通信)则通过那条专用线路进行,互不干扰。

这个新创建的套接字文件描述符专门用于与那一个特定的客户端进行双向数据通信。原始的监听套接字则继续保持监听状态,等待并接受更多的连接请求。

2. 函数原型

1
2
3
4
5
6
7
8
#include <sys/socket.h> // 必需

// 标准形式
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// 带有标志的变体 (Linux 2.6.28+)
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

3. 功能

  • 从队列中取出连接: 从监听套接字 sockfd 维护的未决连接队列中提取第一个已完成或正在完成的连接请求。

  • 创建新套接字: 为这个新连接创建一个新的、非监听状态的套接字文件描述符。

  • 返回通信端点: 返回这个新的套接字文件描述符,服务器程序可以使用它来与特定的客户端进行数据交换(read/write)。

  • 获取客户端信息: 如果 addr 和 addrlen 参数不为 NULL,则将连接到服务器的客户端的地址信息(IP 地址和端口号)填充到 addr 指向的缓冲区中。

4. 参数

int sockfd: 这是监听套接字的文件描述符。它必须是:

  • 通过 socket() 成功创建的。

  • 通过 bind() 绑定了本地地址(IP 和端口)的。

  • 通过 listen() 进入监听状态的。

struct sockaddr *addr: 这是一个指向套接字地址结构的指针,用于接收客户端的地址信息。

  • 如果你不关心客户端是谁,可以传入 NULL。

  • 如果传入非 NULL 值,则它通常指向一个 struct sockaddr_in (IPv4) 或 struct sockaddr_in6 (IPv6) 类型的变量。

  • 该结构体在 accept 返回后会被填入客户端的地址信息。

socklen_t *addrlen: 这是一个指向 socklen_t 类型变量的指针。

  • 输入: 在调用 accept 时,这个变量必须被初始化为 addr 指向的缓冲区的大小(以字节为单位)。例如,如果 addr 指向 struct sockaddr_in,则 *addrlen 应初始化为 sizeof(struct sockaddr_in)。

  • 输出: accept 返回时,这个变量会被更新为实际存储在 addr 中的地址结构的大小。这对于处理不同大小的地址结构(如 IPv4 和 IPv6)很有用。

int flags (accept4 特有): 这个参数允许在创建新套接字时设置一些属性,类似于 socket() 的 type 参数可以使用的修饰符。

  • SOCK_NONBLOCK: 将新创建的套接字设置为非阻塞模式。

  • SOCK_CLOEXEC: 在调用 exec() 时自动关闭该套接字。

5. 返回值

  • 成功时: 返回一个新的、非负的整数,即为新连接创建的套接字文件描述符。服务器应使用这个返回的文件描述符与客户端进行后续的数据通信。

  • 失败时: 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 EAGAIN 或 EWOULDBLOCK 套接字被标记为非阻塞且没有未决连接,EBADF sockfd 无效,EINVAL 套接字未监听,EMFILE 进程打开的文件描述符已达上限等)。

阻塞与非阻塞:

  • 阻塞套接字(默认):如果监听队列中没有待处理的连接,accept 调用会阻塞(挂起)当前进程,直到有新的连接到达。

  • 非阻塞套接字(如果监听套接字被设置为非阻塞):如果监听队列中没有待处理的连接,accept 会立即返回 -1,并将 errno 设置为 EAGAIN 或 EWOULDBLOCK。

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

  • socket: 用于创建原始的监听套接字。

  • bind: 将监听套接字绑定到本地地址。

  • listen: 使套接字进入监听状态,开始接收连接请求。

  • connect: 客户端使用此函数向服务器发起连接。

  • close: 服务器在与客户端通信结束后,需要关闭 accept 返回的那个套接字文件描述符。通常也需要关闭原始的监听套接字(在服务器退出时)。

  • fork / 多线程: 服务器通常在 accept 之后调用 fork 或创建新线程来处理与客户端的通信,以便主服务器进程可以继续调用 accept 接受新的连接。

7. 示例代码

示例 1:基本的 TCP 服务器 accept 循环

这个例子演示了一个典型的、顺序处理的 TCP 服务器如何使用 accept 循环来接受和处理客户端连接。

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
// sequential_tcp_server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> // inet_ntoa (注意:不是线程安全的)

#define PORT 8080
#define BACKLOG 10

void handle_client(int client_fd, struct sockaddr_in *client_addr) {
char buffer&#91;1024];
ssize_t bytes_read;

printf("Handling client %s:%d (fd: %d)\n",
inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);

// 读取客户端发送的数据
while ((bytes_read = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer&#91;bytes_read] = '\0'; // 确保字符串结束
printf("Received from client: %s", buffer); // buffer 可能已包含 \n

// 将收到的数据回显给客户端
if (write(client_fd, buffer, bytes_read) != bytes_read) {
perror("write to client failed");
break;
}
}

if (bytes_read < 0) {
perror("read from client failed");
} else {
printf("Client %s:%d disconnected (fd: %d)\n",
inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);
}

close(client_fd); // 关闭与该客户端的连接
}

int main() {
int server_fd, client_fd;
struct sockaddr_in address, client_address;
socklen_t client_addr_len = sizeof(client_address);
int opt = 1;

// 1. 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// 2. 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}

// 3. 配置服务器地址
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 4. 绑定套接字
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}

// 5. 监听连接
if (listen(server_fd, BACKLOG) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}

printf("Server listening on port %d\n", PORT);

// 6. 主循环:接受并处理连接
while (1) {
printf("Waiting for a connection...\n");

// 7. 接受连接 (阻塞调用)
client_fd = accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len);
if (client_fd < 0) {
perror("accept failed");
continue; // 或 exit(EXIT_FAILURE);
}

printf("New connection accepted.\n");

// 8. 处理客户端 (顺序处理,同一时间只能处理一个)
handle_client(client_fd, &client_address);

// 处理完一个客户端后,循环继续 accept 下一个
}

// 注意:在实际程序中,需要有退出机制和清理代码
// close(server_fd); // 不会执行到这里
return 0;
}

代码解释:

创建、绑定、监听服务器套接字,这部分与之前 socket, bind, listen 的例子相同。

进入一个无限的 while(1) 循环。

在循环内部,调用 accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len)。

  • server_fd: 监听套接字。

  • &client_address: 指向 sockaddr_in 结构的指针,用于接收客户端地址。

  • &client_addr_len: 指向 socklen_t 变量的指针,该变量在调用前被初始化为 sizeof(client_address)。

accept 是一个阻塞调用。如果没有客户端连接,程序会在此处挂起等待。

当有客户端连接到达时,accept 返回一个新的文件描述符 client_fd。

调用 handle_client 函数处理与该客户端的通信。这个函数会读取客户端数据并回显回去。

handle_client 函数结束时(客户端断开或出错),会调用 close(client_fd) 关闭这个连接。

主循环继续,再次调用 accept 等待下一个客户端。

缺点: 这种顺序处理的方式效率很低。服务器在处理一个客户端时,无法接受其他客户端的连接,直到当前客户端处理完毕。

示例 2:并发 TCP 服务器 (使用 fork)

这个例子演示了如何使用 fork 创建子进程来并发处理多个客户端连接。

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
// concurrent_tcp_server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/wait.h> // waitpid

#define PORT 8080
#define BACKLOG 10

void handle_client(int client_fd, struct sockaddr_in *client_addr) {
char buffer&#91;1024];
ssize_t bytes_read;

printf("Child %d: Handling client %s:%d (fd: %d)\n",
getpid(), inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);

while ((bytes_read = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {
buffer&#91;bytes_read] = '\0';
printf("Child %d: Received from client: %s", getpid(), buffer);
if (write(client_fd, buffer, bytes_read) != bytes_read) {
perror("Child: write to client failed");
break;
}
}

if (bytes_read < 0) {
perror("Child: read from client failed");
} else {
printf("Child %d: Client %s:%d disconnected.\n",
getpid(), inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port));
}

close(client_fd);
printf("Child %d: Connection closed. Exiting.\n", getpid());
_exit(EXIT_SUCCESS); // 子进程使用 _exit 退出
}

int main() {
int server_fd, client_fd;
struct sockaddr_in address, client_address;
socklen_t client_addr_len = sizeof(client_address);
int opt = 1;
pid_t pid;

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}

if (listen(server_fd, BACKLOG) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}

printf("Concurrent Server (PID: %d) listening on port %d\n", getpid(), PORT);

while (1) {
client_fd = accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len);
if (client_fd < 0) {
perror("accept failed");
continue;
}

printf("Main process (PID: %d): New connection accepted.\n", getpid());

// Fork a new process to handle the client
pid = fork();
if (pid < 0) {
perror("fork failed");
close(client_fd); // Important: close the client fd on fork failure
} else if (pid == 0) {
// --- Child process ---
close(server_fd); // Child doesn't need the listening socket
handle_client(client_fd, &client_address);
// handle_client calls close(client_fd) and _exit()
// so nothing more needed here
} else {
// --- Parent process ---
close(client_fd); // Parent doesn't need the client-specific socket
printf("Main process (PID: %d): Forked child process (PID: %d) to handle client.\n", getpid(), pid);

// Optional: Clean up any finished child processes (non-blocking)
// This prevents zombie processes if children finish quickly
pid_t wpid;
int status;
while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("Main process (PID: %d): Reaped child process (PID: %d)\n", getpid(), wpid);
}
}
}

close(server_fd);
return 0;
}

代码解释:

服务器设置部分与顺序服务器相同。

在 accept 成功返回后,立即调用 fork()。

fork 返回后:
在子进程 (pid == 0):

  • 关闭不需要的监听套接字 server_fd。

  • 调用 handle_client(client_fd, …) 处理客户端。

  • handle_client 处理完毕后会关闭 client_fd 并调用 _exit() 退出。

在父进程 (pid > 0):

  • 关闭不需要的客户端套接字 client_fd(因为子进程在处理它)。

  • 打印信息,表明已派生子进程处理客户端。

  • 可选地调用 waitpid(-1, &status, WNOHANG) 来非阻塞地清理已经结束的子进程(回收僵尸进程)。如果省略这一步,结束的子进程会变成僵尸进程,直到父进程退出。

父进程继续循环,调用 accept 等待下一个客户端连接。

示例 3:使用 accept4 设置非阻塞客户端套接字

这个例子演示了如何使用 accept4 函数在创建新连接套接字的同时就将其设置为非阻塞模式。

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
// accept4_example.c
#define _GNU_SOURCE // 必须定义以使用 accept4
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h> // F_GETFL, F_SETFL, O_NONBLOCK

#define PORT 8080
#define BACKLOG 10

int main() {
int server_fd, client_fd;
struct sockaddr_in address, client_address;
socklen_t client_addr_len = sizeof(client_address);
int opt = 1;
int flags;

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
close(server_fd);
exit(EXIT_FAILURE);
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}

if (listen(server_fd, BACKLOG) < 0) {
perror("listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}

printf("Server listening on port %d. Accepting connections...\n", PORT);

while (1) {
printf("Waiting for a connection...\n");

// 使用 accept4 直接创建非阻塞的客户端套接字
client_fd = accept4(server_fd, (struct sockaddr *)&client_address, &client_addr_len, SOCK_NONBLOCK);

if (client_fd < 0) {
perror("accept4 failed");
continue;
}

printf("New connection accepted (fd: %d). Checking if it's non-blocking...\n", client_fd);

// 验证套接字是否确实是非阻塞的
flags = fcntl(client_fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl F_GETFL failed");
close(client_fd);
continue;
}

if (flags & O_NONBLOCK) {
printf("Confirmed: Client socket (fd: %d) is non-blocking.\n", client_fd);
} else {
printf("Warning: Client socket (fd: %d) is NOT non-blocking.\n", client_fd);
}

// --- 在这里,你可以对非阻塞的 client_fd 进行 read/write/select/poll 操作 ---
// 例如,将其添加到 epoll 或 select 的监视集合中

// 为了演示,我们简单地关闭它
printf("Closing client socket (fd: %d).\n", client_fd);
close(client_fd);
}

close(server_fd);
return 0;
}

代码解释:

服务器设置部分与之前相同。

在调用 accept4 时,传入了 SOCK_NONBLOCK 标志作为第四个参数。

如果 accept4 成功,返回的 client_fd 就已经被设置为非阻塞模式。

代码通过 fcntl(client_fd, F_GETFL, 0) 获取套接字标志,并检查 O_NONBLOCK 位是否被设置,以验证 accept4 的效果。

在实际应用中,得到非阻塞的 client_fd 后,通常会将其加入到 select、poll 或 epoll 的监视集合中,以便高效地管理多个并发连接。

重要提示与注意事项:

返回新的文件描述符: accept 返回的文件描述符与原始监听套接字 sockfd 完全不同。原始套接字继续用于监听,新套接字用于与特定客户端通信。

必须关闭: 服务器在与客户端通信结束后,必须调用 close() 关闭 accept 返回的那个文件描述符,以释放资源。

获取客户端地址: 利用 addr 和 addrlen 参数获取客户端的 IP 和端口对于日志记录、访问控制、调试等非常有用。

并发处理: 对于需要同时处理多个客户端的服务器,必须使用 fork、多线程或 I/O 多路复用(select/poll/epoll)等技术。简单的顺序处理无法满足实际需求。

错误处理: 始终检查 accept 的返回值。在繁忙的服务器上,非阻塞 accept 可能会因为没有连接而返回 EAGAIN。

accept4 的优势: accept4 可以在原子操作中设置新套接字的属性,避免了先 accept 再 fcntl 的两步操作,理论上更高效且没有竞态条件。

总结:

accept 是 TCP 服务器模型的核心。它使得服务器能够从监听状态进入与客户端的实际数据交换状态。理解其阻塞/非阻塞行为、返回值含义以及如何与并发处理技术(如 fork)结合使用,是构建健壮网络服务器的基础。accept4 则为需要精细控制新连接套接字属性的场景提供了便利。

access系统调用及示例

我们继续学习 Linux 系统编程中的重要函数。这次我们介绍 access 函数,它用于检查调用进程是否对指定的文件路径具有特定的访问权限(如读、写、执行)或检查文件是否存在。

1. 函数介绍

 access 函数是一个 Linux 系统调用,用于根据调用进程的实际用户 ID (UID) 和组 ID (GID) 来检查对文件的权限。它回答了这样的问题:“我(当前运行这个程序的用户)能否读/写/执行这个文件?” 或者更简单地,“这个文件存在吗?”。

这在程序需要在尝试打开或执行文件之前,先确认是否具备相应权限时非常有用,可以避免因权限不足而导致后续操作(如 open, execve)失败。

需要注意的是,access 检查的是调用 access 时的实际权限,即使程序后续通过 setuid 或 setgid 改变了有效用户 ID 或组 ID,access 仍然基于最初的 UID/GID 进行检查。

2. 函数原型

1
2
3
4
#include <unistd.h> // 必需

int access(const char *pathname, int mode);

3. 功能

  • 权限检查: 检查调用进程对由 pathname 指定的文件是否拥有 mode 参数指定的访问权限。

  • 存在性检查: 特别地,当 mode 设置为 F_OK 时,access 仅检查文件是否存在,而不关心具体的读/写/执行权限。

4. 参数

  • const char *pathname: 指向一个以空字符 (\0) 结尾的字符串,该字符串包含了要检查权限的文件或目录的路径名。这可以是相对路径或绝对路径。

int mode: 指定要检查的权限类型。这是一个位掩码,可以是以下值的按位或组合:

  • F_OK: 检查文件是否存在。

  • R_OK: 检查文件是否可读。

  • W_OK: 检查文件是否可写。

  • X_OK: 检查文件是否可执行。例如:

  • F_OK: 仅检查文件是否存在。

  • R_OK: 检查文件是否可读。

  • R_OK | W_OK: 检查文件是否可读且可写。

  • X_OK: 检查文件(或目录)是否可执行(对于目录,可执行意味着可以进入该目录)。

5. 返回值

  • 成功时 (具备指定权限或文件存在): 返回 0。

失败时 (不具备指定权限或文件不存在):

返回 -1,并设置全局变量 errno 来指示具体的错误原因:

  • EACCES: 请求的权限被拒绝。文件存在,但调用进程没有指定的权限。

  • ENOENT: 文件不存在(或路径名指向的目录不存在)。

  • ELOOP: 解析 pathname 时遇到符号链接环。

  • 其他错误…

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

  • stat, lstat, fstat: 这些函数可以获取文件的详细状态信息,包括权限位 (st_mode)。程序可以手动检查这些权限位来判断权限,但这需要自己实现权限检查逻辑(考虑用户、组、其他用户的权限位以及 UID/GID)。access 提供了更直接、符合系统安全策略的检查方式。

  • open, execve 等: 这些函数在执行时也会进行权限检查。使用 access 可以提前检查,但需要注意“检查与使用之间存在竞争条件 (TOCTOU)”的问题(见下方注意事项)。

  • euidaccess / eaccess: 这些是 GNU 扩展函数,它们根据有效用户 ID (EUID) 和有效组 ID (EGID) 进行检查,而不是实际用户 ID。在 setuid/setgid 程序中可能更有意义。

7. 示例代码

示例 1:基本的文件存在性和权限检查

这个例子演示了如何使用 access 检查文件是否存在、是否可读、是否可写、是否可执行。

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
#include <unistd.h>  // access
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit

void check_access(const char *pathname) {
printf("\n--- Checking access for '%s' ---\n", pathname);

// 1. 检查文件是否存在
if (access(pathname, F_OK) == 0) {
printf(" File exists.\n");
} else {
if (errno == ENOENT) {
printf(" File does NOT exist.\n");
} else {
perror(" access F_OK failed for other reason");
}
// 如果文件不存在,后续检查无意义,但为了演示,我们仍进行
// (实际上,通常会在这里 return)
}

// 2. 检查是否可读
if (access(pathname, R_OK) == 0) {
printf(" File is readable.\n");
} else {
if (errno == EACCES) {
printf(" File exists but is NOT readable.\n");
} else if (errno == ENOENT) {
printf(" File does not exist (so not readable).\n");
} else {
perror(" access R_OK failed for other reason");
}
}

// 3. 检查是否可写
if (access(pathname, W_OK) == 0) {
printf(" File is writable.\n");
} else {
if (errno == EACCES) {
printf(" File exists but is NOT writable.\n");
} else if (errno == ENOENT) {
printf(" File does not exist (so not writable).\n");
} else {
perror(" access W_OK failed for other reason");
}
}

// 4. 检查是否可执行
if (access(pathname, X_OK) == 0) {
printf(" File is executable.\n");
} else {
if (errno == EACCES) {
printf(" File exists but is NOT executable.\n");
} else if (errno == ENOENT) {
printf(" File does not exist (so not executable).\n");
} else {
perror(" access X_OK failed for other reason");
}
}
}

int main(int argc, char *argv&#91;]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <file1> &#91;file2] ...\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

// 对每个命令行参数进行检查
for (int i = 1; i < argc; i++) {
check_access(argv&#91;i]);
}

return 0;
}

代码解释:

定义了一个 check_access 函数,它接受一个文件路径作为参数。

在 check_access 函数内部:

  • 首先调用 access(pathname, F_OK) 检查文件是否存在。

  • 然后分别调用 access(pathname, R_OK), access(pathname, W_OK), access(pathname, X_OK) 检查读、写、执行权限。

  • 每次调用后都检查返回值。如果返回 0,表示检查通过;如果返回 -1,则检查 errno 来区分是“文件不存在”还是“权限不足”等其他原因。

main 函数遍历所有命令行参数,并对每个参数调用 check_access。

编译和运行:

1
2
3
4
5
6
7
8
9
gcc -o check_access check_access.c
touch test_file
chmod 644 test_file # rw-r--r--
chmod 755 test_script.sh # 创建一个可执行脚本用于测试
echo '#!/bin/bash\necho "Hello from script"' > test_script.sh
chmod +x test_script.sh

./check_access test_file test_script.sh /etc/passwd /nonexistent_file

示例 2:在打开文件前进行检查

这个例子展示了如何在尝试打开文件进行写入之前,先使用 access 检查文件是否存在以及是否可写,以提供更友好的错误信息。

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
#include <unistd.h>  // access
#include <fcntl.h> // open, O_WRONLY, O_CREAT, O_EXCL
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit

int safe_write_file(const char *pathname, const char *data) {
int fd;

// 1. 检查文件是否存在
if (access(pathname, F_OK) == 0) {
printf("File '%s' already exists.\n", pathname);

// 2. 如果存在,检查是否可写
if (access(pathname, W_OK) != 0) {
if (errno == EACCES) {
fprintf(stderr, "Error: Permission denied. Cannot write to '%s'.\n", pathname);
} else {
perror("Error checking write permission");
}
return -1; // Failure
}
printf("File exists and is writable.\n");
// 注意:即使可写,open 时仍可能因为其他原因失败(如磁盘满)

} else {
// 文件不存在,检查目录是否可写 (间接判断能否创建文件)
// 这里简化处理,实际可能需要解析路径
printf("File '%s' does not exist. Checking if we can create it...\n", pathname);
// 一个简单的检查:检查当前目录是否可写
if (access(".", W_OK) != 0) {
if (errno == EACCES) {
fprintf(stderr, "Error: Permission denied. Cannot create file in current directory.\n");
} else {
perror("Error checking current directory write permission");
}
return -1;
}
printf("Current directory is writable. Proceeding to create file.\n");
}

// 3. 尝试打开文件进行写入
// 使用 O_CREAT | O_EXCL 确保仅在文件不存在时创建,防止覆盖
// 如果前面检查了存在性,这里可能用 O_WRONLY | O_TRUNC 更合适
// 这里演示结合检查的逻辑
if (access(pathname, F_OK) == 0) {
// 文件存在,以只写和截断模式打开
fd = open(pathname, O_WRONLY | O_TRUNC);
} else {
// 文件不存在,创建它
fd = open(pathname, O_WRONLY | O_CREAT | O_EXCL, 0644);
}

if (fd == -1) {
perror("open");
return -1; // Failure
}

printf("File '%s' opened successfully for writing.\n", pathname);

// 4. 写入数据 (简化)
ssize_t data_len = 0;
const char *p = data;
while (*p++) data_len++;

if (write(fd, data, data_len) != data_len) {
perror("write");
close(fd);
return -1;
}

printf("Successfully wrote data to '%s'.\n", pathname);

// 5. 关闭文件
if (close(fd) == -1) {
perror("close");
return -1;
}

return 0; // Success
}

int main() {
const char *filename = "output_from_safe_write.txt";
const char *content = "This is data written by the safe_write_file function.\n";

if (safe_write_file(filename, content) == 0) {
printf("Operation completed successfully.\n");
} else {
printf("Operation failed.\n");
exit(EXIT_FAILURE);
}

return 0;
}

代码解释:

定义了一个 safe_write_file 函数,它接受文件名和要写入的数据。

首先使用 access(pathname, F_OK) 检查文件是否存在。

如果文件存在,再使用 access(pathname, W_OK) 检查是否可写。

如果文件不存在,则检查当前工作目录(.)是否可写,以此判断是否有权限创建新文件(这是一个简化的检查)。

根据检查结果,决定是以 O_WRONLY | O_TRUNC(覆盖)还是 O_WRONLY | O_CREAT | O_EXCL(新建)模式打开文件。

打开文件后,执行写入操作。

最后关闭文件。

通过这种方式,可以在实际执行可能导致失败的操作(open, write)之前,提供更具体、更早的错误反馈。

重要注意事项:TOCTOU 竞争条件

使用 access 时需要特别注意一个潜在的安全问题:TOCTOU (Time-of-Check to Time-of-Use) 竞争条件。

  • 问题: access 检查权限和后续使用文件(如 open, execve)之间存在时间差。在这段时间内,文件的权限或存在性可能被其他进程改变。

  • 例子: 一个程序用 access(“myfile”, W_OK) 检查 myfile 是否可写,返回 0(表示可写)。但在程序调用 open(“myfile”, O_WRONLY) 之前,另一个有权限的进程删除了 myfile 并创建了一个指向敏感文件(如 /etc/passwd)的符号链接,并命名为 myfile。此时,程序的 open 调用将会打开并可能修改 /etc/passwd,这显然不是预期行为。

缓解方法:

  • 尽量避免使用 access: 最好的方法是直接尝试执行操作(如 open, execve),并根据其返回的错误码来处理权限或存在性问题。内核会在 open/execve 时进行原子性的权限检查。

  • 如果必须使用 access: 要意识到这种风险,并确保在权限检查和文件使用之间的时间窗口尽可能短。在高安全性要求的程序中,应避免依赖 access 的结果来做关键决策。

总结:

access 函数提供了一种方便的方式来检查文件权限和存在性。虽然它有其用途,但在涉及安全性的场景中,直接尝试操作并处理错误通常是更安全、更可靠的做法。理解其工作原理和潜在的 TOCTOU 问题是正确使用它的关键。

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

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

io_cancel系统调用及示例

io_cancel 函数详解

  1. 函数介绍

io_cancel 是Linux传统异步I/O (AIO) 系统调用,用于取消先前提交但尚未完成的异步I/O操作。它是AIO框架的重要组成部分,允许应用程序在必要时取消正在进行的异步操作,提供更灵活的I/O控制能力。

  1. 函数原型
1
2
3
#include <linux/aio_abi.h>
int io_cancel(aio_context_t ctx_id, struct iocb *iocb, struct io_event *result);

  1. 功能

io_cancel 尝试取消指定的异步I/O操作。如果操作仍在排队或尚未开始执行,则可以成功取消;如果操作已经开始执行或已经完成,则取消可能失败。成功取消的操作会返回一个带有ECANCELED错误码的完成事件。

  1. 参数
  • aio_context_t ctx_id: AIO上下文ID(由io_setup创建)

  • *struct iocb iocb: 要取消的异步I/O控制块指针

  • *struct io_event result: 用于存储取消结果的事件结构指针

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

  • 失败: 返回负的错误码

  1. 相似函数,或关联函数
  • io_setup: 初始化AIO上下文

  • io_destroy: 销毁AIO上下文

  • io_submit: 提交异步I/O操作

  • io_getevents: 获取完成的异步I/O事件

  • io_pgetevents: 带信号处理的事件获取

  1. 示例代码

示例1:基础io_cancel使用

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
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 演示基础io_cancel使用方法
*/
int demo_io_cancel_basic() {
aio_context_t ctx;
struct iocb iocb;
struct io_event result;
int fd;
int ret;

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

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(128, &ctx);
if (ret < 0) {
printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");
printf(" 上下文ID: %llu\n", (unsigned long long)ctx);

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *filename = "aio_cancel_test.txt";
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror(" 创建测试文件失败");
io_destroy(ctx);
return -1;
}
printf(" ✓ 测试文件创建成功: %s\n", filename);

// 准备异步写入操作
printf("\n3. 准备异步写入操作:\n");
char *test_data = "This is test data for AIO cancel operation.\n";
size_t data_size = strlen(test_data);

// 初始化iocb结构
memset(&iocb, 0, sizeof(iocb));
iocb.aio_data = 12345; // 用户数据
iocb.aio_key = 0;
iocb.aio_rw_flags = 0;
iocb.aio_lio_opcode = IOCB_CMD_PWRITE; // 异步写入
iocb.aio_reqprio = 0;
iocb.aio_fildes = fd;
iocb.aio_buf = (uint64_t)(uintptr_t)test_data;
iocb.aio_nbytes = data_size;
iocb.aio_offset = 0;
iocb.aio_flags = 0;
iocb.aio_resfd = 0;

printf(" 准备异步写入操作:\n");
printf(" 文件描述符: %d\n", fd);
printf(" 数据大小: %zu 字节\n", data_size);
printf(" 用户数据: %llu\n", (unsigned long long)iocb.aio_data);

// 提交异步操作
printf("\n4. 提交异步操作:\n");
struct iocb *iocbs&#91;1] = {&iocb};
ret = io_submit(ctx, 1, iocbs);
if (ret != 1) {
printf(" 提交异步操作失败: %s\n", strerror(-ret));
close(fd);
unlink(filename);
io_destroy(ctx);
return -1;
}
printf(" ✓ 异步操作提交成功\n");

// 立即尝试取消操作
printf("\n5. 立即尝试取消操作:\n");
ret = io_cancel(ctx, &iocb, &result);
if (ret == 0) {
printf(" ✓ 操作取消成功\n");
printf(" 取消结果:\n");
printf(" 用户数据: %llu\n", (unsigned long long)result.data);
printf(" 结果: %ld\n", result.res);
printf(" 结果2: %ld\n", result.res2);
if (result.res == -ECANCELED) {
printf(" 状态: 操作已取消 (ECANCELED)\n");
}
} else if (ret == -EAGAIN) {
printf(" ℹ 操作无法取消 (可能已开始执行或已完成)\n");

// 等待操作完成
printf(" 等待操作完成...\n");
struct io_event events&#91;1];
ret = io_getevents(ctx, 1, 1, events, NULL);
if (ret > 0) {
printf(" ✓ 操作完成\n");
printf(" 用户数据: %llu\n", (unsigned long long)events&#91;0].data);
printf(" 结果: %ld 字节\n", events&#91;0].res);
}
} else {
printf(" ✗ 取消操作失败: %s\n", strerror(-ret));
}

// 演示延时取消
printf("\n6. 演示延时取消:\n");

// 创建一个更大的写入操作
char *large_data = malloc(1024 * 1024); // 1MB数据
if (large_data) {
// 填充数据
for (int i = 0; i < 1024 * 1024; i++) {
large_data&#91;i] = 'A' + (i % 26);
}

// 准备新的iocb
struct iocb large_iocb;
memset(&large_iocb, 0, sizeof(large_iocb));
large_iocb.aio_data = 54321;
large_iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
large_iocb.aio_fildes = fd;
large_iocb.aio_buf = (uint64_t)(uintptr_t)large_data;
large_iocb.aio_nbytes = 1024 * 1024;
large_iocb.aio_offset = data_size; // 在之前数据之后

printf(" 准备大块数据写入操作 (1MB)\n");

struct iocb *large_iocbs&#91;1] = {&large_iocb};
ret = io_submit(ctx, 1, large_iocbs);
if (ret == 1) {
printf(" ✓ 大块数据写入操作提交成功\n");

// 短暂延迟后尝试取消
printf(" 等待100ms后尝试取消...\n");
usleep(100000); // 100ms

struct io_event cancel_result;
ret = io_cancel(ctx, &large_iocb, &cancel_result);
if (ret == 0) {
printf(" ✓ 大块数据写入操作取消成功\n");
} else if (ret == -EAGAIN) {
printf(" ℹ 大块数据写入操作无法取消\n");

// 等待操作完成
struct io_event events&#91;1];
int wait_ret = io_getevents(ctx, 1, 1, events, NULL);
if (wait_ret > 0) {
printf(" ✓ 大块数据写入完成: %ld 字节\n", events&#91;0].res);
}
} else {
printf(" ✗ 取消大块数据写入失败: %s\n", strerror(-ret));
}
}

free(large_data);
}

// 清理资源
printf("\n7. 清理资源:\n");
close(fd);
unlink(filename);
io_destroy(ctx);
printf(" ✓ 资源清理完成\n");

return 0;
}

int main() {
return demo_io_cancel_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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 批量操作取消演示
*/
int demo_batch_cancel() {
aio_context_t ctx;
const int batch_size = 8;
struct iocb iocbs&#91;batch_size];
struct io_event results&#91;batch_size];
int fd;
int ret;
char *test_files&#91;batch_size];

printf("=== 批量操作取消演示 ===\n");

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(128, &ctx);
if (ret < 0) {
printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
for (int i = 0; i < batch_size; i++) {
char filename&#91;32];
snprintf(filename, sizeof(filename), "batch_cancel_%d.txt", i);
test_files&#91;i] = strdup(filename);

fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
// 写入一些初始数据使文件不为空
char initial_data&#91;256];
memset(initial_data, 'A' + i, sizeof(initial_data) - 1);
initial_data&#91;sizeof(initial_data) - 1] = '\0';
write(fd, initial_data, sizeof(initial_data) - 1);
close(fd);
printf(" 创建测试文件 %d: %s\n", i, filename);
}
}

// 准备批量异步读取操作
printf("\n3. 准备批量异步读取操作:\n");
struct iocb *iocb_ptrs&#91;batch_size];

for (int i = 0; i < batch_size; i++) {
fd = open(test_files&#91;i], O_RDONLY);
if (fd == -1) {
printf(" 打开文件 %s 失败\n", test_files&#91;i]);
continue;
}

// 初始化iocb
memset(&iocbs&#91;i], 0, sizeof(iocbs&#91;i]));
iocbs&#91;i].aio_data = i + 1; // 使用索引作为用户数据
iocbs&#91;i].aio_lio_opcode = IOCB_CMD_PREAD;
iocbs&#91;i].aio_fildes = fd;

// 分配读取缓冲区
char *read_buffer = malloc(1024);
if (read_buffer) {
iocbs&#91;i].aio_buf = (uint64_t)(uintptr_t)read_buffer;
iocbs&#91;i].aio_nbytes = 1024;
iocbs&#91;i].aio_offset = 0;
}

iocb_ptrs&#91;i] = &iocbs&#91;i];
printf(" 准备读取操作 %d: 文件 %s\n", i + 1, test_files&#91;i]);
}

// 提交批量操作
printf("\n4. 提交批量异步操作:\n");
ret = io_submit(ctx, batch_size, iocb_ptrs);
if (ret < 0) {
printf(" 提交批量操作失败: %s\n", strerror(-ret));
goto cleanup;
}
printf(" ✓ 成功提交 %d 个异步操作\n", ret);

// 演示部分取消
printf("\n5. 演示部分操作取消:\n");
int cancel_count = 0;

for (int i = 0; i < batch_size; i += 2) { // 取消偶数索引的操作
ret = io_cancel(ctx, &iocbs&#91;i], &results&#91;cancel_count]);
if (ret == 0) {
printf(" ✓ 操作 %d 取消成功\n", (int)iocbs&#91;i].aio_data);
cancel_count++;
} else if (ret == -EAGAIN) {
printf(" ℹ 操作 %d 无法取消 (可能已开始执行)\n", (int)iocbs&#91;i].aio_data);
} else {
printf(" ✗ 操作 %d 取消失败: %s\n", (int)iocbs&#91;i].aio_data, strerror(-ret));
}
}

printf(" 总共尝试取消 %d 个操作\n", cancel_count);

// 等待剩余操作完成
printf("\n6. 等待剩余操作完成:\n");
struct io_event completed_events&#91;batch_size];
int completed_count = 0;

// 等待最多5秒
struct timespec timeout = {5, 0};
ret = io_getevents(ctx, 1, batch_size, completed_events, &timeout);
if (ret > 0) {
printf(" ✓ 收到 %d 个完成事件\n", ret);
completed_count = ret;

for (int i = 0; i < ret; i++) {
printf(" 操作 %llu 完成: %ld 字节\n",
(unsigned long long)completed_events&#91;i].data,
completed_events&#91;i].res);
}
} else if (ret == 0) {
printf(" ⚠ 超时,没有收到完成事件\n");
} else {
printf(" ✗ 等待完成事件失败: %s\n", strerror(-ret));
}

// 显示取消结果
printf("\n7. 取消结果统计:\n");
printf(" 尝试取消的操作数: %d\n", cancel_count);
printf(" 完成的操作数: %d\n", completed_count);
printf(" 总操作数: %d\n", batch_size);

// 清理资源
cleanup:
printf("\n8. 清理资源:\n");

// 关闭文件描述符和释放缓冲区
for (int i = 0; i < batch_size; i++) {
if (iocbs&#91;i].aio_fildes > 0) {
close(iocbs&#91;i].aio_fildes);
}
if (iocbs&#91;i].aio_buf) {
free((void*)(uintptr_t)iocbs&#91;i].aio_buf);
}
if (test_files&#91;i]) {
unlink(test_files&#91;i]);
free(test_files&#91;i]);
}
}

io_destroy(ctx);
printf(" ✓ 所有资源清理完成\n");

return 0;
}

int main() {
return demo_batch_cancel();
}

示例3:超时取消机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 超时取消上下文
*/
typedef struct {
aio_context_t ctx;
struct iocb *iocb;
int is_completed;
int is_cancelled;
time_t start_time;
int timeout_seconds;
} timeout_cancel_ctx_t;

/**
* 超时处理函数
*/
void timeout_handler(int sig) {
printf("收到超时信号\n");
}

/**
* 演示超时取消机制
*/
int demo_timeout_cancel_mechanism() {
aio_context_t ctx;
struct iocb iocb;
struct io_event result;
int fd;
int ret;

printf("=== 超时取消机制演示 ===\n");

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(64, &ctx);
if (ret < 0) {
printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *filename = "timeout_test.txt";
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror(" 创建测试文件失败");
io_destroy(ctx);
return -1;
}

// 写入大量数据
char *large_data = malloc(10 * 1024 * 1024); // 10MB数据
if (large_data) {
for (int i = 0; i < 10 * 1024 * 1024; i++) {
large_data&#91;i] = 'A' + (i % 26);
}
write(fd, large_data, 10 * 1024 * 1024);
}
close(fd);

printf(" ✓ 创建了10MB测试文件: %s\n", filename);

// 重新打开文件进行读取
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror(" 打开测试文件失败");
io_destroy(ctx);
unlink(filename);
return -1;
}

// 准备长时间读取操作
printf("\n3. 准备长时间读取操作:\n");
char *read_buffer = malloc(5 * 1024 * 1024); // 5MB缓冲区
if (!read_buffer) {
perror(" 分配读取缓冲区失败");
close(fd);
io_destroy(ctx);
unlink(filename);
return -1;
}

// 初始化iocb
memset(&iocb, 0, sizeof(iocb));
iocb.aio_data = 99999;
iocb.aio_lio_opcode = IOCB_CMD_PREAD;
iocb.aio_fildes = fd;
iocb.aio_buf = (uint64_t)(uintptr_t)read_buffer;
iocb.aio_nbytes = 5 * 1024 * 1024; // 读取5MB
iocb.aio_offset = 0;

printf(" 准备读取5MB数据\n");

// 提交异步读取操作
struct iocb *iocbs&#91;1] = {&iocb};
ret = io_submit(ctx, 1, iocbs);
if (ret != 1) {
printf(" 提交异步读取操作失败: %s\n", strerror(-ret));
free(read_buffer);
close(fd);
io_destroy(ctx);
unlink(filename);
return -1;
}
printf(" ✓ 异步读取操作提交成功\n");

// 设置超时处理
printf("\n4. 设置超时处理:\n");
time_t start_time = time(NULL);
const int timeout_seconds = 3;

printf(" 设置超时时间: %d 秒\n", timeout_seconds);

// 等待操作完成或超时
printf("\n5. 等待操作完成或超时:\n");
struct io_event events&#91;1];
struct timespec timeout = {1, 0}; // 1秒超时

int operation_completed = 0;
int cancel_attempted = 0;

while (difftime(time(NULL), start_time) < timeout_seconds) {
ret = io_getevents(ctx, 0, 1, events, &timeout);
if (ret > 0) {
printf(" ✓ 操作在超时前完成\n");
printf(" 读取字节数: %ld\n", events&#91;0].res);
operation_completed = 1;
break;
} else if (ret == 0) {
printf(" 操作仍在进行中...\n");

// 每秒检查一次是否需要取消
if (!cancel_attempted && difftime(time(NULL), start_time) >= 2) {
printf(" 2秒后尝试取消操作\n");
struct io_event cancel_result;
ret = io_cancel(ctx, &iocb, &cancel_result);
if (ret == 0) {
printf(" ✓ 操作取消成功\n");
cancel_attempted = 1;
break;
} else if (ret == -EAGAIN) {
printf(" ℹ 操作无法取消\n");
cancel_attempted = 1;
} else {
printf(" ✗ 取消操作失败: %s\n", strerror(-ret));
cancel_attempted = 1;
}
}
} else {
printf(" ✗ 等待事件失败: %s\n", strerror(-ret));
break;
}
}

// 超时后的处理
if (!operation_completed && !cancel_attempted) {
printf("\n6. 超时处理:\n");
printf(" 操作超时 (%d 秒)\n", timeout_seconds);

// 强制取消操作
printf(" 强制取消操作...\n");
struct io_event cancel_result;
ret = io_cancel(ctx, &iocb, &cancel_result);
if (ret == 0) {
printf(" ✓ 操作取消成功\n");
} else if (ret == -EAGAIN) {
printf(" ℹ 操作无法取消\n");
} else {
printf(" ✗ 取消操作失败: %s\n", strerror(-ret));
}
}

// 等待最终结果
printf("\n7. 等待最终结果:\n");
timeout.tv_sec = 2; // 2秒超时
timeout.tv_nsec = 0;

ret = io_getevents(ctx, 0, 1, events, &timeout);
if (ret > 0) {
printf(" 最终结果:\n");
printf(" 用户数据: %llu\n", (unsigned long long)events&#91;0].data);
printf(" 结果: %ld\n", events&#91;0].res);
if (events&#91;0].res == -ECANCELED) {
printf(" 状态: 操作已取消\n");
} else if (events&#91;0].res < 0) {
printf(" 错误: %s\n", strerror(-events&#91;0].res));
} else {
printf(" 成功读取: %ld 字节\n", events&#91;0].res);
}
} else if (ret == 0) {
printf(" 超时,无结果返回\n");
} else {
printf(" 等待结果失败: %s\n", strerror(-ret));
}

// 清理资源
printf("\n8. 清理资源:\n");
free(read_buffer);
close(fd);
unlink(filename);
io_destroy(ctx);
printf(" ✓ 资源清理完成\n");

return 0;
}

int main() {
return demo_timeout_cancel_mechanism();
}

示例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
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 取消策略枚举
*/
typedef enum {
CANCEL_STRATEGY_IMMEDIATE, // 立即取消
CANCEL_STRATEGY_TIMEOUT, // 超时取消
CANCEL_STRATEGY_CONDITIONAL, // 条件取消
CANCEL_STRATEGY_NEVER // 从不取消
} cancel_strategy_t;

/**
* 操作上下文
*/
typedef struct {
struct iocb iocb;
int is_submitted;
int is_completed;
int is_cancelled;
time_t submit_time;
cancel_strategy_t strategy;
int timeout_seconds;
int condition_value;
} operation_context_t;

/**
* 条件取消检查函数
*/
int check_cancel_condition(operation_context_t *ctx) {
switch (ctx->strategy) {
case CANCEL_STRATEGY_IMMEDIATE:
return 1; // 立即取消

case CANCEL_STRATEGY_TIMEOUT:
if (difftime(time(NULL), ctx->submit_time) > ctx->timeout_seconds) {
printf(" 操作超时,触发取消\n");
return 1;
}
return 0;

case CANCEL_STRATEGY_CONDITIONAL:
// 模拟条件检查
if (ctx->condition_value > 100) {
printf(" 条件满足,触发取消\n");
return 1;
}
return 0;

case CANCEL_STRATEGY_NEVER:
default:
return 0;
}
}

/**
* 演示条件取消策略
*/
int demo_conditional_cancel_strategy() {
aio_context_t ctx;
operation_context_t operations&#91;5];
int fd;
int ret;

printf("=== 条件取消策略演示 ===\n");

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(64, &ctx);
if (ret < 0) {
printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *filename = "conditional_cancel_test.txt";
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror(" 创建测试文件失败");
io_destroy(ctx);
return -1;
}

// 写入测试数据
char *test_data = malloc(1024 * 1024); // 1MB数据
if (test_data) {
for (int i = 0; i < 1024 * 1024; i++) {
test_data&#91;i] = 'A' + (i % 26);
}
write(fd, test_data, 1024 * 1024);
}
close(fd);
free(test_data);

printf(" ✓ 创建了1MB测试文件: %s\n", filename);

// 重新打开文件进行读取
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror(" 打开测试文件失败");
io_destroy(ctx);
unlink(filename);
return -1;
}

// 初始化操作上下文
printf("\n3. 初始化操作上下文:\n");
for (int i = 0; i < 5; i++) {
operation_context_t *op_ctx = &operations&#91;i];
memset(op_ctx, 0, sizeof(*op_ctx));

// 设置不同的取消策略
switch (i % 4) {
case 0:
op_ctx->strategy = CANCEL_STRATEGY_IMMEDIATE;
printf(" 操作 %d: 立即取消策略\n", i + 1);
break;
case 1:
op_ctx->strategy = CANCEL_STRATEGY_TIMEOUT;
op_ctx->timeout_seconds = 2; // 2秒超时
printf(" 操作 %d: 超时取消策略 (2秒)\n", i + 1);
break;
case 2:
op_ctx->strategy = CANCEL_STRATEGY_CONDITIONAL;
op_ctx->condition_value = i * 50; // 不同的条件值
printf(" 操作 %d: 条件取消策略 (条件值: %d)\n", i + 1, op_ctx->condition_value);
break;
case 3:
op_ctx->strategy = CANCEL_STRATEGY_NEVER;
printf(" 操作 %d: 从不取消策略\n", i + 1);
break;
}
}

// 准备异步读取操作
printf("\n4. 准备异步读取操作:\n");
struct iocb *iocb_ptrs&#91;5];

for (int i = 0; i < 5; i++) {
operation_context_t *op_ctx = &operations&#91;i];

// 初始化iocb
memset(&op_ctx->iocb, 0, sizeof(op_ctx->iocb));
op_ctx->iocb.aio_data = i + 1;
op_ctx->iocb.aio_lio_opcode = IOCB_CMD_PREAD;
op_ctx->iocb.aio_fildes = fd;

// 分配读取缓冲区
char *read_buffer = malloc(200 * 1024); // 200KB缓冲区
if (read_buffer) {
op_ctx->iocb.aio_buf = (uint64_t)(uintptr_t)read_buffer;
op_ctx->iocb.aio_nbytes = 200 * 1024;
op_ctx->iocb.aio_offset = i * 200 * 1024; // 不同的偏移量

iocb_ptrs&#91;i] = &op_ctx->iocb;
op_ctx->is_submitted = 1;
op_ctx->submit_time = time(NULL);

printf(" 准备操作 %d: 读取200KB数据\n", i + 1);
}
}

// 提交异步操作
printf("\n5. 提交异步操作:\n");
ret = io_submit(ctx, 5, iocb_ptrs);
if (ret < 0) {
printf(" 提交异步操作失败: %s\n", strerror(-ret));
goto cleanup;
}
printf(" ✓ 成功提交 %d 个异步操作\n", ret);

// 执行取消策略
printf("\n6. 执行取消策略:\n");
time_t start_time = time(NULL);
int completed_operations = 0;
int cancelled_operations = 0;

// 监控和取消操作
while (difftime(time(NULL), start_time) < 10) { // 最多等待10秒
// 检查取消条件
for (int i = 0; i < 5; i++) {
operation_context_t *op_ctx = &operations&#91;i];

if (op_ctx->is_submitted && !op_ctx->is_completed && !op_ctx->is_cancelled) {
if (check_cancel_condition(op_ctx)) {
printf(" 尝试取消操作 %d\n", i + 1);

struct io_event cancel_result;
ret = io_cancel(ctx, &op_ctx->iocb, &cancel_result);
if (ret == 0) {
printf(" ✓ 操作 %d 取消成功\n", i + 1);
op_ctx->is_cancelled = 1;
cancelled_operations++;
} else if (ret == -EAGAIN) {
printf(" ℹ 操作 %d 无法取消\n", i + 1);
} else {
printf(" ✗ 操作 %d 取消失败: %s\n", i + 1, strerror(-ret));
}
}
}
}

// 检查完成事件
struct io_event events&#91;5];
struct timespec timeout = {0, 100000000}; // 100ms超时

ret = io_getevents(ctx, 0, 5, events, &timeout);
if (ret > 0) {
printf(" 收到 %d 个完成事件\n", ret);

for (int i = 0; i < ret; i++) {
int op_index = events&#91;i].data - 1;
if (op_index >= 0 && op_index < 5) {
operation_context_t *op_ctx = &operations&#91;op_index];
op_ctx->is_completed = 1;
completed_operations++;

printf(" 操作 %d 完成: %ld 字节\n",
op_index + 1, events&#91;i].res);
}
}
}

// 检查是否所有操作都已完成或取消
int all_done = 1;
for (int i = 0; i < 5; i++) {
operation_context_t *op_ctx = &operations&#91;i];
if (op_ctx->is_submitted && !op_ctx->is_completed && !op_ctx->is_cancelled) {
all_done = 0;
break;
}
}

if (all_done) {
printf(" 所有操作已完成或取消\n");
break;
}

// 更新条件值(模拟条件变化)
for (int i = 0; i < 5; i++) {
operations&#91;i].condition_value += 10;
}

usleep(100000); // 100ms延迟
}

// 最终统计
printf("\n7. 最终统计:\n");
printf(" 总操作数: 5\n");
printf(" 完成操作数: %d\n", completed_operations);
printf(" 取消操作数: %d\n", cancelled_operations);
printf(" 进行中操作数: %d\n", 5 - completed_operations - cancelled_operations);

// 显示每个操作的最终状态
printf("\n8. 操作状态详情:\n");
for (int i = 0; i < 5; i++) {
operation_context_t *op_ctx = &operations&#91;i];
printf(" 操作 %d: ", i + 1);

if (op_ctx->is_cancelled) {
printf("已取消 ");
} else if (op_ctx->is_completed) {
printf("已完成 ");
} else {
printf("进行中 ");
}

switch (op_ctx->strategy) {
case CANCEL_STRATEGY_IMMEDIATE:
printf("(立即取消策略)");
break;
case CANCEL_STRATEGY_TIMEOUT:
printf("(超时取消策略)");
break;
case CANCEL_STRATEGY_CONDITIONAL:
printf("(条件取消策略)");
break;
case CANCEL_STRATEGY_NEVER:
printf("(从不取消策略)");
break;
}
printf("\n");
}

// 清理资源
cleanup:
printf("\n9. 清理资源:\n");

// 释放缓冲区
for (int i = 0; i < 5; i++) {
if (operations&#91;i].iocb.aio_buf) {
free((void*)(uintptr_t)operations&#91;i].iocb.aio_buf);
}
}

close(fd);
unlink(filename);
io_destroy(ctx);
printf(" ✓ 资源清理完成\n");

return 0;
}

int main() {
return demo_conditional_cancel_strategy();
}

示例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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 错误处理上下文
*/
typedef struct {
int error_code;
const char *error_message;
int retry_count;
int max_retries;
time_t error_time;
const char *operation_name;
} error_context_t;

/**
* 记录错误信息
*/
void record_error(error_context_t *ctx, int error_code, const char *operation, const char *message) {
ctx->error_code = error_code;
ctx->error_message = message;
ctx->error_time = time(NULL);
ctx->operation_name = operation;
ctx->retry_count++;

printf("错误记录: %s\n", operation);
printf(" 错误码: %d\n", error_code);
printf(" 错误信息: %s\n", message);
printf(" 重试次数: %d/%d\n", ctx->retry_count, ctx->max_retries);
}

/**
* 错误恢复策略
*/
int apply_recovery_strategy(error_context_t *ctx, aio_context_t aio_ctx, struct iocb *iocb) {
printf("应用错误恢复策略:\n");

switch (ctx->error_code) {
case -EAGAIN:
printf(" EAGAIN错误,操作稍后重试\n");
if (ctx->retry_count < ctx->max_retries) {
printf(" 重试操作...\n");
struct iocb *iocbs&#91;1] = {iocb};
int ret = io_submit(aio_ctx, 1, iocbs);
if (ret == 1) {
printf(" ✓ 重试成功\n");
return 0;
} else {
printf(" ✗ 重试失败: %s\n", strerror(-ret));
record_error(ctx, ret, ctx->operation_name, "重试失败");
}
}
break;

case -ECANCELED:
printf(" ECANCELED错误,操作已被取消\n");
printf(" 尝试重新提交操作...\n");
struct iocb *iocbs&#91;1] = {iocb};
int ret = io_submit(aio_ctx, 1, iocbs);
if (ret == 1) {
printf(" ✓ 重新提交成功\n");
return 0;
} else {
printf(" ✗ 重新提交失败: %s\n", strerror(-ret));
record_error(ctx, ret, ctx->operation_name, "重新提交失败");
}
break;

case -EBADF:
printf(" EBADF错误,文件描述符无效\n");
printf(" 建议:检查文件描述符有效性\n");
break;

case -EINVAL:
printf(" EINVAL错误,参数无效\n");
printf(" 建议:检查参数设置\n");
break;

default:
printf(" 未知错误: %s\n", strerror(-ctx->error_code));
printf(" 建议:记录错误并采取适当措施\n");
break;
}

return -1;
}

/**
* 演示错误处理和恢复
*/
int demo_error_handling_recovery() {
aio_context_t ctx;
struct iocb iocb;
struct io_event result;
int fd;
int ret;
error_context_t error_ctx = {0};

printf("=== 错误处理和恢复演示 ===\n");

// 设置错误处理上下文
error_ctx.max_retries = 3;
error_ctx.retry_count = 0;

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(32, &ctx);
if (ret < 0) {
record_error(&error_ctx, ret, "io_setup", "初始化AIO上下文失败");
apply_recovery_strategy(&error_ctx, ctx, NULL);
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *filename = "error_recovery_test.txt";
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
record_error(&error_ctx, -errno, "open", "创建测试文件失败");
io_destroy(ctx);
return -1;
}
printf(" ✓ 测试文件创建成功\n");

// 准备异步写入操作
printf("\n3. 准备异步写入操作:\n");
const char *test_data = "Error handling and recovery test data.\n";
size_t data_size = strlen(test_data);

memset(&iocb, 0, sizeof(iocb));
iocb.aio_data = 1001;
iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
iocb.aio_fildes = fd;
iocb.aio_buf = (uint64_t)(uintptr_t)test_data;
iocb.aio_nbytes = data_size;
iocb.aio_offset = 0;

printf(" 准备写入操作:\n");
printf(" 数据大小: %zu 字节\n", data_size);
printf(" 用户数据: %llu\n", (unsigned long long)iocb.aio_data);

// 提交异步操作
printf("\n4. 提交异步操作:\n");
struct iocb *iocbs&#91;1] = {&iocb};
ret = io_submit(ctx, 1, iocbs);
if (ret != 1) {
record_error(&error_ctx, ret, "io_submit", "提交异步操作失败");
if (apply_recovery_strategy(&error_ctx, ctx, &iocb) != 0) {
close(fd);
unlink(filename);
io_destroy(ctx);
return -1;
}
}
printf(" ✓ 异步操作提交成功\n");

// 演示各种错误场景
printf("\n5. 演示错误场景:\n");

// 场景1: 尝试取消已完成的操作
printf(" 场景1: 尝试取消已完成的操作\n");
struct io_event events&#91;1];
struct timespec timeout = {2, 0}; // 2秒超时

ret = io_getevents(ctx, 1, 1, events, &timeout);
if (ret > 0) {
printf(" 操作已完成,尝试取消...\n");
ret = io_cancel(ctx, &iocb, &result);
if (ret == -EAGAIN) {
printf(" ✓ 取消失败:操作已完成 (EAGAIN)\n");
} else if (ret == 0) {
printf(" ✓ 操作取消成功\n");
} else {
printf(" 取消操作返回: %s\n", strerror(-ret));
}
}

// 场景2: 使用无效的iocb尝试取消
printf("\n 场景2: 使用无效的iocb尝试取消\n");
struct iocb invalid_iocb;
memset(&invalid_iocb, 0, sizeof(invalid_iocb));
invalid_iocb.aio_data = 9999;

ret = io_cancel(ctx, &invalid_iocb, &result);
if (ret == -EAGAIN) {
printf(" ✓ 取消失败:无效操作 (EAGAIN)\n");
} else {
printf(" 取消操作返回: %s\n", strerror(-ret));
}

// 场景3: 使用无效的上下文ID
printf("\n 场景3: 使用无效的上下文ID\n");
struct iocb test_iocb;
memset(&test_iocb, 0, sizeof(test_iocb));

aio_context_t invalid_ctx = (aio_context_t)-1;
ret = io_cancel(invalid_ctx, &test_iocb, &result);
if (ret == -EINVAL) {
printf(" ✓ 操作失败:无效上下文ID (EINVAL)\n");
} else {
printf(" 取消操作返回: %s\n", strerror(-ret));
}

// 场景4: 重复取消同一个操作
printf("\n 场景4: 重复取消操作\n");
ret = io_cancel(ctx, &iocb, &result);
if (ret == -EAGAIN) {
printf(" ✓ 第二次取消失败:操作已处理 (EAGAIN)\n");
} else if (ret == 0) {
printf(" ✓ 第二次取消成功\n");
ret = io_cancel(ctx, &iocb, &result);
if (ret == -EAGAIN) {
printf(" ✓ 第三次取消失败:操作已处理 (EAGAIN)\n");
}
} else {
printf(" 第二次取消操作返回: %s\n", strerror(-ret));
}

// 演示资源清理错误处理
printf("\n6. 演示资源清理错误处理:\n");

// 正常关闭文件
printf(" 正常关闭文件:\n");
if (close(fd) == 0) {
printf(" ✓ 文件关闭成功\n");
} else {
record_error(&error_ctx, -errno, "close", "关闭文件失败");
printf(" 尝试强制清理...\n");
}

// 删除测试文件
printf(" 删除测试文件:\n");
if (unlink(filename) == 0) {
printf(" ✓ 测试文件删除成功\n");
} else {
record_error(&error_ctx, -errno, "unlink", "删除测试文件失败");
printf(" 注意:可能需要手动清理测试文件\n");
}

// 销毁AIO上下文
printf(" 销毁AIO上下文:\n");
ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ AIO上下文销毁成功\n");
} else {
record_error(&error_ctx, ret, "io_destroy", "销毁AIO上下文失败");
printf(" 销毁操作返回: %s\n", strerror(-ret));
}

// 显示错误处理总结
printf("\n=== 错误处理总结 ===\n");
printf("1. 常见错误类型:\n");
printf(" ✓ EAGAIN: 操作无法取消(已完成或不存在)\n");
printf(" ✓ EINVAL: 参数无效\n");
printf(" ✓ EBADF: 文件描述符无效\n");
printf(" ✓ ECANCELED: 操作已被取消\n");

printf("\n2. 错误处理策略:\n");
printf(" ✓ 记录错误信息\n");
printf(" ✓ 根据错误类型采取不同措施\n");
printf(" ✓ 必要时重试操作\n");
printf(" ✓ 优雅降级处理\n");

printf("\n3. 恢复策略:\n");
printf(" ✓ 重试机制\n");
printf(" ✓ 备用方案\n");
printf(" ✓ 资源清理\n");
printf(" ✓ 状态恢复\n");

printf("\n4. 最佳实践:\n");
printf(" ✓ 完善的错误记录\n");
printf(" ✓ 适当的重试策略\n");
printf(" ✓ 资源泄漏预防\n");
printf(" ✓ 用户友好提示\n");

return 0;
}

int main() {
return demo_error_handling_recovery();
}

io_cancel 使用注意事项

系统要求:

内核版本: 需要支持AIO的Linux内核

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

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

取消限制:

操作状态: 只能取消排队或正在执行的操作

时间窗口: 已完成的操作无法取消

资源释放: 取消后需要清理相关资源

错误处理:

EAGAIN: 操作无法取消(已完成或不存在)

EINVAL: 参数无效

EBADF: 文件描述符无效

ECANCELED: 操作已被取消

性能考虑:

取消开销: 取消操作本身也有性能开销

批量处理: 批量取消可能更高效

超时设置: 合理设置超时时间

状态检查: 取消前检查操作状态

安全考虑:

参数验证: 验证所有输入参数

内存管理: 确保缓冲区内存有效

资源清理: 及时清理已取消操作的资源

状态同步: 确保多线程环境下的状态一致性

最佳实践:

及时取消: 不需要的操作及时取消

状态跟踪: 跟踪每个操作的状态

错误处理: 妥善处理取消失败的情况

资源管理: 确保资源得到正确释放

超时机制: 实现合理的超时取消机制

io_cancel vs 相似函数对比

io_cancel vs io_destroy:

1
2
3
4
5
6
// io_cancel: 取消单个操作
io_cancel(ctx, &iocb, &result);

// io_destroy: 销毁整个AIO上下文(取消所有操作)
io_destroy(ctx);

io_cancel vs io_getevents:

1
2
3
4
5
6
// io_cancel: 主动取消操作
io_cancel(ctx, &iocb, &result);

// io_getevents: 被动等待操作完成
io_getevents(ctx, 1, 1, events, &timeout);

常见使用场景

1. 超时处理:

1
2
3
4
5
// 异步操作超时后取消
if (operation_timeout) {
io_cancel(ctx, &iocb, &result);
}

2. 用户中断:

1
2
3
4
5
// 用户取消操作时取消所有相关异步操作
for (int i = 0; i < operation_count; i++) {
io_cancel(ctx, &iocbs&#91;i], &results&#91;i]);
}

3. 资源清理:

1
2
3
// 应用程序退出前取消未完成的操作
io_cancel(ctx, &iocb, &result);

总结

io_cancel 是Linux AIO框架中重要的操作取消函数,提供了:

操作控制: 精确控制异步操作的生命周期

资源管理: 及时释放不需要的资源

错误处理: 完善的错误处理机制

灵活性: 支持多种取消策略

通过合理使用 io_cancel,可以构建更加健壮和灵活的异步I/O应用。在实际应用中,需要注意错误处理、资源管理和性能优化等关键问题。

io_destroy函数详解

io_destroy 函数详解

  1. 函数介绍

io_destroy 是Linux传统异步I/O (AIO) 系统调用,用于销毁和清理之前通过 io_setup 创建的异步I/O上下文。它是AIO框架的重要组成部分,负责释放与AIO上下文相关的所有内核资源,包括环形缓冲区、等待队列和其他内部数据结构。

  1. 函数原型
1
2
3
4
5
6
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>

int io_destroy(aio_context_t ctx);

  1. 功能

io_destroy 彻底销毁指定的AIO上下文,释放所有相关的内核资源。在销毁过程中,所有未完成的异步操作都会被取消,相关的事件会被清除,确保系统资源得到正确回收。

  1. 参数
  • aio_context_t ctx: 要销毁的AIO上下文ID(由io_setup返回)
  1. 返回值
  • 成功: 返回0

  • 失败: 返回负的错误码

  1. 相似函数,或关联函数
  • io_setup: 初始化AIO上下文

  • io_submit: 提交异步I/O操作

  • io_cancel: 取消异步I/O操作

  • io_getevents: 获取完成的异步I/O事件

  • close: 关闭文件描述符

  1. 示例代码

示例1:基础io_destroy使用

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
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 演示基础io_destroy使用方法
*/
int demo_io_destroy_basic() {
aio_context_t ctx;
int ret;

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

// 初始化AIO上下文
printf("1. 初始化AIO上下文:\n");
ctx = 0;
ret = io_setup(128, &ctx);
if (ret < 0) {
printf(" 初始化AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ AIO上下文初始化成功\n");
printf(" 上下文ID: %llu\n", (unsigned long long)ctx);

// 显示上下文信息
printf(" 上下文状态: 已创建\n");
printf(" 缓冲区大小: 128 个事件\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *filename = "io_destroy_test.txt";
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror(" 创建测试文件失败");
io_destroy(ctx);
return -1;
}
printf(" ✓ 测试文件创建成功: %s\n", filename);

// 准备异步写入操作
printf("\n3. 准备异步写入操作:\n");
char test_data&#91;] = "Test data for io_destroy demonstration.\n";
struct iocb iocb;

memset(&iocb, 0, sizeof(iocb));
iocb.aio_data = 12345;
iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
iocb.aio_fildes = fd;
iocb.aio_buf = (uint64_t)(uintptr_t)test_data;
iocb.aio_nbytes = strlen(test_data);
iocb.aio_offset = 0;

printf(" 准备异步写入操作:\n");
printf(" 文件描述符: %d\n", fd);
printf(" 数据大小: %zu 字节\n", strlen(test_data));
printf(" 用户数据: %llu\n", (unsigned long long)iocb.aio_data);

// 提交异步操作
printf("\n4. 提交异步操作:\n");
struct iocb *iocbs&#91;1] = {&iocb};
ret = io_submit(ctx, 1, iocbs);
if (ret != 1) {
printf(" 提交异步操作失败: %s\n", strerror(-ret));
close(fd);
unlink(filename);
io_destroy(ctx);
return -1;
}
printf(" ✓ 异步操作提交成功\n");

// 等待操作完成
printf("\n5. 等待操作完成:\n");
struct io_event events&#91;1];
struct timespec timeout = {5, 0}; // 5秒超时

ret = io_getevents(ctx, 1, 1, events, &timeout);
if (ret > 0) {
printf(" ✓ 操作完成\n");
printf(" 用户数据: %llu\n", (unsigned long long)events&#91;0].data);
printf(" 结果: %ld 字节\n", events&#91;0].res);
printf(" 错误: %ld\n", events&#91;0].res2);
} else if (ret == 0) {
printf(" ⚠ 操作超时\n");
} else {
printf(" ✗ 等待完成事件失败: %s\n", strerror(-ret));
}

// 关闭文件
printf("\n6. 关闭文件:\n");
close(fd);
printf(" ✓ 文件关闭成功\n");

// 销毁AIO上下文
printf("\n7. 销毁AIO上下文:\n");
printf(" 调用: io_destroy(%llu)\n", (unsigned long long)ctx);

ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ AIO上下文销毁成功\n");
printf(" 上下文状态: 已销毁\n");
} else {
printf(" ✗ AIO上下文销毁失败: %s\n", strerror(-ret));
if (ret == -EINVAL) {
printf(" 原因:无效的上下文ID\n");
} else if (ret == -EAGAIN) {
printf(" 原因:上下文中仍有未完成的操作\n");
}
}

// 清理测试文件
printf("\n8. 清理测试文件:\n");
if (unlink(filename) == 0) {
printf(" ✓ 测试文件清理成功\n");
} else {
printf(" ✗ 测试文件清理失败: %s\n", strerror(errno));
}

return 0;
}

int main() {
return demo_io_destroy_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
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
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/resource.h>

/**
* 资源使用统计结构
*/
typedef struct {
unsigned long open_files;
unsigned long memory_usage_kb;
unsigned long aio_contexts;
} resource_usage_t;

/**
* 获取资源使用情况
*/
int get_resource_usage(resource_usage_t *usage) {
// 获取打开文件数
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
usage->open_files = rl.rlim_cur;
} else {
usage->open_files = 0;
}

// 获取内存使用情况
FILE *fp = fopen("/proc/self/status", "r");
if (fp) {
char line&#91;256];
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "VmRSS:", 6) == 0) {
sscanf(line + 6, "%lu", &usage->memory_usage_kb);
break;
}
}
fclose(fp);
} else {
usage->memory_usage_kb = 0;
}

// 获取AIO上下文信息(简化的模拟)
usage->aio_contexts = 0; // 实际应用中需要更复杂的实现

return 0;
}

/**
* 显示资源使用情况
*/
void show_resource_usage(const char *label, const resource_usage_t *usage) {
printf("%s:\n", label);
printf(" 打开文件数: %lu\n", usage->open_files);
printf(" 内存使用: %lu KB\n", usage->memory_usage_kb);
printf(" AIO上下文: %lu\n", usage->aio_contexts);
}

/**
* 演示资源泄漏检测
*/
int demo_resource_leak_detection() {
aio_context_t contexts&#91;10];
resource_usage_t before_usage, after_usage, after_destroy_usage;
int created_count = 0;
int destroyed_count = 0;

printf("=== 资源泄漏检测演示 ===\n");

// 获取初始资源使用情况
printf("1. 获取初始资源使用情况:\n");
if (get_resource_usage(&before_usage) == 0) {
show_resource_usage(" 初始状态", &before_usage);
}

// 创建多个AIO上下文
printf("\n2. 创建多个AIO上下文:\n");

for (int i = 0; i < 10; i++) {
contexts&#91;i] = 0;
int ret = io_setup(64, &contexts&#91;i]);
if (ret == 0) {
printf(" ✓ 创建AIO上下文 %d: ID=%llu\n", i + 1, (unsigned long long)contexts&#91;i]);
created_count++;
} else {
printf(" ✗ 创建AIO上下文 %d 失败: %s\n", i + 1, strerror(-ret));
}
}

printf(" 成功创建 %d 个AIO上下文\n", created_count);

// 获取创建后的资源使用情况
printf("\n3. 获取创建后的资源使用情况:\n");
sleep(1); // 等待系统更新资源统计

if (get_resource_usage(&after_usage) == 0) {
show_resource_usage(" 创建后状态", &after_usage);

// 显示资源变化
if (after_usage.memory_usage_kb > before_usage.memory_usage_kb) {
printf(" 内存使用增加: %lu KB\n",
after_usage.memory_usage_kb - before_usage.memory_usage_kb);
}
}

// 销毁部分AIO上下文
printf("\n4. 销毁部分AIO上下文:\n");

// 销毁前5个上下文
for (int i = 0; i < 5 && i < created_count; i++) {
int ret = io_destroy(contexts&#91;i]);
if (ret == 0) {
printf(" ✓ 销毁AIO上下文 %d: ID=%llu\n", i + 1, (unsigned long long)contexts&#91;i]);
destroyed_count++;
contexts&#91;i] = 0; // 标记为已销毁
} else {
printf(" ✗ 销毁AIO上下文 %d 失败: %s\n", i + 1, strerror(-ret));
}
}

printf(" 成功销毁 %d 个AIO上下文\n", destroyed_count);

// 获取销毁后的资源使用情况
printf("\n5. 获取销毁后的资源使用情况:\n");
sleep(1); // 等待系统更新资源统计

if (get_resource_usage(&after_destroy_usage) == 0) {
show_resource_usage(" 销毁后状态", &after_destroy_usage);

// 显示资源变化
if (after_destroy_usage.memory_usage_kb < after_usage.memory_usage_kb) {
printf(" 内存使用减少: %lu KB\n",
after_usage.memory_usage_kb - after_destroy_usage.memory_usage_kb);
}

if (after_destroy_usage.memory_usage_kb < before_usage.memory_usage_kb) {
printf(" 内存使用净减少: %lu KB\n",
before_usage.memory_usage_kb - after_destroy_usage.memory_usage_kb);
}
}

// 销毁剩余的AIO上下文
printf("\n6. 销毁剩余的AIO上下文:\n");

for (int i = 5; i < created_count; i++) {
if (contexts&#91;i] != 0) { // 未被销毁的上下文
int ret = io_destroy(contexts&#91;i]);
if (ret == 0) {
printf(" ✓ 销毁AIO上下文 %d: ID=%llu\n", i + 1, (unsigned long long)contexts&#91;i]);
destroyed_count++;
} else {
printf(" ✗ 销毁AIO上下文 %d 失败: %s\n", i + 1, strerror(-ret));
}
}
}

printf(" 总共销毁 %d 个AIO上下文\n", destroyed_count);

// 最终资源使用情况
printf("\n7. 最终资源使用情况:\n");
resource_usage_t final_usage;
sleep(1); // 等待系统更新资源统计

if (get_resource_usage(&final_usage) == 0) {
show_resource_usage(" 最终状态", &final_usage);

// 资源泄漏检测
printf("\n8. 资源泄漏检测:\n");

if (final_usage.memory_usage_kb <= before_usage.memory_usage_kb + 100) {
printf(" ✓ 内存资源回收良好\n");
} else {
printf(" ⚠ 可能存在内存泄漏\n");
printf(" 内存使用增加: %lu KB\n",
final_usage.memory_usage_kb - before_usage.memory_usage_kb);
}

if (final_usage.open_files == before_usage.open_files) {
printf(" ✓ 文件描述符资源回收良好\n");
} else {
printf(" ⚠ 可能存在文件描述符泄漏\n");
}
}

// 显示资源管理最佳实践
printf("\n=== 资源管理最佳实践 ===\n");
printf("1. 及时销毁:\n");
printf(" ✓ 使用完AIO上下文后立即销毁\n");
printf(" ✓ 避免长期持有不必要的上下文\n");
printf(" ✓ 批量销毁提高效率\n");

printf("\n2. 错误处理:\n");
printf(" ✓ 妥善处理销毁失败的情况\n");
printf(" ✓ 记录销毁操作日志\n");
printf(" ✓ 重试机制处理临时失败\n");

printf("\n3. 资源监控:\n");
printf(" ✓ 监控资源使用情况\n");
printf(" ✓ 检测潜在的资源泄漏\n");
printf(" ✓ 定期清理闲置资源\n");

printf("\n4. 安全考虑:\n");
printf(" ✓ 验证上下文ID的有效性\n");
printf(" ✓ 避免重复销毁同一上下文\n");
printf(" ✓ 处理并发访问问题\n");

return 0;
}

int main() {
return demo_resource_leak_detection();
}

示例3:批量销毁管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

/**
* AIO上下文管理器结构
*/
typedef struct {
aio_context_t contexts&#91;64];
int context_count;
int max_contexts;
time_t creation_time;
unsigned long total_created;
unsigned long total_destroyed;
} aio_context_manager_t;

/**
* 初始化AIO上下文管理器
*/
int init_aio_context_manager(aio_context_manager_t *manager) {
memset(manager, 0, sizeof(aio_context_manager_t));
manager->max_contexts = 64;
manager->creation_time = time(NULL);
manager->total_created = 0;
manager->total_destroyed = 0;

printf("AIO上下文管理器初始化完成\n");
printf(" 最大上下文数: %d\n", manager->max_contexts);
printf(" 创建时间: %s", ctime(&manager->creation_time));

return 0;
}

/**
* 创建AIO上下文
*/
int create_aio_context(aio_context_manager_t *manager, unsigned nr_events, aio_context_t *ctx) {
if (manager->context_count >= manager->max_contexts) {
printf("AIO上下文数量已达上限\n");
return -1;
}

*ctx = 0;
int ret = io_setup(nr_events, ctx);
if (ret == 0) {
manager->contexts&#91;manager->context_count] = *ctx;
manager->context_count++;
manager->total_created++;
printf("创建AIO上下文成功: ID=%llu\n", (unsigned long long)*ctx);
return 0;
} else {
printf("创建AIO上下文失败: %s\n", strerror(-ret));
return -1;
}
}

/**
* 批量销毁AIO上下文
*/
int batch_destroy_aio_contexts(aio_context_manager_t *manager) {
printf("批量销毁AIO上下文:\n");
printf(" 当前上下文数量: %d\n", manager->context_count);

int destroyed_count = 0;
int failed_count = 0;

// 从后往前销毁(避免数组索引问题)
for (int i = manager->context_count - 1; i >= 0; i--) {
aio_context_t ctx = manager->contexts&#91;i];
if (ctx != 0) {
int ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ 销毁上下文 %d: ID=%llu\n", i + 1, (unsigned long long)ctx);
destroyed_count++;
manager->total_destroyed++;

// 从数组中移除
for (int j = i; j < manager->context_count - 1; j++) {
manager->contexts&#91;j] = manager->contexts&#91;j + 1];
}
manager->context_count--;
} else {
printf(" ✗ 销毁上下文 %d 失败: %s\n", i + 1, strerror(-ret));
failed_count++;
}
}
}

printf("批量销毁完成:\n");
printf(" 成功销毁: %d 个\n", destroyed_count);
printf(" 销毁失败: %d 个\n", failed_count);
printf(" 剩余上下文: %d 个\n", manager->context_count);

return (failed_count == 0) ? 0 : -1;
}

/**
* 销毁所有AIO上下文
*/
int destroy_all_aio_contexts(aio_context_manager_t *manager) {
printf("销毁所有AIO上下文:\n");

if (manager->context_count == 0) {
printf(" 没有待销毁的上下文\n");
return 0;
}

return batch_destroy_aio_contexts(manager);
}

/**
* 演示批量销毁管理
*/
int demo_batch_destroy_management() {
aio_context_manager_t manager;

printf("=== 批量销毁管理演示 ===\n");

// 初始化管理器
printf("1. 初始化AIO上下文管理器:\n");
if (init_aio_context_manager(&manager) != 0) {
return -1;
}

// 创建多个AIO上下文
printf("\n2. 创建多个AIO上下文:\n");

const int create_count = 10;
aio_context_t created_contexts&#91;create_count];
int successful_creates = 0;

for (int i = 0; i < create_count; i++) {
printf(" 创建第 %d 个上下文:\n", i + 1);

if (create_aio_context(&manager, 32, &created_contexts&#91;i]) == 0) {
successful_creates++;
} else {
created_contexts&#91;i] = 0; // 标记为创建失败
}

// 短暂延迟以避免系统过载
if (i < create_count - 1) {
usleep(10000); // 10ms延迟
}
}

printf(" 创建完成:\n");
printf(" 尝试创建: %d 个\n", create_count);
printf(" 成功创建: %d 个\n", successful_creates);
printf(" 创建失败: %d 个\n", create_count - successful_creates);
printf(" 管理器状态: %d 个上下文\n", manager.context_count);

// 显示当前管理器状态
printf("\n3. 当前管理器状态:\n");
printf(" 总创建数: %lu\n", manager.total_created);
printf(" 总销毁数: %lu\n", manager.total_destroyed);
printf(" 当前上下文数: %d\n", manager.context_count);
printf(" 管理器运行时间: %ld 秒\n",
(long)difftime(time(NULL), manager.creation_time));

// 显示所有上下文ID
printf(" 当前上下文ID列表:\n");
for (int i = 0; i < manager.context_count; i++) {
printf(" %d: %llu\n", i + 1, (unsigned long long)manager.contexts&#91;i]);
}

// 批量销毁
printf("\n4. 批量销毁操作:\n");
if (destroy_all_aio_contexts(&manager) != 0) {
printf(" 批量销毁过程中出现错误\n");
}

// 验证销毁结果
printf("\n5. 验证销毁结果:\n");
printf(" 销毁后状态:\n");
printf(" 当前上下文数: %d\n", manager.context_count);
printf(" 总销毁数: %lu\n", manager.total_destroyed);
printf(" 剩余上下文ID:\n");

if (manager.context_count > 0) {
for (int i = 0; i < manager.context_count; i++) {
printf(" %d: %llu\n", i + 1, (unsigned long long)manager.contexts&#91;i]);
}
printf(" ⚠ 警告:仍有 %d 个上下文未被销毁\n", manager.context_count);
} else {
printf(" 无\n");
printf(" ✓ 所有上下文均已销毁\n");
}

// 重新创建一些上下文进行最终销毁测试
printf("\n6. 最终销毁测试:\n");

// 重新创建几个上下文
printf(" 重新创建测试上下文:\n");
for (int i = 0; i < 3; i++) {
if (create_aio_context(&manager, 16, &created_contexts&#91;i]) == 0) {
printf(" 重新创建上下文 %d 成功\n", i + 1);
}
}

printf(" 重新创建后状态:\n");
printf(" 当前上下文数: %d\n", manager.context_count);
printf(" 总创建数: %lu\n", manager.total_created);

// 最终批量销毁
printf(" 执行最终批量销毁:\n");
if (destroy_all_aio_contexts(&manager) == 0) {
printf(" ✓ 最终批量销毁成功\n");
} else {
printf(" ✗ 最终批量销毁失败\n");
}

// 显示最终统计
printf("\n7. 最终统计:\n");
printf(" 管理器生命周期统计:\n");
printf(" 总创建数: %lu\n", manager.total_created);
printf(" 总销毁数: %lu\n", manager.total_destroyed);
printf(" 当前上下文数: %d\n", manager.context_count);
printf(" 运行时间: %ld 秒\n",
(long)difftime(time(NULL), manager.creation_time));

if (manager.total_created == manager.total_destroyed && manager.context_count == 0) {
printf(" ✓ 资源管理完整性检查通过\n");
} else {
printf(" ⚠ 资源管理完整性检查失败\n");
printf(" 可能存在资源泄漏\n");
}

return 0;
}

int main() {
return demo_batch_destroy_management();
}

示例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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbpp);
}

/**
* 演示异常情况处理
*/
int demo_exception_handling() {
aio_context_t ctx;
int ret;

printf("=== 异常情况处理演示 ===\n");

// 1. 销毁无效的上下文ID
printf("1. 销毁无效的上下文ID:\n");

// 测试销毁0值上下文ID
printf(" 测试销毁上下文ID 0:\n");
ret = io_destroy(0);
if (ret == 0) {
printf(" ✓ 销毁成功(可能表示空操作)\n");
} else {
printf(" %s\n", strerror(-ret));
if (ret == -EINVAL) {
printf(" ✓ 预期结果:无效的上下文ID\n");
} else {
printf(" ✗ 意外错误\n");
}
}

// 测试销毁负值上下文ID
printf(" 测试销毁负值上下文ID (-1):\n");
ret = io_destroy((aio_context_t)-1);
if (ret == 0) {
printf(" ✓ 销毁成功\n");
} else {
printf(" %s\n", strerror(-ret));
if (ret == -EINVAL) {
printf(" ✓ 预期结果:无效的上下文ID\n");
} else {
printf(" ✗ 意外错误\n");
}
}

// 测试销毁超大值上下文ID
printf(" 测试销毁超大值上下文ID:\n");
aio_context_t large_ctx = (aio_context_t)0x7FFFFFFFFFFFFFFFULL;
ret = io_destroy(large_ctx);
if (ret == 0) {
printf(" ✓ 销毁成功\n");
} else {
printf(" %s\n", strerror(-ret));
if (ret == -EINVAL) {
printf(" ✓ 预期结果:无效的上下文ID\n");
} else {
printf(" ✗ 意外错误\n");
}
}

// 2. 重复销毁同一上下文
printf("\n2. 重复销毁同一上下文:\n");

// 创建一个有效的上下文
printf(" 创建有效上下文:\n");
ctx = 0;
ret = io_setup(32, &ctx);
if (ret == 0) {
printf(" ✓ 上下文创建成功: ID=%llu\n", (unsigned long long)ctx);

// 第一次销毁
printf(" 第一次销毁:\n");
ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ 第一次销毁成功\n");
} else {
printf(" ✗ 第一次销毁失败: %s\n", strerror(-ret));
}

// 第二次销毁同一上下文(应该失败)
printf(" 第二次销毁同一上下文:\n");
ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ 第二次销毁成功(可能表示幂等操作)\n");
} else {
printf(" %s\n", strerror(-ret));
if (ret == -EINVAL) {
printf(" ✓ 预期结果:上下文已销毁\n");
} else {
printf(" ✗ 意外错误\n");
}
}
} else {
printf(" ✗ 创建上下文失败: %s\n", strerror(-ret));
}

// 3. 销毁正在使用的上下文
printf("\n3. 销毁正在使用的上下文:\n");

// 创建新的上下文
printf(" 创建新上下文:\n");
ctx = 0;
ret = io_setup(64, &ctx);
if (ret == 0) {
printf(" ✓ 新上下文创建成功: ID=%llu\n", (unsigned long long)ctx);

// 创建测试文件
const char *filename = "in_use_test.txt";
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
printf(" ✓ 测试文件创建成功\n");

// 准备长时间写入操作
char *large_data = malloc(1024 * 1024); // 1MB数据
if (large_data) {
// 填充测试数据
for (int i = 0; i < 1024 * 1024; i++) {
large_data&#91;i] = 'A' + (i % 26);
}

// 准备异步写入操作
struct iocb iocb;
memset(&iocb, 0, sizeof(iocb));
iocb.aio_data = 999;
iocb.aio_lio_opcode = IOCB_CMD_PWRITE;
iocb.aio_fildes = fd;
iocb.aio_buf = (uint64_t)(uintptr_t)large_data;
iocb.aio_nbytes = 1024 * 1024;
iocb.aio_offset = 0;

// 提交异步操作
struct iocb *iocbs&#91;1] = {&iocb};
ret = io_submit(ctx, 1, iocbs);
if (ret == 1) {
printf(" ✓ 异步写入操作提交成功\n");

// 立即尝试销毁上下文
printf(" 立即尝试销毁正在使用的上下文:\n");
ret = io_destroy(ctx);
if (ret == 0) {
printf(" ✓ 上下文销毁成功\n");
} else {
printf(" %s\n", strerror(-ret));
if (ret == -EAGAIN) {
printf(" ✓ 预期结果:上下文正在使用中\n");
} else if (ret == -EINVAL) {
printf(" ✓ 预期结果:上下文无效\n");
} else {
printf(" ✗ 意外错误\n");
}
}
} else {
printf(" ✗ 提交异步操作失败: %s\n", strerror(-ret));
}

free(large_data);
}

close(fd);
unlink(filename);
}
} else {
printf(" ✗ 创建新上下文失败: %s\n", strerror(-ret));
}

// 4. 错误恢复演示
printf("\n4. 错误恢复演示:\n");

// 模拟错误恢复场景
printf(" 错误恢复策略:\n");
printf(" 1. 验证上下文ID有效性\n");
printf(" 2. 检查上下文使用状态\n");
printf(" 3. 尝试取消未完成操作\n");
printf(" 4. 安全销毁上下文\n");
printf(" 5. 记录错误日志\n");
printf(" 6. 通知上层应用\n");

// 5. 资源清理验证
printf("\n5. 资源清理验证:\n");

// 创建多个上下文用于清理验证
aio_context_t test_contexts&#91;5];
int created_count = 0;

printf(" 创建测试上下文:\n");
for (int i = 0; i < 5; i++) {
test_contexts&#91;i] = 0;
ret = io_setup(16, &test_contexts&#91;i]);
if (ret == 0) {
printf(" ✓ 上下文 %d 创建成功: ID=%llu\n",
i + 1, (unsigned long long)test_contexts&#91;i]);
created_count++;
} else {
printf(" ✗ 上下文 %d 创建失败: %s\n", i + 1, strerror(-ret));
test_contexts&#91;i] = 0; // 标记为未创建
}
}

printf(" 清理所有测试上下文:\n");
int destroyed_count = 0;
int error_count = 0;

for (int i = 0; i < 5; i++) {
if (test_contexts&#91;i] != 0) {
ret = io_destroy(test_contexts&#91;i]);
if (ret == 0) {
printf(" ✓ 上下文 %d 销毁成功\n", i + 1);
destroyed_count++;
} else {
printf(" ✗ 上下文 %d 销毁失败: %s\n", i + 1, strerror(-ret));
error_count++;
}
}
}

printf(" 清理结果:\n");
printf(" 成功销毁: %d 个\n", destroyed_count);
printf(" 销毁失败: %d 个\n", error_count);
printf(" 总计处理: %d 个\n", destroyed_count + error_count);

// 6. 最终状态检查
printf("\n6. 最终状态检查:\n");
printf(" 系统状态:\n");
printf(" ✓ 错误处理机制正常\n");
printf(" ✓ 资源清理机制正常\n");
printf(" ✓ 状态恢复机制正常\n");
printf(" ✓ 日志记录机制正常\n");

return 0;
}

int main() {
return demo_exception_handling();
}

示例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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#include <linux/aio_abi.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>

/**
* 生产环境AIO上下文管理器
*/
typedef struct {
aio_context_t ctx;
int is_initialized;
int is_destroyed;
time_t create_time;
time_t destroy_time;
unsigned long operations_submitted;
unsigned long operations_completed;
unsigned long operations_cancelled;
pthread_mutex_t mutex;
volatile int shutdown_requested;
} production_aio_context_t;

/**
* 系统调用包装函数
*/
static inline int io_setup(unsigned nr_events, aio_context_t *ctxp) {
return syscall(__NR_io_setup, nr_events, ctxp);
}

static inline int io_destroy(aio_context_t ctx) {
return syscall(__NR_io_destroy, ctx);
}

static inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp) {
return syscall(__NR_io_submit, ctx, nr, iocbsp);
}

static inline int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result) {
return syscall(__NR_io_cancel, ctx, iocb, result);
}

static inline int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout) {
return syscall(__NR_io_getevents, ctx, min_nr, nr, events, timeout);
}

/**
* 初始化生产环境AIO上下文
*/
int init_production_aio_context(production_aio_context_t *ctx, unsigned nr_events) {
// 初始化结构体
memset(ctx, 0, sizeof(production_aio_context_t));
ctx->create_time = time(NULL);
ctx->is_initialized = 0;
ctx->is_destroyed = 0;
ctx->shutdown_requested = 0;

// 初始化互斥锁
if (pthread_mutex_init(&ctx->mutex, NULL) != 0) {
printf("初始化互斥锁失败\n");
return -1;
}

// 创建AIO上下文
ctx->ctx = 0;
int ret = io_setup(nr_events, &ctx->ctx);
if (ret != 0) {
printf("创建AIO上下文失败: %s\n", strerror(-ret));
pthread_mutex_destroy(&ctx->mutex);
return -1;
}

ctx->is_initialized = 1;
printf("生产环境AIO上下文初始化成功:\n");
printf(" 上下文ID: %llu\n", (unsigned long long)ctx->ctx);
printf(" 缓冲区大小: %u 事件\n", nr_events);
printf(" 创建时间: %s", ctime(&ctx->create_time));

return 0;
}

/**
* 安全销毁生产环境AIO上下文
*/
int destroy_production_aio_context(production_aio_context_t *ctx) {
if (!ctx) {
printf("上下文指针无效\n");
return -1;
}

// 获取互斥锁
if (pthread_mutex_lock(&ctx->mutex) != 0) {
printf("获取互斥锁失败\n");
return -1;
}

// 检查是否已经销毁
if (ctx->is_destroyed) {
printf("上下文已销毁,无需重复操作\n");
pthread_mutex_unlock(&ctx->mutex);
return 0;
}

// 设置关闭请求标志
ctx->shutdown_requested = 1;

// 取消所有未完成的操作
printf("取消所有未完成的操作...\n");
// 这里应该实现取消未完成操作的逻辑

// 销毁AIO上下文
printf("销毁AIO上下文: %llu\n", (unsigned long long)ctx->ctx);
int ret = io_destroy(ctx->ctx);
if (ret != 0) {
printf("销毁AIO上下文失败: %s\n", strerror(-ret));
if (ret == -EAGAIN) {
printf(" 原因:上下文中仍有未完成的操作\n");
} else if (ret == -EINVAL) {
printf(" 原因:无效的上下文ID\n");
}

// 即使销毁失败也要清理资源
ctx->is_destroyed = 1;
ctx->destroy_time = time(NULL);
pthread_mutex_unlock(&ctx->mutex);
pthread_mutex_destroy(&ctx->mutex);
return -1;
}

ctx->is_destroyed = 1;
ctx->destroy_time = time(NULL);

// 显示统计信息
printf("AIO上下文销毁成功:\n");
printf(" 销毁时间: %s", ctime(&ctx->destroy_time));
printf(" 运行时间: %ld 秒\n",
(long)difftime(ctx->destroy_time, ctx->create_time));
printf(" 提交操作数: %lu\n", ctx->operations_submitted);
printf(" 完成操作数: %lu\n", ctx->operations_completed);
printf(" 取消操作数: %lu\n", ctx->operations_cancelled);

pthread_mutex_unlock(&ctx->mutex);
pthread_mutex_destroy(&ctx->mutex);

return 0;
}

/**
* 优雅关闭信号处理
*/
void graceful_shutdown_handler(int sig) {
printf("\n收到关闭信号 %d,开始优雅关闭...\n", sig);
// 这里应该通知所有AIO上下文管理器开始关闭
}

/**
* 演示生产环境使用模式
*/
int demo_production_usage_pattern() {
production_aio_context_t aio_ctx;
struct sigaction sa;

printf("=== 生产环境使用模式演示 ===\n");

// 设置信号处理
printf("1. 设置信号处理:\n");
memset(&sa, 0, sizeof(sa));
sa.sa_handler = graceful_shutdown_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;

if (sigaction(SIGINT, &sa, NULL) == 0) {
printf(" ✓ SIGINT信号处理设置成功\n");
} else {
printf(" ✗ SIGINT信号处理设置失败: %s\n", strerror(errno));
}

if (sigaction(SIGTERM, &sa, NULL) == 0) {
printf(" ✓ SIGTERM信号处理设置成功\n");
} else {
printf(" ✗ SIGTERM信号处理设置失败: %s\n", strerror(errno));
}

// 初始化AIO上下文
printf("\n2. 初始化生产环境AIO上下文:\n");
if (init_production_aio_context(&aio_ctx, 1024) != 0) {
printf("初始化生产环境AIO上下文失败\n");
return -1;
}

printf(" ✓ 生产环境AIO上下文初始化成功\n");

// 模拟生产环境操作
printf("\n3. 模拟生产环境操作:\n");

// 模拟提交操作
printf(" 模拟提交异步操作:\n");
for (int i = 0; i < 100; i++) {
// 这里应该是实际的异步操作提交
aio_ctx.operations_submitted++;

if (i % 20 == 0) {
printf(" 已提交 %d 个操作\n", i);
}

// 模拟操作完成
if (i % 3 == 0) {
aio_ctx.operations_completed++;
}

// 模拟操作取消
if (i % 7 == 0) {
aio_ctx.operations_cancelled++;
}
}

printf(" ✓ 模拟操作完成\n");
printf(" 总提交: %lu\n", aio_ctx.operations_submitted);
printf(" 总完成: %lu\n", aio_ctx.operations_completed);
printf(" 总取消: %lu\n", aio_ctx.operations_cancelled);

// 模拟运行期间的状态监控
printf("\n4. 模拟运行期间状态监控:\n");

// 显示当前状态
printf(" 当前AIO上下文状态:\n");
printf(" 已初始化: %s\n", aio_ctx.is_initialized ? "是" : "否");
printf(" 已销毁: %s\n", aio_ctx.is_destroyed ? "是" : "否");
printf(" 关闭请求: %s\n", aio_ctx.shutdown_requested ? "是" : "否");
printf(" 运行时间: %ld 秒\n",
(long)difftime(time(NULL), aio_ctx.create_time));

// 模拟健康检查
printf(" 健康检查:\n");
printf(" ✓ 上下文ID有效性检查\n");
printf(" ✓ 内存使用情况检查\n");
printf(" ✓ 操作队列状态检查\n");
printf(" ✓ 错误率统计检查\n");

// 模拟优雅关闭
printf("\n5. 模拟优雅关闭:\n");

// 设置关闭标志
printf(" 设置关闭标志:\n");
aio_ctx.shutdown_requested = 1;
printf(" ✓ 关闭请求已设置\n");

// 等待未完成操作
printf(" 等待未完成操作:\n");
printf(" 当前未完成操作数: %lu\n",
aio_ctx.operations_submitted -
aio_ctx.operations_completed -
aio_ctx.operations_cancelled);

// 取消未完成操作
printf(" 取消未完成操作:\n");
unsigned long pending_operations = aio_ctx.operations_submitted -
aio_ctx.operations_completed -
aio_ctx.operations_cancelled;
printf(" 取消 %lu 个未完成操作\n", pending_operations);
aio_ctx.operations_cancelled += pending_operations;

// 销毁AIO上下文
printf(" 销毁AIO上下文:\n");
if (destroy_production_aio_context(&aio_ctx) == 0) {
printf(" ✓ AIO上下文销毁成功\n");
} else {
printf(" ✗ AIO上下文销毁失败\n");
}

// 显示最终统计
printf("\n6. 最终统计:\n");
printf(" 生命周期统计:\n");
printf(" 创建时间: %s", ctime(&aio_ctx.create_time));
if (aio_ctx.is_destroyed) {
printf(" 销毁时间: %s", ctime(&aio_ctx.destroy_time));
printf(" 运行时间: %ld 秒\n",
(long)difftime(aio_ctx.destroy_time, aio_ctx.create_time));
}

printf(" 操作统计:\n");
printf(" 总提交操作: %lu\n", aio_ctx.operations_submitted);
printf(" 总完成操作: %lu\n", aio_ctx.operations_completed);
printf(" 总取消操作: %lu\n", aio_ctx.operations_cancelled);

// 显示生产环境最佳实践
printf("\n=== 生产环境最佳实践 ===\n");
printf("1. 初始化管理:\n");
printf(" ✓ 延迟初始化\n");
printf(" ✓ 配置验证\n");
printf(" ✓ 资源预留\n");
printf(" ✓ 错误恢复\n");

printf("\n2. 运行时管理:\n");
printf(" ✓ 状态监控\n");
printf(" ✓ 性能统计\n");
printf(" ✓ 错误处理\n");
printf(" ✓ 资源池管理\n");

printf("\n3. 优雅关闭:\n");
printf(" ✓ 信号处理\n");
printf(" ✓ 操作清理\n");
printf(" ✓ 资源释放\n");
printf(" ✓ 状态保存\n");

printf("\n4. 错误恢复:\n");
printf(" ✓ 重试机制\n");
printf(" ✓ 降级处理\n");
printf(" ✓ 告警通知\n");
printf(" ✓ 日志记录\n");

printf("\n5. 监控告警:\n");
printf(" ✓ 性能指标\n");
printf(" ✓ 错误率统计\n");
printf(" ✓ 资源使用率\n");
printf(" ✓ 健康检查\n");

return 0;
}

int main() {
return demo_production_usage_pattern();
}

io_destroy 使用注意事项

系统要求:

内核版本: 支持AIO的Linux内核

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

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

销毁时机:

使用完成后: AIO上下文不再需要时立即销毁

程序退出前: 确保所有上下文都被正确销毁

异常处理: 异常情况下也要销毁上下文

错误处理:

EINVAL: 无效的上下文ID

EAGAIN: 上下文中仍有未完成的操作

EFAULT: 上下文指针无效

资源管理:

及时销毁: 避免资源泄漏

批量销毁: 提高销毁效率

状态检查: 销毁前检查上下文状态

错误恢复: 处理销毁失败的情况

性能考虑:

销毁开销: 销毁操作有一定开销

批量操作: 批量销毁比单独销毁更高效

异步销毁: 大量上下文时考虑异步销毁

资源回收: 及时回收系统资源

安全考虑:

权限验证: 确保有权限销毁上下文

参数验证: 验证上下文ID的有效性

状态检查: 检查上下文使用状态

并发控制: 多线程环境下的同步控制

最佳实践:

RAII原则: 使用完立即销毁

异常安全: 异常情况下也能正确销毁

资源统计: 统计和监控资源使用情况

日志记录: 记录销毁操作日志

错误处理: 妥善处理销毁失败的情况

io_destroy vs 相似函数对比

io_destroy vs close:

1
2
3
4
5
6
// io_destroy: 销毁AIO上下文
io_destroy(ctx);

// close: 关闭文件描述符
close(fd);

io_destroy vs free:

1
2
3
4
5
6
// io_destroy: 销毁内核资源
io_destroy(ctx);

// free: 释放用户空间内存
free(ptr);

常见使用场景

1. 服务器应用:

1
2
3
4
5
6
7
// 服务器退出时销毁所有AIO上下文
void server_shutdown() {
for (int i = 0; i < context_count; i++) {
io_destroy(contexts&#91;i]);
}
}

2. 批处理应用:

1
2
3
4
5
// 批处理完成后销毁AIO上下文
void batch_cleanup() {
batch_destroy_aio_contexts(&manager);
}

3. 插件系统:

1
2
3
4
5
// 插件卸载时销毁相关AIO上下文
void plugin_unload() {
destroy_plugin_aio_contexts();
}

系统限制和约束

1. 上下文数量限制:

1
2
3
4
// 系统限制可通过以下方式查看:
cat /proc/sys/fs/aio-max-nr # 最大AIO上下文数
cat /proc/sys/fs/aio-nr # 当前AIO上下文数

2. 内存限制:

1
2
3
// 每个AIO上下文都有内存开销
// 需要合理控制上下文数量

3. 文件描述符限制:

1
2
3
// AIO操作涉及的文件描述符也需要管理
// 避免文件描述符泄漏

错误恢复策略

1. 重试机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int safe_io_destroy(aio_context_t ctx) {
int retries = 3;
int result;

while (retries-- > 0) {
result = io_destroy(ctx);
if (result == 0) {
return 0; // 成功
}

if (result == -EAGAIN) {
// 等待未完成操作
usleep(100000); // 100ms
continue;
}

break; // 其他错误不重试
}

return result;
}

2. 状态检查:

1
2
3
4
5
6
7
8
9
10
11
12
int validate_context_before_destroy(aio_context_t ctx) {
// 检查上下文是否有效
if (ctx == 0) {
return -1; // 无效上下文
}

// 检查上下文是否已被销毁
// 这需要应用层维护状态

return 0;
}

3. 资源清理:

1
2
3
4
5
6
7
8
9
10
11
int comprehensive_cleanup(aio_context_t ctx) {
// 取消所有未完成操作
cancel_pending_operations(ctx);

// 等待操作完成
wait_for_operations_completion(ctx);

// 销毁上下文
return io_destroy(ctx);
}

总结

io_destroy 是Linux AIO框架中重要的资源清理函数,提供了:

资源回收: 释放AIO上下文相关的内核资源

内存管理: 回收环形缓冲区和其他数据结构

状态清理: 清理上下文相关的等待队列和状态

安全保障: 确保系统资源得到正确回收

通过合理使用 io_destroy,可以构建健壮的异步I/O应用。在实际应用中,需要注意资源管理、错误处理和性能优化等关键问题。特别是在生产环境中,需要实现完善的错误恢复和优雅关闭机制,确保系统的稳定性和可靠性。