utimes系统调用及示例

好的,我们来深入学习 utimes 系统调用

1. 函数介绍

在 Linux 系统中,每个文件都关联着一些重要的时间属性:

访问时间 (atime): 文件上一次被读取的时间。

修改时间 (mtime): 文件内容上一次被修改的时间。

状态改变时间 (ctime): 文件的元数据(如权限、所有者、链接数等)上一次被改变的时间。

这些时间戳对于系统管理、备份策略、审计日志等非常重要。

utimes (Update Times) 系统调用的作用就是手动设置一个文件的访问时间 (atime) 和 修改时间 (mtime)。

简单来说,utimes 就是让你用程序来“篡改”一个文件的“上次访问时间”和“上次修改时间”。

你可能会问,为什么要手动修改这些时间呢?常见的场景有:

  • 文件同步工具:在同步文件时,可能需要确保目标文件的时间戳与源文件完全一致。

  • 备份和归档:某些备份工具可能需要调整文件时间戳以匹配备份时的状态。

  • 测试:编写测试程序时,可能需要模拟文件在特定时间点被访问或修改。

  • 修复:如果因为某些原因文件的时间戳不正确,可以手动修正。

2. 函数原型

1
2
3
4
#include <sys/time.h> // 包含 utimes 函数声明和 timeval 结构体

int utimes(const char *filename, const struct timeval times&#91;2]);

3. 功能

设置由 filename 指定的文件的访问时间 (atime) 和修改时间 (mtime)。

4. 参数

filename:

  • const char * 类型。

  • 指向一个以 null 结尾的字符串,表示要修改时间戳的文件的路径名。

times:

  • const struct timeval times[2] 类型。

  • 一个包含两个 struct timeval 元素的数组。

  • times[0] 指定了新的访问时间 (atime)。

  • times[1] 指定了新的修改时间 (mtime)。

  • 如果 times 指针为 NULL,则 utimes 会将 atime 和 mtime 都设置为当前时间。

struct timeval 结构体:

1
2
3
4
5
struct timeval {
time_t tv_sec; /* 秒数 (自 Unix 纪元以来) */
suseconds_t tv_usec; /* 微秒数 (0-999999) */
};

5. 返回值

  • 成功: 返回 0。

  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。

6. 错误码 (errno)

  • EACCES: 搜索 filename 的路径组件时权限不足,或者没有写权限(因为修改时间戳通常需要写权限)。

  • EFAULT: filename 或 times 指向了调用进程无法访问的内存地址。

  • EINVAL: times 数组中的时间值无效(例如,微秒数超出范围)。

  • EIO: I/O 错误。

  • ELOOP: 解析 filename 时遇到符号链接循环。

  • ENAMETOOLONG: filename 太长。

  • ENOENT: filename 指定的文件或目录不存在。

  • ENOMEM: 内核内存不足。

  • ENOTDIR: filename 的某个前缀不是目录。

  • EPERM: 系统调用被阻止(例如,由 seccomp 或安全模块)。

  • EROFS: filename 所在的文件系统是只读的。

7. 相似函数或关联函数

  • utime: 一个更老的、功能类似的函数。它使用 struct utimbuf,精度只到秒。utimes 是 utime 的微秒精度版本。#include <utime.h> struct utimbuf { time_t actime; /* 访问时间 / time_t modtime; / 修改时间 */ }; int utime(const char *filename, const struct utimbuf *times);

  • lutimes: 与 utimes 类似,但如果 filename 是一个符号链接,它会修改符号链接本身的 atime 和 mtime,而不是它指向的目标文件。

  • futimes: 与 utimes 类似,但它通过已打开的文件描述符 (fd) 来指定文件,而不是文件路径。int futimes(int fd, const struct timeval tv[2]);

  • futimens / utimensat: 更现代的系统调用,使用 struct timespec,提供纳秒级精度,并且有更多选项(如 UTIME_OMIT, UTIME_NOW)。这些是推荐在新代码中使用的。#include <fcntl.h> // 包含 AT_FDCWD 等 #include <sys/stat.h> // 包含 timespec, UTIME_* 常量 int futimens(int fd, const struct timespec times[2]); int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);

8. 示例代码

下面的示例演示了如何使用 utimes 来修改文件的时间戳,并与其他相关函数进行比较。

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
#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h> // 包含 utimes, timeval
#include <sys/stat.h> // 包含 stat, timespec
#include <utime.h> // 包含 utime, utimbuf
#include <fcntl.h> // 包含 open, O_* flags
#include <string.h>
#include <errno.h>
#include <time.h> // 包含 time, localtime, strftime

// 辅助函数:打印文件的时间信息
void print_file_times(const char *filename) {
struct stat sb;
if (stat(filename, &sb) == -1) {
perror("stat");
return;
}

printf("File: %s\n", filename);
// 使用 ctime 将 time_t 转换为可读字符串
printf(" Last Status Change (ctime): %s", ctime(&sb.st_ctime)); // ctime 包含换行符
printf(" Last Modification (mtime): %s", ctime(&sb.st_mtime));
printf(" Last Access (atime): %s", ctime(&sb.st_atime));
printf("\n");
}

// 辅助函数:创建一个测试文件
void create_test_file(const char *filename) {
FILE *f = fopen(filename, "w");
if (!f) {
perror("fopen");
exit(EXIT_FAILURE);
}
fprintf(f, "This is a test file for utimes example.\n");
fclose(f);
printf("Created test file: %s\n\n", filename);
}

int main() {
const char *test_file = "utimes_test_file.txt";
struct timeval new_times&#91;2];
time_t fixed_time_sec;
struct tm tm_tmp;

printf("--- Demonstrating utimes ---\n");

// 1. 创建一个测试文件
create_test_file(test_file);

// 2. 显示初始时间
printf("1. Initial timestamps:\n");
print_file_times(test_file);

// 3. 使用 utimes 将 atime 和 mtime 设置为固定时间
printf("2. Setting timestamps to a fixed time using utimes()...\n");
// 设置一个固定的日期时间:例如 2023-10-27 10:00:00 UTC
memset(&tm_tmp, 0, sizeof(tm_tmp));
tm_tmp.tm_year = 2023 - 1900; // tm_year is years since 1900
tm_tmp.tm_mon = 10 - 1; // tm_mon is 0-11
tm_tmp.tm_mday = 27;
tm_tmp.tm_hour = 10;
tm_tmp.tm_min = 0;
tm_tmp.tm_sec = 0;
tm_tmp.tm_isdst = 0; // Not daylight saving time
fixed_time_sec = timegm(&tm_tmp); // Convert to time_t (UTC)
if (fixed_time_sec == -1) {
perror("timegm");
unlink(test_file);
exit(EXIT_FAILURE);
}

new_times&#91;0].tv_sec = fixed_time_sec; // atime
new_times&#91;0].tv_usec = 123456; // atime 微秒
new_times&#91;1].tv_sec = fixed_time_sec; // mtime
new_times&#91;1].tv_usec = 654321; // mtime 微秒

if (utimes(test_file, new_times) == -1) {
perror("utimes");
unlink(test_file);
exit(EXIT_FAILURE);
}
printf("Set atime to %s", ctime(&fixed_time_sec)); // ctime adds newline
printf("Set mtime to %s", ctime(&fixed_time_sec));
printf("Note: Microseconds are set but not displayed by ctime.\n\n");

printf("3. Timestamps after utimes (fixed time):\n");
print_file_times(test_file);

// 4. 使用 utimes(NULL) 将时间设置为当前时间
printf("4. Setting timestamps to CURRENT time using utimes(NULL)...\n");
sleep(3); // 等待几秒,确保当前时间不同
if (utimes(test_file, NULL) == -1) {
perror("utimes(NULL)");
unlink(test_file);
exit(EXIT_FAILURE);
}
printf("Timestamps updated to current time.\n\n");

printf("5. Timestamps after utimes(NULL):\n");
print_file_times(test_file);

// 6. 比较 utime (旧版,秒级精度)
printf("6. --- Comparing with older utime() ---\n");
struct utimbuf old_times;
old_times.actime = fixed_time_sec; // 访问时间
old_times.modtime = fixed_time_sec; // 修改时间
printf("Setting timestamps back to fixed time using utime() (second precision)...\n");
if (utime(test_file, &old_times) == -1) {
perror("utime");
} else {
printf("utime() succeeded.\n");
print_file_times(test_file);
printf("Note: atime and mtime are now at second precision.\n\n");
}

// 7. 比较 futimes (通过文件描述符)
printf("7. --- Comparing with futimes() ---\n");
int fd = open(test_file, O_RDONLY);
if (fd == -1) {
perror("open");
} else {
new_times&#91;0].tv_sec = fixed_time_sec + 3600; // atime + 1 小时
new_times&#91;0].tv_usec = 0;
new_times&#91;1].tv_sec = fixed_time_sec + 7200; // mtime + 2 小时
new_times&#91;1].tv_usec = 0;
printf("Setting timestamps using futimes() via file descriptor...\n");
if (futimes(fd, new_times) == -1) {
perror("futimes");
} else {
printf("futimes() succeeded.\n");
print_file_times(test_file);
}
close(fd);
}

// 8. 清理
printf("8. --- Cleaning up ---\n");
if (unlink(test_file) == 0) {
printf("Deleted test file '%s'.\n", test_file);
} else {
perror("unlink");
}

printf("\n--- Summary ---\n");
printf("1. utimes(filename, times&#91;2]): Sets atime and mtime for a file via its path.\n");
printf("2. Precision is up to microseconds (tv_usec).\n");
printf("3. If times is NULL, both atime and mtime are set to the current time.\n");
printf("4. Related functions:\n");
printf(" - utime(): Older, second-precision version.\n");
printf(" - lutimes(): Modifies symlink itself, not target.\n");
printf(" - futimes(): Modifies file via file descriptor.\n");
printf(" - futimens() / utimensat(): Modern, nanosecond-precision, more flexible (recommended).\n");

return 0;
}

9. 编译和运行

1
2
3
4
5
6
# 假设代码保存在 utimes_example.c 中
gcc -o utimes_example utimes_example.c

# 运行程序
./utimes_example

10. 预期输出

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
--- Demonstrating utimes ---
Created test file: utimes_test_file.txt

1. Initial timestamps:
File: utimes_test_file.txt
Last Status Change (ctime): Fri Oct 27 11:00:00 2023
Last Modification (mtime): Fri Oct 27 11:00:00 2023
Last Access (atime): Fri Oct 27 11:00:00 2023

2. Setting timestamps to a fixed time using utimes()...
Set atime to Fri Oct 27 10:00:00 2023
Set mtime to Fri Oct 27 10:00:00 2023
Note: Microseconds are set but not displayed by ctime.

3. Timestamps after utimes (fixed time):
File: utimes_test_file.txt
Last Status Change (ctime): Fri Oct 27 11:00:00 2023
Last Modification (mtime): Fri Oct 27 10:00:00 2023
Last Access (atime): Fri Oct 27 10:00:00 2023

4. Setting timestamps to CURRENT time using utimes(NULL)...
Timestamps updated to current time.

5. Timestamps after utimes(NULL):
File: utimes_test_file.txt
Last Status Change (ctime): Fri Oct 27 11:00:03 2023
Last Modification (mtime): Fri Oct 27 11:00:03 2023
Last Access (atime): Fri Oct 27 11:00:03 2023

6. --- Comparing with older utime() ---
Setting timestamps back to fixed time using utime() (second precision)...
utime() succeeded.
File: utimes_test_file.txt
Last Status Change (ctime): Fri Oct 27 11:00:03 2023
Last Modification (mtime): Fri Oct 27 10:00:00 2023
Last Access (atime): Fri Oct 27 10:00:00 2023
Note: atime and mtime are now at second precision.

7. --- Comparing with futimes() ---
Setting timestamps using futimes() via file descriptor...
futimes() succeeded.
File: utimes_test_file.txt
Last Status Change (ctime): Fri Oct 27 11:00:03 2023
Last Modification (mtime): Fri Oct 27 12:00:00 2023
Last Access (atime): Fri Oct 27 11:00:00 2023

8. --- Cleaning up ---
Deleted test file 'utimes_test_file.txt'.

--- Summary ---
1. utimes(filename, times&#91;2]): Sets atime and mtime for a file via its path.
2. Precision is up to microseconds (tv_usec).
3. If times is NULL, both atime and mtime are set to the current time.
4. Related functions:
- utime(): Older, second-precision version.
- lutimes(): Modifies symlink itself, not target.
- futimes(): Modifies file via file descriptor.
- futimens() / utimensat(): Modern, nanosecond-precision, more flexible (recommended).

11. 总结

utimes 是一个用于修改文件访问时间和修改时间的系统调用。

  • 核心功能:精确设置文件的 atime 和 mtime(微秒级)。

  • 参数:文件路径和包含两个 timeval 结构的数组。

  • 特殊用法:传入 NULL 可将时间设置为当前时间。

相关函数:

  • utime: 更老的秒级精度版本。

  • lutimes: 修改符号链接本身的时间。

  • futimes: 通过文件描述符修改时间。

  • futimens / utimensat: 现代推荐的函数,提供纳秒精度和更多控制选项。

使用场景:文件同步、备份、测试、时间戳修复。

给 Linux 编程小白的建议:虽然 utimes 很有用,但在编写新代码时,考虑使用更新、更强大的 utimensat 或 futimens,因为它们提供了更好的精度和灵活性。

vfork系统调用及示例

在 fork 之后,根据您提供的列表,下一个函数是 vfork。

1. 函数介绍

vfork 是一个历史悠久的 Linux/Unix 系统调用,它的设计目的是为了优化 fork 在特定场景下的性能。vfork 的行为与 fork 非常相似,但也存在关键的区别。

核心思想:

当一个进程调用 fork 后,最常见的操作是在子进程中立即调用 exec 系列函数来执行一个全新的程序。在标准的 fork 实现中,内核会完整地复制父进程的地址空间(页表、内存页等)给子进程。但是,如果子进程紧接着就调用 exec,这些刚复制的内存很快就会被新程序的内存镜像完全覆盖,那么这次复制操作就是浪费的。

vfork 就是为了解决这个“先 fork 再 exec”的常见模式下的性能浪费问题。

你可以把 vfork 想象成借用自己的身体来打电话:

  • 你(父进程)需要让别人(子进程)去隔壁房间打一个重要的电话(exec)。

  • 用 fork 就像你复制了一个自己的身体(克隆人),然后让克隆人去隔壁打电话。但克隆人刚出门,你就把他的身体销毁了,因为用完就没了。

  • 用 vfork 就像你暂时把自己的身体借给那个人,让他去隔壁打电话。在打电话的这段时间(从 vfork 返回到 exec 或 _exit 被调用),你(父进程)必须一动不动地等着,因为你把身体借出去了。

  • 一旦那个人打完电话(调用 exec 或 _exit),你的身体就回来了,你可以继续做自己的事。

2. 函数原型

1
2
3
4
#include <unistd.h> // 必需

pid_t vfork(void);

3. 功能

  • 创建新进程: 与 fork 类似,vfork 也用于创建一个新的子进程。

  • 共享地址空间: 与 fork 不同,vfork 创建的子进程暂时与父进程共享相同的地址空间(内存、栈等)。这意味着子进程对内存的任何修改在父进程中都是可见的。

  • 挂起父进程: 调用 vfork 后,父进程会被挂起(暂停执行),直到子进程调用 exec 系列函数或 _exit 为止。

  • 子进程限制: 在子进程中,从 vfork 返回到调用 exec 或 _exit 之间,只能执行这两个操作或修改局部变量后直接返回。执行任何其他操作(如修改全局变量、调用可能修改内存的库函数、返回到 vfork 调用之前的函数栈帧)都可能导致未定义行为。

4. 参数

  • void: vfork 函数不接受任何参数。

5. 返回值

vfork 的返回值语义与 fork 完全相同:

在父进程中:

  • 成功: 返回新创建**子进程的进程 ID **(PID)。

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

在子进程中:

  • 成功: 返回 0。

**失败时 **(父进程)

  • 返回 -1,且没有子进程被创建。

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

  • fork: vfork 的“兄弟”。vfork 是 fork 的一个变种,旨在优化“fork-then-exec”模式。

  • clone: Linux 特有的底层系统调用。vfork 在底层可以通过特定的 clone 标志 (CLONE_VFORK | CLONE_VM) 来实现。

  • exec 系列函数: vfork 通常与 exec 系列函数结合使用。

  • _exit: 子进程在 vfork 后通常调用 _exit 来终止,而不是 exit。

7. 示例代码

示例 1:基本的 vfork + exec 使用

这个例子演示了 vfork 最经典和推荐的用法:创建子进程并立即执行新程序。

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
// vfork_exec.c
#include <unistd.h> // vfork, _exit
#include <sys/wait.h> // wait
#include <stdio.h> // printf, perror
#include <stdlib.h> // exit

int global_var = 100; // 全局变量,用于演示共享地址空间

int main() {
pid_t pid;
int local_var = 200; // 局部变量

printf("Before vfork: Parent PID: %d\n", getpid());
printf(" Global var: %d, Local var: %d\n", global_var, local_var);

// --- 关键: 调用 vfork ---
pid = vfork();

if (pid == -1) {
// vfork 失败
perror("vfork failed");
exit(EXIT_FAILURE);

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

// 在 vfork 的子进程中,修改局部变量通常是安全的
// (只要不返回到 vfork 之前的栈帧)
local_var = 250;
printf(" Child modified local var to: %d\n", local_var);

// 修改全局变量也是可以的,但这会影响父进程看到的值
// 这只是为了演示共享内存,实际使用中要非常小心
global_var = 150;
printf(" Child modified global var to: %d\n", global_var);

// --- 关键: 子进程必须立即调用 exec 或 _exit ---
printf(" Child is about to exec 'echo'.\n");

// 准备 execv 所需的参数
char *args&#91;] = { "echo", "Hello from exec'd process!", NULL };

// 调用 execv 执行新的程序
execv("/bin/echo", args);

// --- 如果代码执行到这里,说明 execv 失败了 ---
perror("execv failed in child");
// 在 vfork 的子进程中,失败时必须使用 _exit,而不是 exit
_exit(EXIT_FAILURE);

} else {
// --- 父进程 ---
// vfork 会挂起父进程,直到子进程调用 exec 或 _exit
printf("Parent process (PID: %d) resumed after child's exec.\n", getpid());

// 父进程现在可以安全地访问自己的变量了
// 注意:由于子进程修改了 global_var,在 exec 之前,
// 父进程看到的 global_var 值可能已经被改变了
// (但这依赖于具体实现和时机,不应依赖此行为)
printf(" Parent sees global var as: %d (may be modified by child)\n", global_var);
printf(" Parent sees local var as: %d (should be unchanged)\n", local_var);

// 等待子进程结束 (子进程 exec 后变成了新的程序,最终会退出)
int status;
if (wait(&status) == -1) {
perror("wait failed");
exit(EXIT_FAILURE);
}

if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("Parent: Child (new process) exited with status %d.\n", exit_code);
} else {
printf("Parent: Child (new process) did not exit normally.\n");
}

printf("Parent process (PID: %d) finished.\n", getpid());
}

return 0;
}

代码解释:

定义了一个全局变量 global_var 和一个局部变量 local_var。

调用 pid = vfork();。

在子进程中 (pid == 0):

  • 打印信息。

  • 修改局部变量 local_var(这通常被认为是安全的)。

  • 修改全局变量 global_var(这是为了演示地址空间共享,但实际编程中非常危险且不推荐)。

  • 关键: 准备 execv 的参数并立即调用 execv(“/bin/echo”, args)。

  • 如果 execv 失败,调用 _exit(EXIT_FAILURE) 退出。强调: 在 vfork 子进程中,失败时必须使用 _exit,而不是 exit。

在父进程中 (pid > 0):

  • 程序执行到这里时,意味着子进程已经调用了 exec 或 _exit,父进程被恢复执行。

打印信息,并检查变量的值。

  • local_var 应该没有变化。

  • global_var 的值是不确定的,因为子进程可能修改了它。这展示了共享地址空间的风险。

调用 wait 等待子进程(现在是 echo 程序)结束。

打印子进程的退出状态。

父进程结束。

示例 2:演示 vfork 子进程中的危险操作

这个例子(仅供演示,请勿模仿!)展示了在 vfork 子进程中执行不当操作可能导致的问题。

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
// vfork_danger.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int global_counter = 0;

// 一个简单的函数
void dangerous_function() {
global_counter++; // 修改全局变量
printf("Dangerous function called, global_counter: %d\n", global_counter);
// 如果这个函数还调用了其他库函数,或者有复杂的返回路径,
// 在 vfork 子进程中调用它会非常危险。
}

int main() {
pid_t pid;

printf("Parent PID: %d, Global counter: %d\n", getpid(), global_counter);

pid = vfork();

if (pid == -1) {
perror("vfork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// --- vfork 子进程 ---
printf("Child PID: %d\n", getpid());

// --- 危险操作 1: 调用非 async-signal-safe 的函数 ---
// printf 通常被认为是安全的,但更复杂的函数可能不是。
// dangerous_function(); // 取消注释这行可能会导致问题

// --- 危险操作 2: 从 vfork 子进程中返回 ---
// return 0; // 这是非常危险的!绝对不要这样做!
// 从 vfork 子进程中返回会导致返回到父进程的栈帧,
// 而父进程的栈可能已经被子进程修改或破坏。

// --- 危险操作 3: 修改复杂的数据结构 ---
// 任何涉及 malloc/free, stdio buffers, 等的操作都可能不安全。

// 正确的做法:只调用 exec 或 _exit
// 为了演示,我们在这里直接 _exit
printf("Child is exiting via _exit().\n");
_exit(EXIT_SUCCESS);

} else {
// --- 父进程 ---
// 父进程在这里恢复
printf("Parent PID: %d resumed. Global counter: %d\n", getpid(), global_counter);

// 简单等待,实际程序中应使用 wait
sleep(1);
printf("Parent finished.\n");
}

return 0;
}

代码解释:

该示例旨在说明在 vfork 子进程中的限制。

dangerous_function 是一个示例函数,它修改全局变量并调用 printf。

代码注释中指出了几种在 vfork 子进程中不应该做的事情:

  • 调用复杂的库函数。

  • 从子进程函数中返回(return)。

  • 修改复杂的数据结构。

强调了在 vfork 子进程中只应执行 exec 或 _exit。

重要提示与注意事项:

已过时/不推荐: 在现代 Linux 编程中,vfork通常被认为是过时的,并且不推荐使用。原因如下:

  • 复杂且易出错: 子进程的行为受到严格限制,很容易因违反规则而导致程序崩溃或数据损坏。

  • 优化不再显著: 现代操作系统的 fork 实现(利用写时复制 Copy-On-Write, COW 技术)已经非常高效。当 fork 后立即 exec 时,内核几乎不需要复制任何实际的物理内存页,因为 exec 会立即替换整个地址空间。因此,vfork 带来的性能提升非常有限。

  • 更安全的替代方案: posix_spawn() 是一个更现代、更安全、更可移植的创建并执行新进程的方式,它旨在提供 fork + exec 的功能,同时避免 vfork 的陷阱。

vfork vs fork + COW:

  • 传统的 fork 确实会复制页表。

  • 但是现代的 fork 实现使用写时复制(COW)。这意味着 fork 调用本身很快,因为它只复制页表,而物理内存页在父子进程之间是共享的。只有当任一进程尝试修改某页时,内核才会复制该页。如果子进程紧接着调用 exec,那么大部分(甚至全部)页面都无需复制。

  • 因此,vfork 的性能优势在现代系统上已经大大减弱。

严格的使用规则: 如果你必须使用 vfork(例如,为了兼容非常老的系统或特殊需求),必须严格遵守其规则:

  • 子进程只能调用 exec 或 _exit。

  • 子进程不能修改除局部变量外的任何数据。

  • 子进程不能返回到 vfork 调用之前的任何函数栈帧。

  • 子进程不能调用任何非异步信号安全(async-signal-safe)的函数(除了 exec 和 _exit)。

_exit vs exit: 与 fork 子进程一样,在 vfork 子进程中,如果需要终止,应使用 _exit() 而不是 exit()。

可移植性: vfork 不是 POSIX 标准的一部分,尽管在很多类 Unix 系统上都可用。fork 和 posix_spawn 具有更好的可移植性。

总结:

vfork 是一个为特定场景(fork 后立即 exec)优化的系统调用。它通过让父子进程共享地址空间并挂起父进程来避免内存复制的开销。然而,由于其使用规则极其严格且容易出错,加上现代 fork 实现(COW)已经非常高效,vfork 在现代编程实践中已基本被弃用。推荐使用 fork + exec 或更现代的 posix_spawn 来创建和执行新进程。理解 vfork 的原理和历史意义仍然重要,但应避免在新代码中使用它。

vmsplice系统调用及示例

vmsplice函数详解

  1. 函数介绍

vmsplice函数是Linux系统中用于高效地将用户空间缓冲区数据传输到管道的函数。它是splice系列函数的一部分,专门用于从用户空间向管道传输数据。可以把vmsplice想象成一个”高速数据管道注入器”,它能够将内存中的数据块直接传输到管道中,而无需传统的数据拷贝操作。

vmsplice的主要优势在于它提供了零拷贝或最小拷贝的数据传输机制,通过将用户空间的内存页直接映射到内核空间,大大提高了数据传输的效率。这在需要大量数据传输的高性能应用中特别有用。

使用场景:

  • 高性能网络服务器的数据传输

  • 大数据处理和流处理应用

  • 实时系统的数据管道

  • 避免内存拷贝的高效数据传输

  • 管道和套接字之间的数据传输

  1. 函数原型
1
2
3
4
5
6
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/uio.h>

ssize_t vmsplice(int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags);

  1. 功能

vmsplice函数的主要功能是将用户空间缓冲区的数据高效地传输到管道中。它支持多种传输模式和选项,可以优化不同场景下的数据传输性能。

  1. 参数

fd: 管道文件描述符

  • 类型:int

  • 含义:目标管道的写端文件描述符

iov: 输入输出向量数组

  • 类型:const struct iovec*

  • 含义:描述要传输的数据缓冲区的向量数组

  • 结构体定义:struct iovec { void *iov_base; // 缓冲区起始地址 size_t iov_len; // 缓冲区长度 };

nr_segs: 向量数组元素个数

  • 类型:unsigned long

  • 含义:iov数组中有效元素的个数

flags: 操作标志

  • 类型:unsigned int

  • 含义:控制传输行为的标志位

常用值:

  • SPLICE_F_MOVE:尽可能移动页面而不是复制

  • SPLICE_F_NONBLOCK:非阻塞操作

  • SPLICE_F_MORE:提示还有更多数据要写入

  • SPLICE_F_GIFT:页面是礼品(传输后内核拥有页面)

  1. 返回值
  • 成功: 返回实际传输的字节数

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

  • EAGAIN:非阻塞模式下无法立即完成

  • EBADF:无效的文件描述符

  • EINVAL:参数无效

  • ENOMEM:内存不足

  • EPIPE:管道已关闭读端

  1. 相似函数或关联函数
  • splice(): 在文件描述符之间传输数据

  • tee(): 在管道之间复制数据

  • write(): 传统的数据写入函数

  • writev(): 向量写入函数

  • mmap(): 内存映射函数

  • sendfile(): 文件到套接字的高效传输

  1. 示例代码

示例1:基础vmsplice使用 - 简单数据传输

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

// 创建管道
int create_pipe(int pipefd&#91;2]) {
if (pipe(pipefd) == -1) {
perror("创建管道失败");
return -1;
}
printf("创建管道: 读端=%d, 写端=%d\n", pipefd&#91;0], pipefd&#91;1]);
return 0;
}

// 使用vmsplice传输数据
ssize_t send_data_with_vmsplice(int pipe_write_fd, const char* data, size_t data_len) {
struct iovec iov;
iov.iov_base = (void*)data;
iov.iov_len = data_len;

ssize_t result = vmsplice(pipe_write_fd, &iov, 1, SPLICE_F_MOVE);
if (result == -1) {
perror("vmsplice传输失败");
} else {
printf("vmsplice传输成功: %zd 字节\n", result);
}

return result;
}

// 从管道接收数据
ssize_t receive_data_from_pipe(int pipe_read_fd, char* buffer, size_t buffer_size) {
ssize_t bytes_read = read(pipe_read_fd, buffer, buffer_size - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf("从管道读取: %zd 字节\n", bytes_read);
} else if (bytes_read == -1) {
perror("从管道读取失败");
}

return bytes_read;
}

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

int pipefd&#91;2];

// 创建管道
if (create_pipe(pipefd) == -1) {
exit(EXIT_FAILURE);
}

// 准备测试数据
const char* test_data = "这是使用vmsplice传输的测试数据";
size_t data_len = strlen(test_data);

printf("1. 准备传输数据:\n");
printf(" 数据内容: %s\n", test_data);
printf(" 数据长度: %zu 字节\n", data_len);

// 使用vmsplice传输数据
printf("\n2. 使用vmsplice传输数据:\n");
ssize_t bytes_sent = send_data_with_vmsplice(pipefd&#91;1], test_data, data_len);

if (bytes_sent > 0) {
// 从管道接收数据
printf("\n3. 从管道接收数据:\n");
char receive_buffer&#91;256];
ssize_t bytes_received = receive_data_from_pipe(pipefd&#91;0], receive_buffer, sizeof(receive_buffer));

if (bytes_received > 0) {
printf(" 接收内容: %s\n", receive_buffer);
printf(" 验证结果: %s\n",
strcmp(test_data, receive_buffer) == 0 ? "✓ 数据一致" : "✗ 数据不一致");
}
}

// 演示向量传输
printf("\n4. 演示向量传输:\n");

// 准备多个数据段
const char* segments&#91;] = {
"第一段数据",
"第二段数据",
"第三段数据"
};

size_t segment_count = sizeof(segments) / sizeof(segments&#91;0]);
struct iovec iov&#91;segment_count];

// 初始化向量数组
for (size_t i = 0; i < segment_count; i++) {
iov&#91;i].iov_base = (void*)segments&#91;i];
iov&#91;i].iov_len = strlen(segments&#91;i]);
printf(" 段 %zu: %s (%zu 字节)\n", i + 1, segments&#91;i], strlen(segments&#91;i]));
}

// 使用vmsplice传输多个段
ssize_t vector_result = vmsplice(pipefd&#91;1], iov, segment_count, SPLICE_F_MOVE);
if (vector_result == -1) {
perror("向量传输失败");
} else {
printf(" 向量传输成功: %zd 字节\n", vector_result);

// 接收并验证数据
char vector_buffer&#91;256];
ssize_t total_received = 0;
printf(" 接收数据:\n");

while (total_received < vector_result) {
ssize_t bytes_read = read(pipefd&#91;0], vector_buffer + total_received,
sizeof(vector_buffer) - total_received - 1);
if (bytes_read > 0) {
total_received += bytes_read;
} else if (bytes_read == 0) {
break; // 管道已关闭
} else {
perror("读取数据失败");
break;
}
}

if (total_received > 0) {
vector_buffer&#91;total_received] = '\0';
printf(" 接收内容: %s\n", vector_buffer);
}
}

// 演示错误处理
printf("\n5. 错误处理演示:\n");

// 使用无效的文件描述符
struct iovec invalid_iov = {(void*)"test", 4};
ssize_t error_result = vmsplice(999, &invalid_iov, 1, 0);
if (error_result == -1) {
printf(" 使用无效fd: %s (预期错误)\n", strerror(errno));
}

// 使用空向量
error_result = vmsplice(pipefd&#91;1], NULL, 0, 0);
if (error_result == -1) {
printf(" 使用空向量: %s (预期错误)\n", strerror(errno));
}

// 清理资源
printf("\n6. 清理资源:\n");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
printf(" 管道已关闭\n");

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

#define BUFFER_SIZE 4096
#define NUM_BUFFERS 100

// 生产者进程 - 使用vmsplice发送数据
void producer_process(int pipe_write_fd) {
printf("生产者进程启动 (PID: %d)\n", getpid());

// 准备数据缓冲区
char** buffers = malloc(NUM_BUFFERS * sizeof(char*));
if (!buffers) {
perror("分配缓冲区失败");
exit(EXIT_FAILURE);
}

// 初始化缓冲区
for (int i = 0; i < NUM_BUFFERS; i++) {
buffers&#91;i] = malloc(BUFFER_SIZE);
if (!buffers&#91;i]) {
perror("分配缓冲区失败");
exit(EXIT_FAILURE);
}

// 填充缓冲区内容
snprintf(buffers&#91;i], BUFFER_SIZE,
"生产者数据包 %d - 时间戳: %ld", i + 1, time(NULL));
}

struct iovec iov;
int total_sent = 0;

// 发送数据包
for (int i = 0; i < NUM_BUFFERS; i++) {
iov.iov_base = buffers&#91;i];
iov.iov_len = strlen(buffers&#91;i]) + 1; // 包括字符串结束符

// 使用vmsplice发送数据
ssize_t bytes_sent = vmsplice(pipe_write_fd, &iov, 1,
SPLICE_F_MOVE | SPLICE_F_MORE);

if (bytes_sent == -1) {
if (errno == EAGAIN) {
printf("生产者: 管道满,等待...\n");
usleep(1000); // 等待1毫秒后重试
i--; // 重试当前数据包
continue;
} else {
perror("生产者: vmsplice发送失败");
break;
}
} else {
total_sent += bytes_sent;
if ((i + 1) % 20 == 0) {
printf("生产者: 已发送 %d 个数据包 (%d 字节)\n", i + 1, total_sent);
}
}

// 模拟处理时间
if (i % 10 == 0) {
usleep(10000); // 10毫秒
}
}

printf("生产者: 总共发送 %d 字节\n", total_sent);

// 清理缓冲区
for (int i = 0; i < NUM_BUFFERS; i++) {
free(buffers&#91;i]);
}
free(buffers);

printf("生产者进程完成\n");
}

// 消费者进程 - 从管道接收数据
void consumer_process(int pipe_read_fd) {
printf("消费者进程启动 (PID: %d)\n", getpid());

char buffer&#91;BUFFER_SIZE];
int total_received = 0;
int packet_count = 0;

clock_t start_time = clock();

// 接收数据
while (packet_count < NUM_BUFFERS) {
ssize_t bytes_received = read(pipe_read_fd, buffer, sizeof(buffer) - 1);

if (bytes_received > 0) {
buffer&#91;bytes_received] = '\0';
total_received += bytes_received;
packet_count++;

if (packet_count % 20 == 0) {
printf("消费者: 已接收 %d 个数据包 (%d 字节)\n",
packet_count, total_received);
}
} else if (bytes_received == 0) {
printf("消费者: 管道已关闭\n");
break;
} else {
if (errno == EAGAIN) {
usleep(1000); // 等待1毫秒后重试
continue;
} else {
perror("消费者: 读取数据失败");
break;
}
}
}

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

printf("消费者: 总共接收 %d 个数据包, %d 字节\n", packet_count, total_received);
printf("消费者: 耗时 %.3f 秒, 吞吐量 %.2f KB/s\n",
elapsed_time, (total_received / 1024.0) / elapsed_time);

printf("消费者进程完成\n");
}

// 性能测试函数
void performance_test() {
printf("=== 性能测试 ===\n");

int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
return;
}

// 设置管道为非阻塞模式
int flags = fcntl(pipefd&#91;1], F_GETFL);
fcntl(pipefd&#91;1], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(pipefd&#91;0], F_GETFL);
fcntl(pipefd&#91;0], F_SETFL, flags | O_NONBLOCK);

// 准备大量测试数据
char* large_data = malloc(1024 * 1024); // 1MB数据
if (!large_data) {
perror("分配测试数据失败");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
return;
}

memset(large_data, 'A', 1024 * 1024);
large_data&#91;1024 * 1024 - 1] = '\0';

// 测试vmsplice性能
clock_t start_time = clock();

struct iovec iov;
iov.iov_base = large_data;
iov.iov_len = 1024 * 1024;

ssize_t bytes_sent = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_MOVE);

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

if (bytes_sent > 0) {
printf("vmsplice传输 %zd 字节\n", bytes_sent);
printf("耗时: %.6f 秒\n", elapsed_time);
printf("吞吐量: %.2f MB/s\n", (bytes_sent / (1024.0 * 1024.0)) / elapsed_time);
} else {
printf("vmsplice传输失败: %s\n", strerror(errno));
}

// 测试传统write性能(对比)
lseek(pipefd&#91;0], 0, SEEK_SET); // 清空管道
lseek(pipefd&#91;1], 0, SEEK_SET);

start_time = clock();
ssize_t write_result = write(pipefd&#91;1], large_data, 1024 * 1024);
end_time = clock();
elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;

if (write_result > 0) {
printf("write传输 %zd 字节\n", write_result);
printf("耗时: %.6f 秒\n", elapsed_time);
printf("吞吐量: %.2f MB/s\n", (write_result / (1024.0 * 1024.0)) / elapsed_time);
}

free(large_data);
close(pipefd&#91;0]);
close(pipefd&#91;1]);

printf("=== 性能测试完成 ===\n\n");
}

int main() {
printf("=== 高性能数据管道应用示例 ===\n");

// 执行性能测试
performance_test();

// 创建管道用于进程间通信
int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
exit(EXIT_FAILURE);
}

printf("创建数据管道: 读端=%d, 写端=%d\n", pipefd&#91;0], pipefd&#91;1]);

// 启动生产者进程
pid_t producer_pid = fork();
if (producer_pid == 0) {
// 生产者子进程
close(pipefd&#91;0]); // 关闭读端
producer_process(pipefd&#91;1]);
close(pipefd&#91;1]);
exit(EXIT_SUCCESS);
} else if (producer_pid == -1) {
perror("创建生产者进程失败");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
exit(EXIT_FAILURE);
}

// 启动消费者进程
pid_t consumer_pid = fork();
if (consumer_pid == 0) {
// 消费者子进程
close(pipefd&#91;1]); // 关闭写端
consumer_process(pipefd&#91;0]);
close(pipefd&#91;0]);
exit(EXIT_SUCCESS);
} else if (consumer_pid == -1) {
perror("创建消费者进程失败");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
exit(EXIT_FAILURE);
}

// 父进程关闭两端并等待子进程完成
close(pipefd&#91;0]);
close(pipefd&#91;1]);

printf("主进程等待子进程完成...\n");
waitpid(producer_pid, NULL, 0);
waitpid(consumer_pid, NULL, 0);

printf("所有进程已完成\n");

printf("\n=== 高性能数据管道应用演示完成 ===\n");

return 0;
}

示例3:vmsplice与内存映射结合使用

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

#define SHARED_MEMORY_SIZE (1024 * 1024) // 1MB
#define PACKET_SIZE 1024

// 共享内存结构
typedef struct {
volatile int write_index;
volatile int read_index;
volatile int data_ready;
char data&#91;SHARED_MEMORY_SIZE - sizeof(int) * 3];
} shared_memory_t;

// 使用vmsplice发送内存映射数据
ssize_t send_mmap_data_with_vmsplice(int pipe_fd, const void* data, size_t data_size) {
struct iovec iov;
iov.iov_base = (void*)data;
iov.iov_len = data_size;

// 使用SPLICE_F_GIFT标志,表示传输后内核拥有页面
ssize_t result = vmsplice(pipe_fd, &iov, 1, SPLICE_F_MOVE | SPLICE_F_GIFT);

if (result == -1) {
printf("vmsplice发送失败: %s\n", strerror(errno));
} else {
printf("vmsplice发送成功: %zd 字节\n", result);
}

return result;
}

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

// 填充测试数据
char* buffer = malloc(4096);
if (!buffer) {
close(fd);
return -1;
}

for (size_t i = 0; i < size; i += 4096) {
size_t write_size = (size - i > 4096) ? 4096 : (size - i);
memset(buffer, 'A' + (i / 4096) % 26, write_size - 1);
buffer&#91;write_size - 1] = '\n';

if (write(fd, buffer, write_size) != (ssize_t)write_size) {
perror("写入测试数据失败");
free(buffer);
close(fd);
return -1;
}
}

free(buffer);
printf("创建测试文件: %s (%zu 字节)\n", filename, size);
return fd;
}

// 演示文件到管道的高效传输
void demonstrate_file_to_pipe_transfer() {
printf("=== 文件到管道传输演示 ===\n");

const char* test_file = "vmsplice_test_data.txt";
const size_t file_size = 64 * 1024; // 64KB

// 创建测试数据文件
int file_fd = create_test_data_file(test_file, file_size);
if (file_fd == -1) {
return;
}

// 创建管道
int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
close(file_fd);
unlink(test_file);
return;
}

// 内存映射文件
char* mapped_data = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0);
if (mapped_data == MAP_FAILED) {
perror("内存映射文件失败");
close(file_fd);
close(pipefd&#91;0]);
close(pipefd&#91;1]);
unlink(test_file);
return;
}

printf("文件内存映射成功: %p (%zu 字节)\n", mapped_data, file_size);

// 使用vmsplice传输映射的数据
printf("使用vmsplice传输映射数据...\n");

clock_t start_time = clock();
ssize_t bytes_sent = send_mmap_data_with_vmsplice(pipefd&#91;1], mapped_data, file_size);
clock_t end_time = clock();

if (bytes_sent > 0) {
double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;
printf("传输完成: %zd 字节\n", bytes_sent);
printf("耗时: %.6f 秒\n", elapsed_time);
printf("吞吐量: %.2f MB/s\n", (bytes_sent / (1024.0 * 1024.0)) / elapsed_time);

// 验证数据传输
printf("验证数据传输...\n");
char* verify_buffer = malloc(file_size);
if (verify_buffer) {
ssize_t bytes_received = read(pipefd&#91;0], verify_buffer, file_size);
if (bytes_received > 0) {
printf("验证接收: %zd 字节\n", bytes_received);
if (bytes_received == bytes_sent) {
printf("✓ 数据传输验证通过\n");
} else {
printf("✗ 数据传输验证失败\n");
}
}
free(verify_buffer);
}
}

// 清理资源
munmap(mapped_data, file_size);
close(file_fd);
close(pipefd&#91;0]);
close(pipefd&#91;1]);
unlink(test_file);

printf("=== 文件传输演示完成 ===\n\n");
}

// 高级vmsplice特性演示
void advanced_vmsplice_features() {
printf("=== 高级vmsplice特性演示 ===\n");

int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
return;
}

// 演示不同标志的使用
printf("1. 不同标志演示:\n");

const char* test_message = "测试消息数据";
struct iovec iov;
iov.iov_base = (void*)test_message;
iov.iov_len = strlen(test_message);

// SPLICE_F_MOVE 标志
printf(" 使用 SPLICE_F_MOVE 标志:\n");
ssize_t result = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_MOVE);
if (result > 0) {
printf(" ✓ 传输成功: %zd 字节\n", result);

// 读取验证
char buffer&#91;256];
ssize_t bytes_read = read(pipefd&#91;0], buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" 接收数据: %s\n", buffer);
}
}

// SPLICE_F_MORE 标志(提示还有更多数据)
printf(" 使用 SPLICE_F_MORE 标志:\n");
const char* more_data = "更多数据";
iov.iov_base = (void*)more_data;
iov.iov_len = strlen(more_data);

result = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_MORE);
if (result > 0) {
printf(" ✓ 传输成功: %zd 字节\n", result);
}

// SPLICE_F_NONBLOCK 标志(非阻塞模式)
printf(" 使用 SPLICE_F_NONBLOCK 标志:\n");

// 设置管道为非阻塞模式
int flags = fcntl(pipefd&#91;1], F_GETFL);
fcntl(pipefd&#91;1], F_SETFL, flags | O_NONBLOCK);

const char* nonblock_data = "非阻塞数据";
iov.iov_base = (void*)nonblock_data;
iov.iov_len = strlen(nonblock_data);

result = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_NONBLOCK);
if (result > 0) {
printf(" ✓ 非阻塞传输成功: %zd 字节\n", result);
} else if (result == -1) {
if (errno == EAGAIN) {
printf(" ✓ 非阻塞模式下暂时无法传输 (EAGAIN)\n");
} else {
printf(" ✗ 传输失败: %s\n", strerror(errno));
}
}

// 演示大数据传输
printf("\n2. 大数据传输演示:\n");

// 重置管道为阻塞模式
fcntl(pipefd&#91;1], F_SETFL, flags);

// 分配大块内存
size_t large_data_size = 64 * 1024; // 64KB
char* large_data = malloc(large_data_size);
if (large_data) {
// 填充数据
for (size_t i = 0; i < large_data_size; i++) {
large_data&#91;i] = 'A' + (i % 26);
}

iov.iov_base = large_data;
iov.iov_len = large_data_size;

clock_t start_time = clock();
result = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_MOVE);
clock_t end_time = clock();

if (result > 0) {
double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;
printf(" 大数据传输: %zd 字节\n", result);
printf(" 耗时: %.6f 秒\n", elapsed_time);
printf(" 吞吐量: %.2f MB/s\n", (result / (1024.0 * 1024.0)) / elapsed_time);
}

free(large_data);
}

// 清理资源
close(pipefd&#91;0]);
close(pipefd&#91;1]);

printf("=== 高级特性演示完成 ===\n\n");
}

int main() {
printf("=== vmsplice与内存映射结合使用示例 ===\n");

// 演示文件到管道的高效传输
demonstrate_file_to_pipe_transfer();

// 演示高级vmsplice特性
advanced_vmsplice_features();

// 错误处理和边界情况演示
printf("=== 错误处理演示 ===\n");

// 使用无效参数
printf("1. 无效参数测试:\n");

ssize_t result = vmsplice(-1, NULL, 0, 0);
if (result == -1) {
printf(" 无效文件描述符: %s (预期)\n", strerror(errno));
}

// 使用空向量
int dummy_pipe&#91;2];
if (pipe(dummy_pipe) == 0) {
result = vmsplice(dummy_pipe&#91;1], NULL, 0, 0);
if (result == -1) {
printf(" 空向量: %s (预期)\n", strerror(errno));
}
close(dummy_pipe&#91;0]);
close(dummy_pipe&#91;1]);
}

// 使用无效标志
if (pipe(dummy_pipe) == 0) {
struct iovec iov = {(void*)"test", 4};
result = vmsplice(dummy_pipe&#91;1], &iov, 1, 0xFFFFFFFF);
if (result == -1) {
printf(" 无效标志: %s (预期)\n", strerror(errno));
}
close(dummy_pipe&#91;0]);
close(dummy_pipe&#91;1]);
}

printf("=== 错误处理演示完成 ===\n");

printf("\n=== vmsplice综合演示完成 ===\n");

return 0;
}

示例4:实际应用场景 - 网络数据转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>

#define BUFFER_SIZE 8192
#define MAX_PACKETS 1000

// 数据包结构
typedef struct {
char data&#91;BUFFER_SIZE];
size_t length;
int packet_id;
time_t timestamp;
} data_packet_t;

// 转发器统计信息
typedef struct {
volatile long long packets_forwarded;
volatile long long bytes_forwarded;
volatile long long packets_dropped;
time_t start_time;
} forwarder_stats_t;

forwarder_stats_t stats = {0};

// 模拟网络接收缓冲区
typedef struct {
char* buffer;
size_t size;
size_t offset;
} network_buffer_t;

// 创建模拟网络缓冲区
network_buffer_t* create_network_buffer(size_t size) {
network_buffer_t* nb = malloc(sizeof(network_buffer_t));
if (!nb) return NULL;

nb->buffer = malloc(size);
if (!nb->buffer) {
free(nb);
return NULL;
}

nb->size = size;
nb->offset = 0;

// 填充模拟数据
for (size_t i = 0; i < size; i++) {
nb->buffer&#91;i] = 'A' + (i % 26);
}

return nb;
}

// 从网络缓冲区读取数据包
int read_packet_from_buffer(network_buffer_t* nb, char* packet_buffer, size_t max_size) {
if (nb->offset >= nb->size) {
return 0; // 没有更多数据
}

// 模拟不同大小的数据包
size_t packet_size = 100 + (rand() % 1000);
if (packet_size > max_size) {
packet_size = max_size;
}

if (nb->offset + packet_size > nb->size) {
packet_size = nb->size - nb->offset;
}

if (packet_size > 0) {
memcpy(packet_buffer, nb->buffer + nb->offset, packet_size);
nb->offset += packet_size;
return packet_size;
}

return 0;
}

// 使用vmsplice转发数据包
int forward_packet_with_vmsplice(int pipe_fd, const char* packet_data, size_t packet_size) {
struct iovec iov;
iov.iov_base = (void*)packet_data;
iov.iov_len = packet_size;

ssize_t result = vmsplice(pipe_fd, &iov, 1, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);

if (result > 0) {
__atomic_fetch_add(&stats.packets_forwarded, 1, __ATOMIC_RELAXED);
__atomic_fetch_add(&stats.bytes_forwarded, result, __ATOMIC_RELAXED);
return 0; // 成功
} else if (result == -1) {
if (errno == EAGAIN) {
// 管道满,数据包被丢弃
__atomic_fetch_add(&stats.packets_dropped, 1, __ATOMIC_RELAXED);
return 1; // 丢弃
} else {
perror("vmsplice转发失败");
return -1; // 错误
}
}

return -1;
}

// 数据包生成器线程
void* packet_generator_thread(void* arg) {
int pipe_write_fd = *(int*)arg;
network_buffer_t* nb = create_network_buffer(1024 * 1024); // 1MB缓冲区

if (!nb) {
printf("生成器: 创建网络缓冲区失败\n");
return NULL;
}

printf("生成器线程启动\n");

char packet_buffer&#91;BUFFER_SIZE];
int packet_count = 0;

// 生成数据包
while (packet_count < MAX_PACKETS) {
int packet_size = read_packet_from_buffer(nb, packet_buffer, sizeof(packet_buffer));

if (packet_size > 0) {
// 使用vmsplice转发数据包
int result = forward_packet_with_vmsplice(pipe_write_fd, packet_buffer, packet_size);

if (result == 0) {
packet_count++;
if (packet_count % 100 == 0) {
printf("生成器: 已生成 %d 个数据包\n", packet_count);
}
} else if (result == 1) {
printf("生成器: 数据包被丢弃 (管道满)\n");
} else {
printf("生成器: 转发错误\n");
break;
}

// 模拟网络延迟
usleep(1000); // 1毫秒
} else {
break; // 没有更多数据
}
}

free(nb->buffer);
free(nb);

printf("生成器线程完成,共生成 %d 个数据包\n", packet_count);
return NULL;
}

// 数据包处理器线程
void* packet_processor_thread(void* arg) {
int pipe_read_fd = *(int*)arg;

printf("处理器线程启动\n");

char buffer&#91;BUFFER_SIZE];
int processed_packets = 0;

// 处理数据包
while (processed_packets < MAX_PACKETS) {
ssize_t bytes_received = read(pipe_read_fd, buffer, sizeof(buffer));

if (bytes_received > 0) {
// 模拟数据包处理
processed_packets++;

if (processed_packets % 100 == 0) {
printf("处理器: 已处理 %d 个数据包\n", processed_packets);
}

// 模拟处理时间
usleep(500); // 0.5毫秒
} else if (bytes_received == 0) {
printf("处理器: 管道已关闭\n");
break;
} else {
if (errno == EAGAIN) {
usleep(1000); // 等待1毫秒后重试
continue;
} else {
perror("处理器: 读取数据失败");
break;
}
}
}

printf("处理器线程完成,共处理 %d 个数据包\n", processed_packets);
return NULL;
}

// 显示转发统计
void show_forwarding_statistics() {
time_t current_time = time(NULL);
double elapsed_time = difftime(current_time, stats.start_time);

long long packets_forwarded = __atomic_load_n(&stats.packets_forwarded, __ATOMIC_RELAXED);
long long bytes_forwarded = __atomic_load_n(&stats.bytes_forwarded, __ATOMIC_RELAXED);
long long packets_dropped = __atomic_load_n(&stats.packets_dropped, __ATOMIC_RELAXED);

printf("\n=== 转发统计 ===\n");
printf("转发数据包: %lld\n", packets_forwarded);
printf("转发字节数: %lld (%.2f MB)\n", bytes_forwarded, bytes_forwarded / (1024.0 * 1024.0));
printf("丢弃数据包: %lld\n", packets_dropped);
printf("运行时间: %.2f 秒\n", elapsed_time);

if (elapsed_time > 0) {
printf("平均转发速率: %.2f 包/秒\n", packets_forwarded / elapsed_time);
printf("平均吞吐量: %.2f MB/s\n", (bytes_forwarded / (1024.0 * 1024.0)) / elapsed_time);
}

if (packets_forwarded + packets_dropped > 0) {
double drop_rate = (double)packets_dropped / (packets_forwarded + packets_dropped) * 100;
printf("丢包率: %.2f%%\n", drop_rate);
}
printf("================\n\n");
}

// 性能基准测试
void performance_benchmark() {
printf("=== vmsplice性能基准测试 ===\n");

int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
return;
}

// 设置非阻塞模式
int flags = fcntl(pipefd&#91;1], F_GETFL);
fcntl(pipefd&#91;1], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(pipefd&#91;0], F_GETFL);
fcntl(pipefd&#91;0], F_SETFL, flags | O_NONBLOCK);

// 准备测试数据
size_t test_sizes&#91;] = {1024, 4096, 16384, 65536, 262144}; // 1KB到256KB
int num_sizes = sizeof(test_sizes) / sizeof(test_sizes&#91;0]);

printf("%-10s %-15s %-15s %-15s\n", "大小", "vmsplice", "write", "性能提升");
printf("%-10s %-15s %-15s %-15s\n", "----", "--------", "-----", "--------");

for (int i = 0; i < num_sizes; i++) {
size_t size = test_sizes&#91;i];
char* test_data = malloc(size);
if (!test_data) continue;

// 填充测试数据
memset(test_data, 'X', size);

struct iovec iov;
iov.iov_base = test_data;
iov.iov_len = size;

// 测试vmsplice
clock_t start = clock();
ssize_t vmsplice_result = vmsplice(pipefd&#91;1], &iov, 1, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
clock_t end = clock();
double vmsplice_time = ((double)(end - start)) / CLOCKS_PER_SEC;

// 清空管道
char dummy_buffer&#91;1024 * 1024];
while (read(pipefd&#91;0], dummy_buffer, sizeof(dummy_buffer)) > 0);

// 测试传统write
start = clock();
ssize_t write_result = write(pipefd&#91;1], test_data, size);
end = clock();
double write_time = ((double)(end - start)) / CLOCKS_PER_SEC;

// 清空管道
while (read(pipefd&#91;0], dummy_buffer, sizeof(dummy_buffer)) > 0);

double speedup = (write_time > 0) ? (write_time / vmsplice_time) : 0;

printf("%-10zu %-15.6f %-15.6f %-15.2fx\n",
size, vmsplice_time, write_time, speedup);

free(test_data);
}

close(pipefd&#91;0]);
close(pipefd&#91;1]);

printf("=== 性能基准测试完成 ===\n\n");
}

int main() {
printf("=== 网络数据转发应用示例 ===\n");

// 执行性能基准测试
performance_benchmark();

// 初始化统计
stats.start_time = time(NULL);

// 创建管道用于线程间通信
int pipefd&#91;2];
if (pipe(pipefd) == -1) {
perror("创建管道失败");
exit(EXIT_FAILURE);
}

// 设置管道为非阻塞模式
int flags = fcntl(pipefd&#91;1], F_GETFL);
fcntl(pipefd&#91;1], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(pipefd&#91;0], F_GETFL);
fcntl(pipefd&#91;0], F_SETFL, flags | O_NONBLOCK);

printf("创建转发管道: 读端=%d, 写端=%d\n", pipefd&#91;0], pipefd&#91;1]);

// 创建线程
pthread_t generator_thread, processor_thread;

// 启动数据包生成器线程
if (pthread_create(&generator_thread, NULL, packet_generator_thread, &pipefd&#91;1]) != 0) {
perror("创建生成器线程失败");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
exit(EXIT_FAILURE);
}

// 启动数据包处理器线程
if (pthread_create(&processor_thread, NULL, packet_processor_thread, &pipefd&#91;0]) != 0) {
perror("创建处理器线程失败");
close(pipefd&#91;0]);
close(pipefd&#91;1]);
exit(EXIT_FAILURE);
}

// 主线程定期显示统计信息
for (int i = 0; i < 30; i++) { // 运行30秒
sleep(2);
show_forwarding_statistics();
}

// 等待线程完成
pthread_join(generator_thread, NULL);
pthread_join(processor_thread, NULL);

// 显示最终统计
show_forwarding_statistics();

// 清理资源
close(pipefd&#91;0]);
close(pipefd&#91;1]);

printf("=== 网络数据转发应用演示完成 ===\n");

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
# 编译示例(需要定义_GNU_SOURCE)
gcc -D_GNU_SOURCE -o vmsplice_example1 vmsplice_example1.c
gcc -D_GNU_SOURCE -o vmsplice_example2 vmsplice_example2.c
gcc -D_GNU_SOURCE -o vmsplice_example3 vmsplice_example3.c
gcc -D_GNU_SOURCE -o vmsplice_example4 vmsplice_example4.c -lpthread

# 运行示例
./vmsplice_example1
./vmsplice_example2
./vmsplice_example3
./vmsplice_example4

重要注意事项

内核支持: vmsplice需要Linux 2.6.17或更高版本内核支持

权限要求: 需要适当的权限来创建和访问管道

内存管理: 使用SPLICE_F_GIFT标志时要注意内存所有权转移

非阻塞模式: 建议在生产环境中使用非阻塞模式避免阻塞

错误处理: 必须检查返回值并处理EAGAIN等错误

性能考虑: vmsplice在大数据传输时优势明显

线程安全: 管道操作是线程安全的

最佳实践

使用非阻塞模式: 避免无限期阻塞

合理设置标志: 根据应用场景选择合适的标志

内存管理: 正确处理内存所有权和生命周期

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

性能监控: 监控传输性能和系统资源使用

批量传输: 使用向量传输提高效率

资源清理: 及时关闭文件描述符和释放内存

通过这些示例,你可以理解vmsplice在高效数据传输方面的强大功能,它为Linux系统提供了零拷贝或最小拷贝的数据传输机制,特别适用于高性能网络应用、大数据处理和实时系统。

wait4系统调用及示例

1. 函数介绍

wait4 是一个 Linux 系统调用,它是 waitpid 的一个扩展版本。它的主要功能是等待子进程的状态发生变化(通常是子进程终止或停止),并获取该子进程的退出状态信息。

你可以把 wait4 想象成一位家长在等待孩子(子进程)完成任务后回来汇报情况:

  • 家长(父进程)给孩子(子进程)布置了一个任务(比如运行一个程序)。

  • 孩子出去执行任务。

  • 家长调用 wait4,表示“我在家等你回来,告诉我任务完成得怎么样”。

  • 孩子完成任务回家(子进程终止)。

  • wait4 返回,告诉家长孩子的 PID 和他是如何完成任务的(成功、失败、被中断等)。

wait4 比 wait 和 waitpid 更强大,因为它不仅可以获取子进程的 PID 和状态,还可以同时获取子进程使用的资源统计信息(如用户 CPU 时间、系统 CPU 时间、最大内存使用量等)。

2. 函数原型

1
2
3
4
5
#include <sys/wait.h> // 必需
#include <sys/resource.h> // 包含 struct rusage

pid_t wait4(pid_t pid, int *wstatus, int options, struct rusage *rusage);

3. 功能

  • 等待子进程: 挂起调用进程(父进程),直到由 pid 参数指定的一个或多个子进程的状态发生变化。

  • 获取状态: 当子进程状态变化被检测到时,wait4 会返回该子进程的进程 ID (PID),并将其退出状态(exit status)存储到 wstatus 指向的整型变量中。

  • 获取资源使用信息(可选): 如果 rusage 参数非空,wait4 还会将子进程的资源使用统计信息(Resource Usage)填充到 rusage 指向的 struct rusage 结构体中。

4. 参数

pid_t pid: 指定要等待的子进程的 ID。其行为与 waitpid 的 pid 参数完全相同:

  • < -1: 等待进程组 ID 等于 pid 绝对值的任意子进程。

  • -1: 等待任意子进程(这是最常用的情况)。

  • 0: 等待调用进程组 ID 与调用进程相同的任意子进程。

  • 0: 等待进程 ID 等于 pid 的特定子进程。

int *wstatus: 这是一个指向 int 类型变量的指针,用于接收子进程的退出状态。

  • 如果不需要获取状态,可以传入 NULL(但在实践中很少这么做)。

  • 子进程的退出状态包含了它是如何结束的(正常退出、被信号终止等)以及具体的退出码或信号编号。

通常使用 <sys/wait.h> 中定义的宏来检查和解析 wstatus 的值:

  • WIFEXITED(wstatus): 如果子进程是正常退出(通过 exit() 或从 main 返回),返回真(非 0)。

  • WEXITSTATUS(wstatus): 如果 WIFEXITED(wstatus) 为真,返回子进程的退出码(0-255)。

  • WIFSIGNALED(wstatus): 如果子进程是被信号终止的,返回真。

  • WTERMSIG(wstatus): 如果 WIFSIGNALED(wstatus) 为真,返回终止子进程的信号编号。

  • WIFSTOPPED(wstatus): 如果子进程当前是停止状态(通常由 SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU 信号导致),返回真。

  • WSTOPSIG(wstatus): 如果 WIFSTOPPED(wstatus) 为真,返回导致子进程停止的信号编号。

int options: 这是一个位掩码,用于修改 wait4 的行为。它可以是以下零个或多个标志的按位或(OR)组合:

  • WNOHANG: 如果没有子进程状态立即可用,则 wait4 不阻塞,立即返回 0。

  • WUNTRACED: 报告因信号而停止的子进程状态(即使没有追踪它们)。

  • WCONTINUED: 报告先前因信号停止、现已收到 SIGCONT 信号并继续执行的子进程。

struct rusage *rusage: 这是一个指向 struct rusage 结构体的指针。如果非 NULL,wait4 会将子进程的资源使用统计信息填充到该结构体中。如果为 NULL,则不收集资源信息。struct rusage 包含了很多关于子进程执行期间资源消耗的详细信息,例如:struct rusage { struct timeval ru_utime; // 用户 CPU 时间 struct timeval ru_stime; // 系统 CPU 时间 long ru_maxrss; // 最大常驻集大小 (KB) long ru_ixrss; // 共享内存大小积分 (未维护) long ru_idrss; // 未共享数据大小积分 (未维护) long ru_isrss; // 未共享栈大小积分 (未维护) long ru_minflt; // 缺页中断次数 (无需从磁盘加载页面) long ru_majflt; // 主缺页中断次数 (需要从磁盘加载页面) long ru_nswap; // 内存交换次数 (未维护) long ru_inblock; // 文件系统读入操作次数 long ru_oublock; // 文件系统写入操作次数 long ru_msgsnd; // IPC 消息发送次数 (未维护) long ru_msgrcv; // IPC 消息接收次数 (未维护) long ru_nsignals; // 信号接收次数 long ru_nvcsw; // 自愿上下文切换次数 long ru_nivcsw; // 非自愿上下文切换次数 };

  • 注意: 并非所有字段在所有系统上都得到维护或精确计算。

5. 返回值

成功时:

  • 返回已更改状态的子进程的 PID。

失败时:

  • 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 ECHILD 没有符合条件的子进程,EINTR 调用被信号中断,EINVAL options 参数无效)。

WNOHANG 且无子进程状态改变时:

  • 返回 0。

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

  • wait: wait(&status) 等价于 wait4(-1, &status, 0, NULL)。它是最简单的等待任意子进程结束的函数。

  • waitpid: waitpid(pid, &status, options) 等价于 wait4(pid, &status, options, NULL)。它比 wait 更灵活,允许指定等待哪个子进程以及设置选项。

  • waitid: POSIX.1-2001 标准引入的更现代的等待函数,提供了更细粒度的控制和信息。

  • getrusage: 用于获取调用进程自身或其已终止子进程的资源使用信息。wait4 的 rusage 参数提供了类似的功能,但针对特定的已终止子进程。

7. 示例代码

示例 1:基本的 wait4 使用

这个例子演示了 wait4 最基本的用法,等待子进程结束并获取其退出状态。

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
// wait4_basic.c
#include <sys/wait.h> // wait4, WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG
#include <sys/resource.h> // struct rusage
#include <unistd.h> // fork, getpid
#include <stdio.h> // printf, perror
#include <stdlib.h> // exit

int main() {
pid_t pid;
int status;
struct rusage usage;

printf("Parent process (PID: %d) starting.\n", getpid());

pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}

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

// 模拟一些工作
for (int i = 0; i < 3; ++i) {
printf(" Child working... %d\n", i + 1);
sleep(1);
}

// 子进程以特定状态码退出
int exit_code = 42;
printf("Child process (PID: %d) finished. Exiting with code %d.\n", getpid(), exit_code);
exit(exit_code); // 正常退出

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

// --- 关键: 调用 wait4 等待子进程结束 ---
// pid: 等待特定子进程 pid
// &status: 接收子进程退出状态
// 0: 默认选项 (阻塞等待)
// &usage: 接收资源使用信息
pid_t waited_pid = wait4(pid, &status, 0, &usage);

if (waited_pid == -1) {
perror("wait4 failed");
exit(EXIT_FAILURE);
}

printf("Parent: wait4 returned. Waited for child PID %d.\n", (int)waited_pid);

// --- 检查和解析子进程退出状态 ---
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
printf("Parent: Child exited normally with status code %d.\n", exit_status);
} else if (WIFSIGNALED(status)) {
int signal_num = WTERMSIG(status);
printf("Parent: Child was killed by signal %d.\n", signal_num);
} else {
printf("Parent: Child did not exit normally or by signal.\n");
}

// --- 打印资源使用信息 ---
printf("\n--- Resource Usage of Child (PID: %d) ---\n", (int)waited_pid);
printf("User CPU time used: %ld.%06ld seconds\n",
(long)usage.ru_utime.tv_sec, (long)usage.ru_utime.tv_usec);
printf("System CPU time used: %ld.%06ld seconds\n",
(long)usage.ru_stime.tv_sec, (long)usage.ru_stime.tv_usec);
printf("Maximum resident set size: %ld KB\n", usage.ru_maxrss);
printf("Page reclaims (soft page faults): %ld\n", usage.ru_minflt);
printf("Page faults (hard page faults): %ld\n", usage.ru_majflt);
printf("Voluntary context switches: %ld\n", usage.ru_nvcsw);
printf("Involuntary context switches: %ld\n", usage.ru_nivcsw);
printf("------------------------------------------\n");

printf("Parent process (PID: %d) finished.\n", getpid());
}

return 0;
}

代码解释:

父进程调用 fork() 创建子进程。

子进程:

  • 执行一些模拟工作(循环打印并 sleep)。

  • 以状态码 42 调用 exit(42) 正常退出。

父进程:
调用 wait4(pid, &status, 0, &usage)。

  • pid: 等待之前创建的特定子进程。

  • &status: 指向 int 变量,用于接收退出状态。

  • 0: 使用默认选项,即阻塞等待。

  • &usage: 指向 struct rusage 变量,用于接收资源使用信息。

检查 wait4 的返回值。如果成功,返回值是子进程的 PID。

使用 WIFEXITED 和 WEXITSTATUS 宏检查子进程是否正常退出,并获取其退出码(42)。

打印从 rusage 结构体中获取的资源使用统计信息,如用户 CPU 时间、系统 CPU 时间、最大内存使用量等。

示例 2:使用 wait4 等待任意子进程 (pid = -1)

这个例子演示了如何使用 wait4 等待任意一个子进程结束。

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

#define NUM_CHILDREN 3

int main() {
pid_t pid;
int i;

printf("Parent process (PID: %d) creating %d children.\n", getpid(), NUM_CHILDREN);

// 1. 创建多个子进程
for (i = 0; i < NUM_CHILDREN; i++) {
pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// --- 子进程 ---
printf("Child %d (PID: %d) started.\n", i, getpid());
// 每个子进程睡眠不同的时间
sleep(i + 2);
printf("Child %d (PID: %d) finished. Exiting with code %d.\n", i, getpid(), i);
exit(i); // 以 i 作为退出码
}
// --- 父进程继续循环 ---
}

printf("Parent (PID: %d) created all children. Now waiting for them to finish.\n", getpid());

// 2. 循环等待所有子进程结束
// 使用 wait4(pid=-1, ...) 等待任意子进程
for (i = 0; i < NUM_CHILDREN; i++) {
int status;
struct rusage usage;
pid_t waited_pid;

// --- 关键: 使用 pid = -1 等待任意子进程 ---
waited_pid = wait4(-1, &status, 0, &usage);

if (waited_pid == -1) {
perror("wait4 failed");
// 可能需要更复杂的错误处理
continue;
}

printf("\nParent: wait4 returned. Waited for child PID %d.\n", (int)waited_pid);

if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("Parent: Child (PID: %d) exited normally with code %d.\n", (int)waited_pid, exit_code);
} else {
printf("Parent: Child (PID: %d) did not exit normally.\n", (int)waited_pid);
}

printf("Resource usage summary for child (PID: %d):\n", (int)waited_pid);
printf(" User CPU time: %ld.%06lds\n", (long)usage.ru_utime.tv_sec, (long)usage.ru_utime.tv_usec);
printf(" Sys CPU time: %ld.%06lds\n", (long)usage.ru_stime.tv_sec, (long)usage.ru_stime.tv_usec);
printf(" Max RSS: %ld KB\n", usage.ru_maxrss);
}

printf("\nParent (PID: %d) finished. All children have been reaped.\n", getpid());
return 0;
}

代码解释:

父进程通过循环调用 fork() 创建 3 个子进程。

每个子进程执行不同的睡眠时间(2秒、3秒、4秒),然后以自己的索引 i 作为退出码退出。

父进程进入另一个循环,调用 NUM_CHILDREN 次 wait4。

关键: 每次调用 wait4(-1, &status, 0, &usage)。

  • pid = -1: 表示等待任意一个子进程结束。

每次 wait4 返回,就处理一个子进程的退出信息和资源使用情况。

循环结束后,所有子进程都被回收。

示例 3:使用 wait4 的 WNOHANG 选项

这个例子演示了如何使用 WNOHANG 选项让 wait4 非阻塞地检查是否有子进程已经结束。

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

int main() {
pid_t pid;
int i;

printf("Parent process (PID: %d) creating 2 children.\n", getpid());

// 1. 创建两个子进程
for (i = 0; i < 2; i++) {
pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// --- 子进程 ---
printf("Child %d (PID: %d) started, will sleep for %d seconds.\n", i, getpid(), 5 + i * 2);
sleep(5 + i * 2); // 第一个睡 5 秒,第二个睡 7 秒
printf("Child %d (PID: %d) finished. Exiting.\n", i, getpid());
exit(0);
}
}

printf("Parent (PID: %d) created children. Now polling with WNOHANG.\n", getpid());

int children_finished = 0;
int loop_count = 0;

// 2. 循环,使用 WNOHANG 非阻塞地检查子进程状态
while (children_finished < 2) {
loop_count++;
int status;
struct rusage usage;
pid_t waited_pid;

// --- 关键: 使用 WNOHANG 选项 ---
waited_pid = wait4(-1, &status, WNOHANG, &usage);

if (waited_pid == -1) {
if (errno == ECHILD) {
// 没有子进程了,但这在循环中不太可能发生
// 因为我们知道有两个子进程
printf("No children exist (ECHILD). This is unexpected in this loop.\n");
break;
} else {
perror("wait4 failed");
break;
}
} else if (waited_pid == 0) {
// 没有子进程状态改变
printf("Loop %d: No child has finished yet. Parent doing other work...\n", loop_count);
// 模拟父进程在等待期间做其他事情
sleep(1); // 实际应用中可能是处理其他任务
} else {
// 有一个子进程结束了
children_finished++;
printf("\nLoop %d: Parent: wait4 returned. Child PID %d has finished.\n", loop_count, (int)waited_pid);
if (WIFEXITED(status)) {
printf(" Child (PID: %d) exited normally with code %d.\n", (int)waited_pid, WEXITSTATUS(status));
}
printf(" Parent did other work for %d loop(s) while waiting.\n", loop_count);
}
}

printf("\nParent (PID: %d) finished. Both children have been reaped after %d loops.\n", getpid(), loop_count);
return 0;
}

代码解释:

父进程创建两个子进程,它们分别睡眠 5 秒和 7 秒。

父进程进入一个 while 循环。

关键: 在循环内部调用 wait4(-1, &status, WNOHANG, &usage)。

  • WNOHANG: 使 wait4 成为非阻塞调用。

检查 wait4 的返回值:

  • waited_pid == -1: 调用失败。检查 errno 是否为 ECHILD。

  • waited_pid == 0: 没有子进程状态改变。父进程可以在此时执行其他任务(这里用 sleep(1) 模拟)。

  • waited_pid > 0: 一个子进程结束了。处理其状态,并增加计数器 children_finished。

循环直到两个子进程都结束。

打印父进程在等待期间执行了多少次循环(模拟做了多少其他工作)。

重要提示与注意事项:

僵尸进程: 当子进程结束而父进程尚未调用 wait4(或 wait, waitpid)来获取其状态时,子进程会变成僵尸进程(Zombie Process)。这会浪费一个进程表项。因此,父进程必须等待其子进程。

ECHILD 错误: 如果没有符合条件的子进程(例如,所有子进程都已结束且状态已被回收),wait4 会返回 -1 并设置 errno 为 ECHILD。

EINTR 错误: 如果 wait4(阻塞模式)在等待期间被信号中断,它会返回 -1 并设置 errno 为 EINTR。可以使用 sigaction 设置 SA_RESTART 标志或在循环中处理此错误。

rusage 的准确性: rusage 提供的资源信息的准确性和完整性可能因系统和内核版本而异。某些字段可能未被维护。

WNOHANG 的用途: WNOHANG 对于实现非阻塞的服务器或需要在等待子进程的同时处理其他任务的程序非常有用。

与 wait/waitpid 的关系:

  • wait(&status) 等价于 wait4(-1, &status, 0, NULL)。

  • waitpid(pid, &status, options) 等价于 wait4(pid, &status, options, NULL)。

总结:

wait4 是一个功能强大的系统调用,用于等待子进程状态变化并获取其退出状态和资源使用信息。它通过 pid 参数提供了灵活的等待目标选择,通过 options 参数(特别是 WNOHANG)提供了阻塞/非阻塞行为控制,并通过 rusage 参数提供了宝贵的性能分析数据。理解其参数和返回值对于编写健壮、高效的多进程程序至关重要。

waitid 系统调用及示例

好的,我们来深入学习 waitid 系统调用

1. 函数介绍

在 Linux 系统中,当一个进程创建了子进程(使用 fork),父进程通常需要知道子进程何时结束(退出或被终止),以及它是如何结束的(正常退出码、被哪个信号杀死等)。这是进程管理和资源回收的重要环节。

waitid 系统调用就是用来让父进程(或具有适当权限的进程)等待一个或一组子进程的状态发生变化,并获取该变化的详细信息。

你可以把它想象成一个“进程状态监听器”。父进程调用 waitid 后,它会挂起(阻塞),直到它感兴趣的子进程发生了指定类型的事件(比如退出、被信号终止、停止、继续等)。当事件发生时,waitid 会返回,并把详细信息(哪个子进程、如何结束的)填充到一个结构体中。

waitid 相比于老一些的 wait 和 waitpid,提供了更强大和灵活的功能。

简单来说,waitid 就是让你用程序来“等待”并“获取”子进程的“死亡/停止/恢复”通知书,并且通知书上写得非常详细。

2. 函数原型

1
2
3
4
#include <sys/wait.h> // 包含 waitid 函数声明和相关常量

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

3. 功能

挂起调用进程,直到由 idtype 和 id 指定的一个或多个子进程的状态发生变化(变化类型由 options 指定)。当满足条件的子进程状态改变时,将详细的状态信息填充到 infop 指向的 siginfo_t 结构体中。

4. 参数详解

idtype:

  • idtype_t 类型。

指定要等待的进程的类型。它决定了 id 参数的含义。常见的值有:

  • P_PID: 等待由 id 指定的特定进程 ID (PID) 的子进程。

  • P_PGID: 等待进程组 ID (PGID) 等于 id 的所有子进程。

  • P_ALL: 等待调用进程的所有子进程(此时 id 参数被忽略)。

id:

  • id_t 类型。

其含义由 idtype 决定:

  • 如果 idtype 是 P_PID,则 id 是要等待的子进程的 PID。

  • 如果 idtype 是 P_PGID,则 id 是要等待的子进程组的 PGID。

  • 如果 idtype 是 P_ALL,则 id 被忽略(通常设为 0)。

infop:

  • siginfo_t * 类型。

  • 一个指向 siginfo_t 结构体的指针。当 waitid 成功返回时,该结构体会被内核填充为关于已改变状态的子进程的详细信息。

siginfo_t 结构体包含很多字段,关键的有:

  • si_pid: 导致状态改变的子进程的 PID。

  • si_status: 子进程的退出状态或导致其状态改变的信号编号。

si_code: 状态改变的原因代码,例如:

  • CLD_EXITED: 子进程通过 exit() 或从 main 返回正常退出。

  • CLD_KILLED: 子进程被信号杀死。

  • CLD_DUMPED: 子进程被信号杀死并产生了核心转储 (core dump)。

  • CLD_STOPPED: 子进程被信号(如 SIGSTOP)停止。

  • CLD_CONTINUED: 子进程从停止状态被 SIGCONT 信号恢复继续运行。

… 还有其他字段。

options:

  • int 类型。

一个位掩码,用于指定要等待的状态变化类型以及调用的行为。可以是以下值的按位或 (|) 组合:

状态类型 (必须至少指定一个):

  • WEXITED: 等待子进程正常退出(调用 exit() 或从 main 返回)。

  • WSTOPPED: 等待子进程被停止(通常是收到 SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU 信号)。

  • WCONTINUED: 等待被停止的子进程恢复运行(收到 SIGCONT 信号)。

行为标志 (可选):

  • WNOHANG: 非阻塞。如果没有任何子进程的状态符合条件,waitid 立即返回 0,而不挂起调用进程。

  • WNOWAIT: 不收割。获取子进程状态信息,但不将其从内核的子进程表中删除。这意味着后续的 wait 调用仍可能获取到该子进程的信息。

5. 返回值

  • 成功: 返回 0。

  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。

6. 错误码 (errno)

  • ECHILD: 没有符合条件的子进程。例如,指定了一个不存在的 PID,或者使用 WNOHANG 时没有子进程处于可收割状态。

  • EINTR: 系统调用被信号中断。

  • EINVAL: idtype 或 options 参数无效。

7. 相似函数或关联函数

  • wait: 最基础的等待子进程退出的函数。它等待任意一个子进程退出,并返回 PID 和状态码(需要使用宏如 WIFEXITED, WEXITSTATUS 等来解析)。pid_t wait(int *wstatus);

  • waitpid: wait 的增强版。允许指定等待特定 PID 的子进程,或使用 WNOHANG 等选项。pid_t waitpid(pid_t pid, int *wstatus, int options);

  • wait3 / wait4: 更老的函数,功能与 waitpid 类似,但可以额外返回资源使用信息(struct rusage)。

  • siginfo_t: waitid 使用的关键数据结构,包含详细的子进程状态信息。

8. 示例代码

下面的示例演示了如何使用 waitid 来等待不同类型的子进程事件。

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
#define _GNU_SOURCE // 启用 GNU 扩展
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h> // 包含 waitid, siginfo_t 等
#include <signal.h> // 包含 kill, SIG* 常量
#include <string.h>
#include <errno.h>

// 辅助函数:打印 siginfo_t 中的信息
void print_siginfo(const siginfo_t *info) {
printf(" Child PID: %d\n", info->si_pid);
printf(" Signal/Exit Code: %d\n", info->si_status);
printf(" Reason Code: ");
switch (info->si_code) {
case CLD_EXITED:
printf("CLD_EXITED (Child called exit())\n");
printf(" Exit Status: %d\n", info->si_status);
break;
case CLD_KILLED:
printf("CLD_KILLED (Child was killed by signal)\n");
printf(" Signal Number: %d\n", info->si_status);
break;
case CLD_DUMPED:
printf("CLD_DUMPED (Child killed by signal and dumped core)\n");
printf(" Signal Number: %d\n", info->si_status);
break;
case CLD_STOPPED:
printf("CLD_STOPPED (Child was stopped by signal)\n");
printf(" Stop Signal Number: %d\n", info->si_status);
break;
case CLD_CONTINUED:
printf("CLD_CONTINUED (Child continued)\n");
// si_status for CLD_CONTINUED is not defined to be meaningful
break;
default:
printf("Unknown reason code: %d\n", info->si_code);
break;
}
}

int main() {
pid_t pid1, pid2, pid3;
siginfo_t info;

printf("--- Demonstrating waitid ---\n");
printf("Parent PID: %d\n", getpid());

// 1. 创建一个会正常退出的子进程
pid1 = fork();
if (pid1 == 0) {
// --- Child 1 ---
printf("&#91;Child 1, PID %d] Running for 3 seconds then exiting with status 42.\n", getpid());
sleep(3);
exit(42);
}

// 2. 创建一个会被信号杀死的子进程
pid2 = fork();
if (pid2 == 0) {
// --- Child 2 ---
printf("&#91;Child 2, PID %d] Running for 5 seconds then will be killed by SIGTERM.\n", getpid());
sleep(5);
// 这行不会执行到
exit(0);
}

// 3. 创建一个会停止和恢复的子进程
pid3 = fork();
if (pid3 == 0) {
// --- Child 3 ---
printf("&#91;Child 3, PID %d] Running, then will stop and continue.\n", getpid());
printf("&#91;Child 3] Entering loop, press Ctrl+Z in another terminal to stop me (if I'm foreground).\n");
printf("&#91;Child 3] Or, the parent will send SIGSTOP and SIGCONT.\n");
int counter = 0;
while (counter < 10) {
printf("&#91;Child 3] Working... %d\n", counter++);
sleep(1);
}
printf("&#91;Child 3] Finished normally.\n");
exit(100);
}

// --- Parent Process ---
printf("&#91;Parent] Created children: PID1=%d, PID2=%d, PID3=%d\n", pid1, pid2, pid3);

// 稍等一下,让子进程启动
sleep(1);

// 4. 向 Child 3 发送 SIGSTOP 使其停止
printf("\n&#91;Parent] Sending SIGSTOP to Child 3 (PID %d)...\n", pid3);
if (kill(pid3, SIGSTOP) == -1) {
perror("&#91;Parent] kill SIGSTOP");
}

// 等待 Child 3 停止
printf("&#91;Parent] Waiting for Child 3 to stop using waitid(WSTOPPED)...\n");
memset(&info, 0, sizeof(info)); // 清零结构体
if (waitid(P_PID, pid3, &info, WSTOPPED) == -1) {
perror("&#91;Parent] waitid for stop");
} else {
printf("&#91;Parent] Detected Child 3 stopped:\n");
print_siginfo(&info);
}

// 5. 等待 Child 1 正常退出
printf("\n&#91;Parent] Waiting for Child 1 to exit using waitid(WEXITED)...\n");
memset(&info, 0, sizeof(info));
if (waitid(P_PID, pid1, &info, WEXITED) == -1) {
perror("&#91;Parent] waitid for exit pid1");
} else {
printf("&#91;Parent] Detected Child 1 exited:\n");
print_siginfo(&info);
}

// 6. 向 Child 2 发送 SIGTERM 使其终止
printf("\n&#91;Parent] Sending SIGTERM to Child 2 (PID %d)...\n", pid2);
if (kill(pid2, SIGTERM) == -1) {
perror("&#91;Parent] kill SIGTERM");
}

// 等待 Child 2 被杀死
printf("&#91;Parent] Waiting for Child 2 to be killed using waitid(WEXITED)...\n");
memset(&info, 0, sizeof(info));
if (waitid(P_PID, pid2, &info, WEXITED) == -1) {
perror("&#91;Parent] waitid for exit pid2");
} else {
printf("&#91;Parent] Detected Child 2 killed/exited:\n");
print_siginfo(&info);
}

// 7. 向 Child 3 发送 SIGCONT 使其恢复
printf("\n&#91;Parent] Sending SIGCONT to Child 3 (PID %d)...\n", pid3);
if (kill(pid3, SIGCONT) == -1) {
perror("&#91;Parent] kill SIGCONT");
}

// 等待 Child 3 恢复运行 (这个可能不会立即发生,取决于子进程何时真正恢复)
// 更常见的是等待它最终退出
printf("&#91;Parent] Waiting for Child 3 to continue and then exit using waitid(WCONTINUED | WEXITED)...\n");
printf("&#91;Parent] (WCONTINUED detection might be unreliable, waiting for exit instead)\n");
memset(&info, 0, sizeof(info));
// 通常我们只等待最终的退出
if (waitid(P_PID, pid3, &info, WEXITED) == -1) {
perror("&#91;Parent] waitid for exit pid3");
} else {
printf("&#91;Parent] Detected Child 3 exited:\n");
print_siginfo(&info);
}

// 8. 演示 WNOHANG (非阻塞)
printf("\n&#91;Parent] Demonstrating WNOHANG...\n");
printf("&#91;Parent] Calling waitid(P_ALL, 0, info, WEXITED | WNOHANG)...\n");
memset(&info, 0, sizeof(info));
int result = waitid(P_ALL, 0, &info, WEXITED | WNOHANG);
if (result == -1) {
perror("&#91;Parent] waitid WNOHANG");
} else if (result == 0) {
// 如果返回 0,表示成功调用,但没有符合条件的子进程状态改变
// 因为我们已经等待了所有子进程退出,所以这里应该没有更多可收割的
printf("&#91;Parent] WNOHANG returned 0: No children available to wait for.\n");
}

printf("\n&#91;Parent] All children have been waited for. Parent exiting.\n");

printf("\n--- Summary ---\n");
printf("1. waitid(idtype, id, infop, options) waits for child process state changes.\n");
printf("2. idtype/id let you specify which child/children to wait for (PID, PGID, ALL).\n");
printf("3. options specify what events to wait for (WEXITED, WSTOPPED, WCONTINUED).\n");
printf("4. WNOHANG makes it non-blocking. WNOWAIT gets status without reaping.\n");
printf("5. infop (siginfo_t*) provides detailed information about the event.\n");
printf("6. It's more flexible and informative than wait/waitpid.\n");

return 0;
}

9. 编译和运行

1
2
3
4
5
6
# 假设代码保存在 waitid_example.c 中
gcc -o waitid_example waitid_example.c

# 运行程序
./waitid_example

10. 预期输出

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
--- Demonstrating waitid ---
Parent PID: 12345
&#91;Child 1, PID 12346] Running for 3 seconds then exiting with status 42.
&#91;Child 2, PID 12347] Running for 5 seconds then will be killed by SIGTERM.
&#91;Child 3, PID 12348] Running, then will stop and continue.
&#91;Child 3] Entering loop, press Ctrl+Z in another terminal to stop me (if I'm foreground).
&#91;Child 3] Or, the parent will send SIGSTOP and SIGCONT.
&#91;Parent] Created children: PID1=12346, PID2=12347, PID3=12348

&#91;Parent] Sending SIGSTOP to Child 3 (PID 12348)...
&#91;Parent] Waiting for Child 3 to stop using waitid(WSTOPPED)...
&#91;Child 3] Working... 0
&#91;Child 3] Working... 1
&#91;Parent] Detected Child 3 stopped:
Child PID: 12348
Signal/Exit Code: 19
Reason Code: CLD_STOPPED (Child was stopped by signal)
Stop Signal Number: 19

&#91;Parent] Waiting for Child 1 to exit using waitid(WEXITED)...
&#91;Child 1] Running for 3 seconds then exiting with status 42.
&#91;Parent] Detected Child 1 exited:
Child PID: 12346
Signal/Exit Code: 42
Reason Code: CLD_EXITED (Child called exit())
Exit Status: 42

&#91;Parent] Sending SIGTERM to Child 2 (PID 12347)...
&#91;Parent] Waiting for Child 2 to be killed using waitid(WEXITED)...
&#91;Child 2] Running for 5 seconds then will be killed by SIGTERM.
&#91;Parent] Detected Child 2 killed/exited:
Child PID: 12347
Signal/Exit Code: 15
Reason Code: CLD_KILLED (Child was killed by signal)
Signal Number: 15

&#91;Parent] Sending SIGCONT to Child 3 (PID 12348)...
&#91;Parent] Waiting for Child 3 to continue and then exit using waitid(WCONTINUED | WEXITED)...
&#91;Parent] (WCONTINUED detection might be unreliable, waiting for exit instead)
&#91;Child 3] Working... 2
&#91;Child 3] Working... 3
&#91;Child 3] Working... 4
&#91;Child 3] Working... 5
&#91;Child 3] Working... 6
&#91;Child 3] Working... 7
&#91;Child 3] Working... 8
&#91;Child 3] Working... 9
&#91;Child 3] Finished normally.
&#91;Parent] Detected Child 3 exited:
Child PID: 12348
Signal/Exit Code: 100
Reason Code: CLD_EXITED (Child called exit())
Exit Status: 100

&#91;Parent] Demonstrating WNOHANG...
&#91;Parent] Calling waitid(P_ALL, 0, info, WEXITED | WNOHANG)...
&#91;Parent] WNOHANG returned 0: No children available to wait for.

&#91;Parent] All children have been waited for. Parent exiting.

--- Summary ---
1. waitid(idtype, id, infop, options) waits for child process state changes.
2. idtype/id let you specify which child/children to wait for (PID, PGID, ALL).
3. options specify what events to wait for (WEXITED, WSTOPPED, WCONTINUED).
4. WNOHANG makes it non-blocking. WNOWAIT gets status without reaping.
5. infop (siginfo_t*) provides detailed information about the event.
6. It's more flexible and informative than wait/waitpid.

11. 总结

waitid 是一个功能强大且信息丰富的系统调用,用于等待子进程状态变化。

核心优势:

  • 灵活性高:可以精确指定等待哪个进程/进程组,以及等待哪种类型的事件(退出、停止、恢复)。

  • 信息详细:通过 siginfo_t 结构体返回非常详细的子进程状态信息,比 wait/waitpid 的 wstatus 整数更易于理解和使用。

  • 功能完整:支持停止/恢复事件的等待(WSTOPPED, WCONTINUED)。

参数:idtype/id 定义范围,options 定义事件类型和行为,infop 接收结果。

使用场景:

  • 需要精确控制等待哪个子进程。

  • 需要区分子进程是正常退出、被信号杀死还是停止/恢复。

  • 编写复杂的进程管理器或守护进程。

与 wait/waitpid 的关系:

  • wait(&status) 基本等价于 waitpid(-1, &status, 0)。

  • waitpid(pid, &status, options) 功能是 waitid 的子集。

  • waitid 提供了 waitpid 所没有的 WSTOPPED/WCONTINUED 等选项(除非使用非标准扩展),以及更详细的信息返回方式。

write系统调用及示例

我们来介绍与 read 和 open 紧密配合使用的 write 函数。如果说 read 是从文件描述符“拿”数据,那么 write 就是向文件描述符“放”数据。

  1. 函数介绍

write 是一个 Linux 系统调用,用于将数据从程序的缓冲区写入到由文件描述符 fd 指定的文件、管道、套接字或其他输出流中。它是程序向外部(如文件、屏幕、网络)发送数据的基本方式之一。

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

ssize_t write(int fd, const void *buf, size_t count);

  1. 功能

尝试将 count 个字节的数据从 buf 指向的缓冲区写入到文件描述符 fd 所关联的文件或资源中。

  1. 参数
  • int fd: 这是目标文件描述符。它标识了数据要写入的目标,比如通过 open 打开的文件、标准输出 (STDOUT_FILENO)、标准错误 (STDERR_FILENO) 或一个网络套接字等。

  • const void *buf: 这是一个指向包含待写入数据的缓冲区的指针。由于数据是从这个缓冲区读取并写出去的,所以指针被声明为 const,表明函数不会修改这块内存中的数据。

  • size_t count: 这是要写入的字节数。函数会尝试写入从 buf 开始的 count 个字节。

  1. 返回值

write 函数返回实际成功写入的字节数。

成功时:

  • 返回实际写入的字节数 (0 <= 返回值 <= count)。

  • 在阻塞模式下,通常返回值会等于 count。但在某些情况下(如写入管道或网络套接字时缓冲区满),返回值可能小于 count。这时,程序通常需要循环调用 write 来写入剩余的数据。

出错时:

  • 返回 -1,并设置全局变量 errno 来指示具体的错误类型(例如 EAGAIN、EBADF、EFAULT、ENOSPC 等)。

重要提示: 绝对不能仅仅通过检查 write 的返回值是否为 -1 来判断写操作是否完全成功。必须检查返回值是否等于请求写入的字节数 count,或者在返回值小于 count 时采取相应措施(如循环写入)。

  1. 相似函数,或关联函数
  • pwrite: 类似于 write,但它允许你在一次调用中同时指定要写入的文件描述符、缓冲区、写入字节数以及文件内的偏移量。它不会改变文件的当前偏移量。

  • writev: 允许你将多个不连续缓冲区(一个 iovec 数组)中的数据写入到文件描述符中,这对于需要拼接发送多个数据块的场景非常有用。

  • read: 与 write 相反,用于从文件描述符读取数据。

  • open: 通常在调用 write 之前使用,用来获取要写入文件的文件描述符。

  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
#include <unistd.h>  // write, close
#include <fcntl.h> // open, O_WRONLY, O_CREAT, O_TRUNC
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <string.h> // strlen
#include <errno.h> // errno

int main() {
int fd; // 文件描述符
const char *message = "Hello, Linux System Programming!\n";
size_t message_len = strlen(message); // 获取要写入的字节数
ssize_t bytes_written; // 实际写入的字节数

// 1. 打开(或创建)一个文件用于写入
// O_WRONLY: 只写模式
// O_CREAT: 如果文件不存在则创建
// O_TRUNC: 如果文件存在,则截断(清空)它
// 0644 是新创建文件的权限 (所有者读写,组和其他用户只读)
fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("Error opening/creating file");
exit(EXIT_FAILURE);
}

printf("File 'output.txt' opened/created successfully with fd: %d\n", fd);

// 2. 调用 write 将数据写入文件
bytes_written = write(fd, message, message_len);

// 3. 检查 write 的返回值
if (bytes_written == -1) {
perror("Error writing to file");
close(fd); // 出错也要记得关闭文件
exit(EXIT_FAILURE);
} else if ((size_t)bytes_written != message_len) {
// 检查是否所有数据都被写入 (在简单场景下通常如此,但好习惯是检查)
fprintf(stderr, "Warning: Only %zd of %zu bytes written to file.\n", bytes_written, message_len);
// 在实际应用中,可能需要循环 write 来处理这种情况
} else {
printf("Successfully wrote %zd bytes to 'output.txt'.\n", bytes_written);
}

// 4. 关闭文件描述符
if (close(fd) == -1) {
perror("Error closing file");
exit(EXIT_FAILURE);
}

printf("File closed. Check 'output.txt' for the content.\n");
return 0;
}

代码解释:

  • 定义要写入的字符串 message 和其长度 message_len。

  • 使用 open() 以只写模式 (O_WRONLY) 打开或创建 output.txt 文件。如果文件不存在则创建 (O_CREAT),如果存在则清空 (O_TRUNC)。权限设置为 0644。

  • 调用 write(fd, message, message_len) 尝试将整个消息写入文件。

  • 检查 write 的返回值:-1 表示错误;如果返回值不等于 message_len,则表示未完全写入(在此简单场景下不太可能发生,但展示了检查的必要性);否则表示成功写入。

  • 最后,使用 close() 关闭文件描述符。

示例 2:向标准错误输出写入错误信息

这个例子展示了如何使用 write 向标准错误 (STDERR_FILENO) 写入信息,这通常用于输出错误或诊断信息,与标准输出分开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <unistd.h>  // write
#include <string.h> // strlen

int main() {
const char *error_msg = "An error occurred in the program.\n";

// 直接向标准错误 (文件描述符 2) 写入错误信息
// 注意:这里也没有处理 write 可能部分写入的情况,
// 对于短消息写入 stderr 通常可以假设一次性成功,
// 但在严格要求下仍需检查返回值。
write(STDERR_FILENO, error_msg, strlen(error_msg));

return 0; // 程序正常退出,但 stderr 上有错误信息
}

代码解释:

定义错误信息字符串。

直接调用 write(STDERR_FILENO, error_msg, strlen(error_msg)) 将错误信息写入标准错误输出。STDERR_FILENO 是预定义的常量,值为 2。

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

writev系统调用及示例

writev 函数详解

  1. 函数介绍

writev 是Linux系统调用,用于向文件描述符写入多个分散的缓冲区数据(scatter-gather I/O)。它是 write 函数的增强版本,允许一次系统调用写入多个不连续的内存区域,减少了系统调用的开销,提高了I/O性能。

  1. 函数原型
1
2
3
#include <sys/uio.h>
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

  1. 功能

writev 将多个分散的缓冲区数据原子性地写入到指定的文件描述符中。它使用分散/聚集I/O(scatter-gather I/O)机制,可以显著减少系统调用次数,提高大量小数据块写入的性能。

  1. 参数
  • int fd: 目标文件描述符

  • *const struct iovec iov: iovec结构体数组,描述多个缓冲区

  • int iovcnt: iovec数组中的元素个数

  1. 返回值
  • 成功: 返回实际写入的字节数

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

  1. 相似函数,或关联函数
  • readv: 对应的读取函数

  • write: 基本写入函数

  • sendmsg/recvmsg: 网络套接字的分散/聚集I/O

  • preadv/pwritev: 带偏移量的分散/聚集I/O

  1. 示例代码

示例1:基础writev使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

/**
* 演示基础writev使用方法
*/
int demo_writev_basic() {
const char *header = "HTTP/1.1 200 OK\r\n";
const char *content_type = "Content-Type: text/html\r\n";
const char *content_length = "Content-Length: 25\r\n";
const char *connection = "Connection: close\r\n";
const char *blank_line = "\r\n";
const char *body = "<html><body>Hello</body></html>";

struct iovec iov&#91;6];
int fd;
ssize_t bytes_written;

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

// 准备iovec数组
iov&#91;0].iov_base = (void*)header;
iov&#91;0].iov_len = strlen(header);

iov&#91;1].iov_base = (void*)content_type;
iov&#91;1].iov_len = strlen(content_type);

iov&#91;2].iov_base = (void*)content_length;
iov&#91;2].iov_len = strlen(content_length);

iov&#91;3].iov_base = (void*)connection;
iov&#91;3].iov_len = strlen(connection);

iov&#91;4].iov_base = (void*)blank_line;
iov&#91;4].iov_len = strlen(blank_line);

iov&#91;5].iov_base = (void*)body;
iov&#91;5].iov_len = strlen(body);

// 显示要写入的数据
printf("准备写入的数据:\n");
for (int i = 0; i < 6; i++) {
printf(" 缓冲区 %d: %.*s", i + 1, (int)iov&#91;i].iov_len, (char*)iov&#91;i].iov_base);
// 如果不是以换行符结尾,添加换行符
if (iov&#91;i].iov_len > 0 && ((char*)iov&#91;i].iov_base)&#91;iov&#91;i].iov_len - 1] != '\n') {
printf("\n");
}
}

printf("\n总数据长度: %zu 字节\n",
iov&#91;0].iov_len + iov&#91;1].iov_len + iov&#91;2].iov_len +
iov&#91;3].iov_len + iov&#91;4].iov_len + iov&#91;5].iov_len);

// 写入到标准输出(演示用途)
printf("\n1. 使用writev写入到标准输出:\n");
bytes_written = writev(STDOUT_FILENO, iov, 6);
if (bytes_written == -1) {
perror("writev 失败");
return -1;
}
printf(" 成功写入 %zd 字节\n", bytes_written);

// 写入到文件
printf("\n2. 使用writev写入到文件:\n");
fd = open("writev_output.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建文件失败");
return -1;
}

bytes_written = writev(fd, iov, 6);
if (bytes_written == -1) {
perror("writev 写入文件失败");
close(fd);
return -1;
}
printf(" 成功写入文件 %zd 字节\n", bytes_written);

close(fd);

// 验证写入结果
printf("\n3. 验证写入结果:\n");
fd = open("writev_output.txt", O_RDONLY);
if (fd != -1) {
char buffer&#91;1024];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" 文件内容:\n%s", buffer);
}
close(fd);
unlink("writev_output.txt"); // 清理测试文件
}

return 0;
}

int main() {
return demo_writev_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
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
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>

/**
* 性能测试结构
*/
typedef struct {
const char *name;
double writev_time;
double write_time;
ssize_t total_bytes;
int operation_count;
} performance_test_t;

/**
* 使用writev进行批量写入
*/
ssize_t writev_bulk_write(int fd, const char **messages, int count) {
struct iovec *iov = malloc(count * sizeof(struct iovec));
if (!iov) {
return -1;
}

ssize_t total_written = 0;

// 准备iovec数组
for (int i = 0; i < count; i++) {
iov&#91;i].iov_base = (void*)messages&#91;i];
iov&#91;i].iov_len = strlen(messages&#91;i]);
}

// 执行writev写入
ssize_t result = writev(fd, iov, count);
if (result != -1) {
total_written = result;
}

free(iov);
return total_written;
}

/**
* 使用多次write进行批量写入
*/
ssize_t write_bulk_write(int fd, const char **messages, int count) {
ssize_t total_written = 0;

for (int i = 0; i < count; i++) {
ssize_t result = write(fd, messages&#91;i], strlen(messages&#91;i]));
if (result == -1) {
return -1;
}
total_written += result;
}

return total_written;
}

/**
* 获取当前时间(微秒)
*/
long long get_current_time_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000LL + tv.tv_usec;
}

/**
* 演示writev性能对比
*/
int demo_writev_performance_comparison() {
const int message_count = 1000;
const char *test_messages&#91;1000];
int fd_writev, fd_write;
performance_test_t test_results;

printf("=== writev vs write 性能对比测试 ===\n");

// 准备测试消息
printf("1. 准备测试数据:\n");
char **message_buffers = malloc(message_count * sizeof(char*));
if (!message_buffers) {
perror("分配消息缓冲区失败");
return -1;
}

for (int i = 0; i < message_count; i++) {
message_buffers&#91;i] = malloc(64);
if (message_buffers&#91;i]) {
snprintf(message_buffers&#91;i], 64, "Test message %d: Hello World!\n", i + 1);
test_messages&#91;i] = message_buffers&#91;i];
} else {
printf("分配消息 %d 失败\n", i);
// 清理已分配的缓冲区
for (int j = 0; j < i; j++) {
free(message_buffers&#91;j]);
}
free(message_buffers);
return -1;
}
}

printf(" 准备了 %d 条测试消息\n", message_count);

// 计算总数据量
size_t total_data_size = 0;
for (int i = 0; i < message_count; i++) {
total_data_size += strlen(test_messages&#91;i]);
}
printf(" 总数据量: %zu 字节 (%.2f KB)\n", total_data_size, total_data_size / 1024.0);

// 创建测试文件
printf("\n2. 创建测试文件:\n");
fd_writev = open("writev_test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
fd_write = open("write_test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);

if (fd_writev == -1 || fd_write == -1) {
perror("创建测试文件失败");
if (fd_writev != -1) close(fd_writev);
if (fd_write != -1) close(fd_write);
// 清理消息缓冲区
for (int i = 0; i < message_count; i++) {
free(message_buffers&#91;i]);
}
free(message_buffers);
return -1;
}

printf(" writev测试文件: writev_test.txt\n");
printf(" write测试文件: write_test.txt\n");

// writev性能测试
printf("\n3. writev性能测试:\n");
long long start_time = get_current_time_us();

ssize_t writev_bytes = writev_bulk_write(fd_writev, test_messages, message_count);
if (writev_bytes == -1) {
perror("writev测试失败");
close(fd_writev);
close(fd_write);
unlink("writev_test.txt");
unlink("write_test.txt");
// 清理消息缓冲区
for (int i = 0; i < message_count; i++) {
free(message_buffers&#91;i]);
}
free(message_buffers);
return -1;
}

long long end_time = get_current_time_us();
double writev_time = (end_time - start_time) / 1000.0; // 转换为毫秒

printf(" writev写入字节数: %zd\n", writev_bytes);
printf(" writev耗时: %.3f 毫秒\n", writev_time);

close(fd_writev);

// write性能测试
printf("\n4. write性能测试:\n");
start_time = get_current_time_us();

ssize_t write_bytes = write_bulk_write(fd_write, test_messages, message_count);
if (write_bytes == -1) {
perror("write测试失败");
close(fd_write);
unlink("writev_test.txt");
unlink("write_test.txt");
// 清理消息缓冲区
for (int i = 0; i < message_count; i++) {
free(message_buffers&#91;i]);
}
free(message_buffers);
return -1;
}

end_time = get_current_time_us();
double write_time = (end_time - start_time) / 1000.0; // 转换为毫秒

printf(" write写入字节数: %zd\n", write_bytes);
printf(" write耗时: %.3f 毫秒\n", write_time);

close(fd_write);

// 显示性能对比结果
printf("\n5. 性能对比结果:\n");
printf(" 数据总量: %zu 字节\n", total_data_size);
printf(" writev操作: %d 次系统调用\n", 1);
printf(" write操作: %d 次系统调用\n", message_count);
printf(" writev耗时: %.3f 毫秒\n", writev_time);
printf(" write耗时: %.3f 毫秒\n", write_time);

if (write_time > 0 && writev_time > 0) {
double speedup = write_time / writev_time;
double reduction = (write_time - writev_time) / write_time * 100;

printf(" 性能提升: %.2f 倍\n", speedup);
printf(" 时间减少: %.1f%%\n", reduction);
}

// 验证数据一致性
printf("\n6. 数据一致性验证:\n");
if (writev_bytes == write_bytes) {
printf(" ✓ 写入字节数一致\n");
} else {
printf(" ✗ 写入字节数不一致 (writev: %zd, write: %zd)\n", writev_bytes, write_bytes);
}

// 清理测试文件
unlink("writev_test.txt");
unlink("write_test.txt");

// 清理消息缓冲区
for (int i = 0; i < message_count; i++) {
free(message_buffers&#91;i]);
}
free(message_buffers);

// 显示性能分析
printf("\n=== 性能分析 ===\n");
printf("1. 系统调用开销:\n");
printf(" writev减少了 %d 次系统调用\n", message_count - 1);
printf(" 每次系统调用节省约 %.3f 微秒\n",
((write_time - writev_time) * 1000) / (message_count - 1));

printf("\n2. 适用场景:\n");
printf(" ✓ 大量小数据块写入\n");
printf(" ✓ 网络协议数据组装\n");
printf(" ✓ 日志文件批量写入\n");
printf(" ✓ 数据库事务日志\n");

printf("\n3. 性能优化建议:\n");
printf(" ✓ 合理设置iovec数组大小\n");
printf(" ✓ 避免过于频繁的writev调用\n");
printf(" ✓ 考虑使用异步I/O\n");
printf(" ✓ 监控系统调用性能\n");

return 0;
}

int main() {
return demo_writev_performance_comparison();
}

示例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
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

/**
* HTTP响应结构
*/
typedef struct {
int status_code;
const char *status_text;
const char *content_type;
size_t content_length;
const char *headers&#91;16];
int header_count;
} http_response_t;

/**
* 创建HTTP响应头部
*/
int create_http_headers(http_response_t *response, struct iovec *iov, int max_iov) {
int iov_index = 0;

// 状态行
char *status_line = malloc(64);
if (!status_line) return -1;
snprintf(status_line, 64, "HTTP/1.1 %d %s\r\n",
response->status_code, response->status_text);
iov&#91;iov_index].iov_base = status_line;
iov&#91;iov_index].iov_len = strlen(status_line);
iov_index++;

// Content-Type
char *content_type_header = malloc(64);
if (!content_type_header) {
free(status_line);
return -1;
}
snprintf(content_type_header, 64, "Content-Type: %s\r\n", response->content_type);
iov&#91;iov_index].iov_base = content_type_header;
iov&#91;iov_index].iov_len = strlen(content_type_header);
iov_index++;

// Content-Length
char *content_length_header = malloc(64);
if (!content_length_header) {
free(status_line);
free(content_type_header);
return -1;
}
snprintf(content_length_header, 64, "Content-Length: %zu\r\n", response->content_length);
iov&#91;iov_index].iov_base = content_length_header;
iov&#91;iov_index].iov_len = strlen(content_length_header);
iov_index++;

// 自定义头部
for (int i = 0; i < response->header_count && iov_index < max_iov - 2; i++) {
char *custom_header = strdup(response->headers&#91;i]);
if (custom_header) {
iov&#91;iov_index].iov_base = custom_header;
iov&#91;iov_index].iov_len = strlen(custom_header);
iov_index++;
}
}

// 空行分隔符
char *blank_line = malloc(3);
if (!blank_line) {
// 清理已分配的内存
for (int i = 0; i < iov_index; i++) {
free(iov&#91;i].iov_base);
}
return -1;
}
strcpy(blank_line, "\r\n");
iov&#91;iov_index].iov_base = blank_line;
iov&#91;iov_index].iov_len = 2;
iov_index++;

return iov_index;
}

/**
* 演示网络协议数据组装
*/
int demo_network_protocol_assembly() {
http_response_t response = {0};
struct iovec iov&#91;32];
int iov_count;
int fd;

printf("=== 网络协议数据组装演示 ===\n");

// 初始化HTTP响应
printf("1. 初始化HTTP响应:\n");
response.status_code = 200;
response.status_text = "OK";
response.content_type = "application/json";
response.content_length = 45;
response.header_count = 2;
response.headers&#91;0] = "Server: MyWebServer/1.0";
response.headers&#91;1] = "Cache-Control: no-cache";

printf(" 状态码: %d %s\n", response.status_code, response.status_text);
printf(" 内容类型: %s\n", response.content_type);
printf(" 内容长度: %zu 字节\n", response.content_length);
printf(" 自定义头部: %d 个\n", response.header_count);
for (int i = 0; i < response.header_count; i++) {
printf(" %s\n", response.headers&#91;i]);
}

// 创建响应内容
const char *content = "{\"message\":\"Hello World\",\"status\":\"success\"}";

// 创建HTTP头部
printf("\n2. 创建HTTP头部:\n");
iov_count = create_http_headers(&response, iov, 32);
if (iov_count == -1) {
printf("创建HTTP头部失败\n");
return -1;
}

printf(" 创建了 %d 个头部片段\n", iov_count);

// 添加响应内容
printf("\n3. 添加响应内容:\n");
if (iov_count < 32) {
iov&#91;iov_count].iov_base = (void*)content;
iov&#91;iov_count].iov_len = strlen(content);
iov_count++;
printf(" 添加响应内容: %s\n", content);
}

// 显示完整的HTTP响应
printf("\n4. 完整HTTP响应:\n");
for (int i = 0; i < iov_count; i++) {
printf("%.*s", (int)iov&#91;i].iov_len, (char*)iov&#91;i].iov_base);
}

// 计算总长度
size_t total_length = 0;
for (int i = 0; i < iov_count; i++) {
total_length += iov&#91;i].iov_len;
}
printf("\n总响应长度: %zu 字节\n", total_length);

// 写入到文件(模拟网络发送)
printf("\n5. 写入到文件(模拟网络发送):\n");
fd = open("http_response.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建响应文件失败");
// 清理内存
for (int i = 0; i < iov_count; i++) {
free(iov&#91;i].iov_base);
}
return -1;
}

ssize_t bytes_written = writev(fd, iov, iov_count);
if (bytes_written == -1) {
perror("writev 写入失败");
close(fd);
unlink("http_response.txt");
// 清理内存
for (int i = 0; i < iov_count; i++) {
free(iov&#91;i].iov_base);
}
return -1;
}

printf(" 成功写入 %zd 字节到文件\n", bytes_written);
close(fd);

// 验证写入结果
printf("\n6. 验证写入结果:\n");
fd = open("http_response.txt", O_RDONLY);
if (fd != -1) {
char buffer&#91;512];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" 文件内容 (%zd 字节):\n", bytes_read);
printf("%s", buffer);
}
close(fd);
unlink("http_response.txt");
}

// 清理内存
for (int i = 0; i < iov_count; i++) {
free(iov&#91;i].iov_base);
}

// 显示协议组装优势
printf("\n=== 协议组装优势 ===\n");
printf("1. 零拷贝组装:\n");
printf(" ✓ 避免数据拷贝操作\n");
printf(" ✓ 减少内存使用\n");
printf(" ✓ 提高组装效率\n");

printf("\n2. 原子性保证:\n");
printf(" ✓ 单次系统调用完成\n");
printf(" ✓ 数据完整性保证\n");
printf(" ✓ 避免部分写入问题\n");

printf("\n3. 灵活性:\n");
printf(" ✓ 动态头部组装\n");
printf(" ✓ 可变内容长度\n");
printf(" ✓ 复杂协议支持\n");

return 0;
}

int main() {
return demo_network_protocol_assembly();
}

示例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
#include <sys/uio.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>

/**
* 日志条目结构
*/
typedef struct {
time_t timestamp;
const char *level;
const char *module;
const char *message;
pid_t pid;
pthread_t tid;
} log_entry_t;

/**
* 日志缓冲区管理器
*/
typedef struct {
struct iovec *iov;
int capacity;
int count;
size_t total_size;
int fd;
} log_buffer_t;

/**
* 初始化日志缓冲区
*/
int init_log_buffer(log_buffer_t *buffer, int capacity, const char *filename) {
buffer->iov = malloc(capacity * sizeof(struct iovec));
if (!buffer->iov) {
return -1;
}

buffer->capacity = capacity;
buffer->count = 0;
buffer->total_size = 0;

// 打开日志文件
buffer->fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0644);
if (buffer->fd == -1) {
free(buffer->iov);
return -1;
}

printf("日志缓冲区初始化完成:\n");
printf(" 缓冲区容量: %d 条目\n", capacity);
printf(" 日志文件: %s\n", filename);

return 0;
}

/**
* 格式化日志条目
*/
char* format_log_entry(const log_entry_t *entry) {
char *buffer = malloc(512);
if (!buffer) {
return NULL;
}

struct tm *tm_info = localtime(&entry->timestamp);
char time_str&#91;32];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);

snprintf(buffer, 512, "&#91;%s] &#91;%s] &#91;%s] &#91;PID:%d TID:%lu] %s\n",
time_str, entry->level, entry->module,
entry->pid, (unsigned long)entry->tid, entry->message);

return buffer;
}

/**
* 添加日志条目到缓冲区
*/
int add_log_entry(log_buffer_t *buffer, const log_entry_t *entry) {
if (buffer->count >= buffer->capacity) {
printf("日志缓冲区已满\n");
return -1;
}

char *formatted_entry = format_log_entry(entry);
if (!formatted_entry) {
return -1;
}

buffer->iov&#91;buffer->count].iov_base = formatted_entry;
buffer->iov&#91;buffer->count].iov_len = strlen(formatted_entry);

buffer->total_size += buffer->iov&#91;buffer->count].iov_len;
buffer->count++;

return 0;
}

/**
* 冲刷日志缓冲区
*/
int flush_log_buffer(log_buffer_t *buffer) {
if (buffer->count == 0) {
return 0;
}

printf("冲刷日志缓冲区: %d 条目, %zu 字节\n", buffer->count, buffer->total_size);

ssize_t bytes_written = writev(buffer->fd, buffer->iov, buffer->count);
if (bytes_written == -1) {
perror("写入日志失败");
return -1;
}

printf(" 成功写入 %zd 字节\n", bytes_written);

// 清理内存
for (int i = 0; i < buffer->count; i++) {
free(buffer->iov&#91;i].iov_base);
}

buffer->count = 0;
buffer->total_size = 0;

return 0;
}

/**
* 演示日志系统优化
*/
int demo_log_system_optimization() {
log_buffer_t log_buffer;
const int buffer_capacity = 100;
const char *log_filename = "optimized_log.txt";

printf("=== 日志系统优化演示 ===\n");

// 初始化日志缓冲区
printf("1. 初始化日志缓冲区:\n");
if (init_log_buffer(&log_buffer, buffer_capacity, log_filename) != 0) {
printf("初始化日志缓冲区失败\n");
return -1;
}

// 生成测试日志条目
printf("\n2. 生成测试日志条目:\n");
const char *modules&#91;] = {"Database", "Network", "Cache", "Security", "Monitor"};
const char *levels&#91;] = {"DEBUG", "INFO", "WARN", "ERROR"};
const char *messages&#91;] = {
"Operation completed successfully",
"Data synchronized with remote server",
"Cache hit ratio improved to 95%",
"Security scan completed without issues",
"Performance metrics updated"
};

srand(time(NULL));

// 模拟日志生成
printf(" 生成日志条目:\n");
for (int i = 0; i < 15; i++) {
log_entry_t entry;
entry.timestamp = time(NULL);
entry.level = levels&#91;rand() % 4];
entry.module = modules&#91;rand() % 5];
entry.message = messages&#91;rand() % 5];
entry.pid = getpid();
entry.tid = pthread_self();

if (add_log_entry(&log_buffer, &entry) == 0) {
printf(" &#91;%s] &#91;%s] %s\n", entry.level, entry.module, entry.message);
} else {
printf(" 添加日志条目失败\n");
break;
}

// 模拟批量冲刷
if ((i + 1) % 5 == 0) {
printf(" 批量冲刷日志 (%d 条目)\n", log_buffer.count);
flush_log_buffer(&log_buffer);
}
}

// 最终冲刷
printf("\n3. 最终冲刷剩余日志:\n");
flush_log_buffer(&log_buffer);

// 验证日志文件
printf("\n4. 验证日志文件:\n");
int fd = open(log_filename, O_RDONLY);
if (fd != -1) {
char buffer&#91;2048];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" 日志文件内容 (%zd 字节):\n", bytes_read);

// 只显示前几行
char *line = strtok(buffer, "\n");
int line_count = 0;
while (line && line_count < 10) {
printf(" %s\n", line);
line = strtok(NULL, "\n");
line_count++;
}
if (line) {
printf(" ... (还有更多日志)\n");
}
}
close(fd);
unlink(log_filename);
}

// 清理资源
free(log_buffer.iov);
close(log_buffer.fd);

// 显示优化效果
printf("\n=== 日志系统优化效果 ===\n");
printf("1. 性能提升:\n");
printf(" ✓ 减少系统调用次数\n");
printf(" ✓ 提高批量写入效率\n");
printf(" ✓ 降低I/O开销\n");

printf("\n2. 资源优化:\n");
printf(" ✓ 减少内存分配次数\n");
printf(" ✓ 优化缓冲区使用\n");
printf(" ✓ 提高磁盘I/O效率\n");

printf("\n3. 可靠性提升:\n");
printf(" ✓ 原子性日志写入\n");
printf(" ✓ 错误处理机制\n");
printf(" ✓ 数据完整性保证\n");

return 0;
}

int main() {
return demo_log_system_optimization();
}

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

/**
* 事务操作类型
*/
typedef enum {
TXN_BEGIN = 1,
TXN_COMMIT = 2,
TXN_ROLLBACK = 3,
TXN_INSERT = 4,
TXN_UPDATE = 5,
TXN_DELETE = 6
} txn_operation_t;

/**
* 事务日志条目
*/
typedef struct {
txn_operation_t operation;
time_t timestamp;
unsigned long transaction_id;
const char *table_name;
const char *data;
size_t data_size;
} txn_log_entry_t;

/**
* 事务日志管理器
*/
typedef struct {
int log_fd;
char log_filename&#91;256];
struct iovec *iov_buffer;
int buffer_capacity;
int buffer_count;
size_t buffer_size;
unsigned long current_txn_id;
} txn_log_manager_t;

/**
* 初始化事务日志管理器
*/
int init_txn_log_manager(txn_log_manager_t *manager, const char *log_dir) {
// 创建日志目录
struct stat st = {0};
if (stat(log_dir, &st) == -1) {
if (mkdir(log_dir, 0755) == -1) {
perror("创建日志目录失败");
return -1;
}
}

// 初始化管理器
snprintf(manager->log_filename, sizeof(manager->log_filename),
"%s/transaction.log", log_dir);

manager->log_fd = open(manager->log_filename,
O_CREAT | O_WRONLY | O_APPEND, 0644);
if (manager->log_fd == -1) {
perror("创建事务日志文件失败");
return -1;
}

manager->buffer_capacity = 64;
manager->iov_buffer = malloc(manager->buffer_capacity * sizeof(struct iovec));
if (!manager->iov_buffer) {
close(manager->log_fd);
return -1;
}

manager->buffer_count = 0;
manager->buffer_size = 0;
manager->current_txn_id = time(NULL); // 简单的事务ID生成

printf("事务日志管理器初始化完成:\n");
printf(" 日志文件: %s\n", manager->log_filename);
printf(" 缓冲区容量: %d\n", manager->buffer_capacity);
printf(" 初始事务ID: %lu\n", manager->current_txn_id);

return 0;
}

/**
* 格式化事务日志条目
*/
char* format_txn_log_entry(const txn_log_entry_t *entry) {
char *buffer = malloc(512);
if (!buffer) {
return NULL;
}

struct tm *tm_info = localtime(&entry->timestamp);
char time_str&#91;32];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);

const char *op_names&#91;] = {
"", "BEGIN", "COMMIT", "ROLLBACK", "INSERT", "UPDATE", "DELETE"
};

snprintf(buffer, 512, "&#91;%s] TXN:%lu OP:%s TABLE:%s SIZE:%zu DATA:%s\n",
time_str, entry->transaction_id,
op_names&#91;entry->operation],
entry->table_name ? entry->table_name : "N/A",
entry->data_size,
entry->data ? entry->data : "");

return buffer;
}

/**
* 添加事务日志条目
*/
int add_txn_log_entry(txn_log_manager_t *manager, const txn_log_entry_t *entry) {
if (manager->buffer_count >= manager->buffer_capacity) {
printf("事务日志缓冲区已满,需要冲刷\n");
if (flush_txn_log_buffer(manager) != 0) {
return -1;
}
}

char *formatted_entry = format_txn_log_entry(entry);
if (!formatted_entry) {
return -1;
}

manager->iov_buffer&#91;manager->buffer_count].iov_base = formatted_entry;
manager->iov_buffer&#91;manager->buffer_count].iov_len = strlen(formatted_entry);

manager->buffer_size += manager->iov_buffer&#91;manager->buffer_count].iov_len;
manager->buffer_count++;

printf("添加事务日志条目: TXN:%lu OP:%d\n",
entry->transaction_id, entry->operation);

return 0;
}

/**
* 冲刷事务日志缓冲区
*/
int flush_txn_log_buffer(txn_log_manager_t *manager) {
if (manager->buffer_count == 0) {
return 0;
}

printf("冲刷事务日志缓冲区: %d 条目, %zu 字节\n",
manager->buffer_count, manager->buffer_size);

ssize_t bytes_written = writev(manager->log_fd, manager->iov_buffer, manager->buffer_count);
if (bytes_written == -1) {
perror("写入事务日志失败");
return -1;
}

printf(" 成功写入 %zd 字节事务日志\n", bytes_written);

// 清理内存
for (int i = 0; i < manager->buffer_count; i++) {
free(manager->iov_buffer&#91;i].iov_base);
}

manager->buffer_count = 0;
manager->buffer_size = 0;

// 同步到磁盘
fsync(manager->log_fd);

return 0;
}

/**
* 演示数据库事务日志
*/
int demo_database_transaction_log() {
txn_log_manager_t log_manager;
const char *log_directory = "./txn_logs";

printf("=== 数据库事务日志演示 ===\n");

// 初始化事务日志管理器
printf("1. 初始化事务日志管理器:\n");
if (init_txn_log_manager(&log_manager, log_directory) != 0) {
printf("初始化事务日志管理器失败\n");
return -1;
}

// 模拟数据库事务操作
printf("\n2. 模拟数据库事务操作:\n");

// 事务1: 插入操作
printf(" 事务1: 数据插入操作\n");
txn_log_entry_t insert_entry = {
.operation = TXN_BEGIN,
.timestamp = time(NULL),
.transaction_id = log_manager.current_txn_id++,
.table_name = "users",
.data = "{'name':'John','email':'john@example.com'}",
.data_size = 45
};
add_txn_log_entry(&log_manager, &insert_entry);

txn_log_entry_t insert_data = {
.operation = TXN_INSERT,
.timestamp = time(NULL),
.transaction_id = insert_entry.transaction_id,
.table_name = "users",
.data = "INSERT INTO users VALUES ('John', 'john@example.com')",
.data_size = 52
};
add_txn_log_entry(&log_manager, &insert_data);

txn_log_entry_t commit_entry = {
.operation = TXN_COMMIT,
.timestamp = time(NULL),
.transaction_id = insert_entry.transaction_id,
.table_name = "users",
.data = "Transaction committed successfully",
.data_size = 32
};
add_txn_log_entry(&log_manager, &commit_entry);

// 冲刷第一个事务
flush_txn_log_buffer(&log_manager);

// 事务2: 更新操作
printf("\n 事务2: 数据更新操作\n");
txn_log_entry_t update_entry = {
.operation = TXN_BEGIN,
.timestamp = time(NULL),
.transaction_id = log_manager.current_txn_id++,
.table_name = "orders",
.data = "{'order_id':12345,'status':'processing'}",
.data_size = 42
};
add_txn_log_entry(&log_manager, &update_entry);

txn_log_entry_t update_data = {
.operation = TXN_UPDATE,
.timestamp = time(NULL),
.transaction_id = update_entry.transaction_id,
.table_name = "orders",
.data = "UPDATE orders SET status='shipped' WHERE order_id=12345",
.data_size = 55
};
add_txn_log_entry(&log_manager, &update_data);

// 事务3: 删除操作(在同一事务中)
txn_log_entry_t delete_data = {
.operation = TXN_DELETE,
.timestamp = time(NULL),
.transaction_id = update_entry.transaction_id,
.table_name = "cart_items",
.data = "DELETE FROM cart_items WHERE user_id=1001",
.data_size = 43
};
add_txn_log_entry(&log_manager, &delete_data);

txn_log_entry_t commit_update = {
.operation = TXN_COMMIT,
.timestamp = time(NULL),
.transaction_id = update_entry.transaction_id,
.table_name = "orders",
.data = "Multi-table transaction committed",
.data_size = 35
};
add_txn_log_entry(&log_manager, &commit_update);

// 事务4: 回滚操作
printf("\n 事务3: 事务回滚操作\n");
txn_log_entry_t rollback_entry = {
.operation = TXN_BEGIN,
.timestamp = time(NULL),
.transaction_id = log_manager.current_txn_id++,
.table_name = "inventory",
.data = "Stock adjustment transaction",
.data_size = 28
};
add_txn_log_entry(&log_manager, &rollback_entry);

txn_log_entry_t error_entry = {
.operation = TXN_UPDATE,
.timestamp = time(NULL),
.transaction_id = rollback_entry.transaction_id,
.table_name = "inventory",
.data = "UPDATE inventory SET quantity=-5 WHERE product_id=100", // 错误:负库存
.data_size = 57
};
add_txn_log_entry(&log_manager, &error_entry);

txn_log_entry_t rollback_txn = {
.operation = TXN_ROLLBACK,
.timestamp = time(NULL),
.transaction_id = rollback_entry.transaction_id,
.table_name = "inventory",
.data = "Rollback due to negative stock adjustment",
.data_size = 45
};
add_txn_log_entry(&log_manager, &rollback_txn);

// 最终冲刷
printf("\n3. 最终冲刷剩余日志:\n");
flush_txn_log_buffer(&log_manager);

// 验证日志文件
printf("\n4. 验证事务日志文件:\n");
int fd = open(log_manager.log_filename, O_RDONLY);
if (fd != -1) {
char buffer&#91;2048];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer&#91;bytes_read] = '\0';
printf(" 事务日志内容 (%zd 字节):\n", bytes_read);

// 显示日志内容
char *line = strtok(buffer, "\n");
while (line) {
printf(" %s\n", line);
line = strtok(NULL, "\n");
}
}
close(fd);
}

// 清理资源
if (log_manager.iov_buffer) {
// 清理可能残留的缓冲区条目
for (int i = 0; i < log_manager.buffer_count; i++) {
free(log_manager.iov_buffer&#91;i].iov_base);
}
free(log_manager.iov_buffer);
}
if (log_manager.log_fd != -1) {
close(log_manager.log_fd);
}

// 清理日志文件
unlink(log_manager.log_filename);
rmdir(log_directory);

// 显示事务日志优势
printf("\n=== 事务日志优势 ===\n");
printf("1. ACID特性保证:\n");
printf(" ✓ 原子性: 事务操作要么全部成功要么全部失败\n");
printf(" ✓ 一致性: 系统从一个一致状态转换到另一个一致状态\n");
printf(" ✓ 隔离性: 并发事务之间相互隔离\n");
printf(" ✓ 持久性: 事务提交后对数据的修改是永久性的\n");

printf("\n2. 性能优化:\n");
printf(" ✓ 批量日志写入减少I/O操作\n");
printf(" ✓ 零拷贝数据组装提高效率\n");
printf(" ✓ 缓冲机制减少系统调用开销\n");
printf(" ✓ 异步写入提高响应速度\n");

printf("\n3. 可靠性保障:\n");
printf(" ✓ 完整的事务轨迹记录\n");
printf(" ✓ 快速故障恢复能力\n");
printf(" ✓ 数据一致性验证\n");
printf(" ✓ 审计和合规支持\n");

return 0;
}

int main() {
return demo_database_transaction_log();
}

writev 使用注意事项

系统限制:

IOV_MAX: 系统限制iovec数组的最大长度(通常为1024)

单次写入限制: 一次writev调用写入的总字节数有限制

文件描述符类型: 不是所有文件描述符都支持writev

错误处理:

部分写入: 可能只写入部分数据

错误恢复: 需要处理部分成功的场景

资源清理: 失败时需要清理已分配的资源

性能考虑:

缓冲区大小: 合理设置iovec数组大小

内存对齐: 考虑内存对齐优化

批量操作: 尽量批量处理减少系统调用

安全考虑:

缓冲区验证: 验证iovec缓冲区的有效性

权限检查: 确保有适当的文件写入权限

数据完整性: 确保写入数据的完整性

最佳实践:

合理使用: 在合适的场景下使用writev

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

资源管理: 及时释放分配的资源

性能监控: 监控writev的性能表现

writev vs 相似函数对比

writev vs write:

1
2
3
4
5
6
7
8
9
10
11
// write: 单缓冲区写入
char buffer&#91;1024];
write(fd, buffer, sizeof(buffer));

// writev: 多缓冲区写入
struct iovec iov&#91;3];
iov&#91;0].iov_base = buffer1; iov&#91;0].iov_len = len1;
iov&#91;1].iov_base = buffer2; iov&#91;1].iov_len = len2;
iov&#91;2].iov_base = buffer3; iov&#91;2].iov_len = len3;
writev(fd, iov, 3);

writev vs sendmsg:

1
2
3
4
5
6
7
8
9
10
// writev: 简单的分散写入
writev(sockfd, iov, iovcnt);

// sendmsg: 带控制信息的分散写入
struct msghdr msg = {0};
msg.msg_iov = iov;
msg.msg_iovlen = iovcnt;
msg.msg_control = control_data;
sendmsg(sockfd, &msg, flags);

常见使用场景

1. 网络协议组装:

1
2
3
4
5
6
7
8
9
// HTTP响应组装
struct iovec iov&#91;5];
iov&#91;0].iov_base = status_line; iov&#91;0].iov_len = strlen(status_line);
iov&#91;1].iov_base = headers; iov&#91;1].iov_len = strlen(headers);
iov&#91;2].iov_base = blank_line; iov&#91;2].iov_len = 2;
iov&#91;3].iov_base = content; iov&#91;3].iov_len = content_len;
iov&#91;4].iov_base = trailers; iov&#91;4].iov_len = strlen(trailers);
writev(client_fd, iov, 5);

2. 日志系统优化:

1
2
3
4
5
// 批量日志写入
struct iovec log_entries&#91;100];
// ... 填充日志条目
writev(log_fd, log_entries, entry_count);

3. 数据库事务日志:

1
2
3
4
5
// 事务操作日志
struct iovec txn_records&#91;10];
// ... 填充事务记录
writev(txn_log_fd, txn_records, record_count);

总结

writev 是Linux系统中重要的分散写入函数,提供了:

高效性: 减少系统调用次数,提高I/O性能

灵活性: 支持多个不连续缓冲区的原子写入

标准兼容: 符合POSIX标准,广泛支持

应用场景广: 适用于网络编程、日志系统、数据库等场景

通过合理使用 writev,可以构建高性能的数据处理和传输系统。在实际应用中,需要注意系统限制、错误处理和性能优化等问题。

Advanced C Programming Techniques and Best Practices

Advanced C Programming Techniques and Best Practices - Complete Edition

Table of Contents

Macro Definitions and Preprocessing Techniques

Advanced Memory Management Techniques

Function Pointers and Callback Mechanisms

Data Structure Design

Concurrency and Multithreading

Error Handling and Exception Mechanisms

Performance Optimization Techniques

Debugging and Testing Techniques

Cross-Platform Programming

Secure Programming Practices

Comprehensive Demonstration Examples

Macro Definitions and Preprocessing Techniques

1. Conditional Compilation and Platform Detection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Platform and compiler detection
* Used for conditional compilation and cross-platform compatibility
*/
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM_WINDOWS
#elif defined(__linux__)
#define PLATFORM_LINUX
#elif defined(__APPLE__)
#define PLATFORM_MACOS
#endif

// Compiler detection
#if defined(__GNUC__)
#define COMPILER_GCC
#elif defined(_MSC_VER)
#define COMPILER_MSVC
#endif

// Version detection
#if __STDC_VERSION__ >= 201112L
#define C11_SUPPORTED
#endif

2. Advanced Macro Techniques

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
/**
* Advanced macro collection
* Provides type-safe and convenient macro utilities
*/

// Stringification and concatenation
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define CONCAT(a, b) a##b

// Get array size
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)&#91;0]))

// container_of macro (get container pointer from member pointer)
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })

// Min/Max values
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// Variable swap (without temporary variable)
#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while(0)

// Compile-time assertion
#define STATIC_ASSERT(condition, message) \
typedef char static_assertion_##message&#91;(condition) ? 1 : -1]

// Variadic macros
#define DEBUG_PRINT(fmt, ...) \
fprintf(stderr, "&#91;DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

3. Modern C Language Features

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* C11 modern features
* Leverage new standards to improve code quality
*/

// Generic selection
#define generic_max(a, b) _Generic((a), \
int: max_int, \
float: max_float, \
double: max_double \
)(a, b)

// Static assertion (C11)
_Static_assert(sizeof(int) >= 4, "int must be at least 4 bytes");

// Thread-local storage (C11)
_Thread_local int thread_var;

Advanced Memory Management Techniques

1. Memory Pool Design

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
/**
* Memory pool implementation
* Provides efficient memory allocation and recycling
*/
typedef struct {
void *memory;
size_t size;
size_t used;
size_t block_size;
} memory_pool_t;

memory_pool_t* create_pool(size_t size, size_t block_size) {
memory_pool_t *pool = malloc(sizeof(memory_pool_t));
pool->memory = malloc(size);
pool->size = size;
pool->used = 0;
pool->block_size = block_size;
return pool;
}

void* pool_alloc(memory_pool_t *pool, size_t size) {
if (pool->used + size > pool->size) return NULL;
void *ptr = (char*)pool->memory + pool->used;
pool->used += size;
return ptr;
}

2. Smart Pointer Simulation

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
/**
* Smart pointer simulation system
* Implements reference-counted automatic memory management
*/

#include <stdatomic.h>

typedef struct {
void *ptr;
void (*deleter)(void*);
atomic_int *ref_count;
} smart_ptr_t;

smart_ptr_t make_smart_ptr(void *ptr, void (*deleter)(void*)) {
smart_ptr_t sp = {ptr, deleter, malloc(sizeof(atomic_int))};
atomic_init(sp.ref_count, 1);
return sp;
}

smart_ptr_t smart_ptr_copy(smart_ptr_t sp) {
atomic_fetch_add(sp.ref_count, 1);
return sp;
}

void smart_ptr_free(smart_ptr_t *sp) {
if (atomic_fetch_sub(sp->ref_count, 1) == 1) {
sp->deleter(sp->ptr);
free(sp->ref_count);
}
}

3. Memory Alignment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Memory alignment utilities
* Ensures proper alignment of data structures in memory
*/

// C11 alignment
_Alignas(16) char aligned_buffer&#91;256];

// Manual alignment
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
#define IS_ALIGNED(x, align) (((x) & ((align) - 1)) == 0)

void* aligned_malloc(size_t size, size_t alignment) {
void *ptr = malloc(size + alignment - 1 + sizeof(void*));
if (!ptr) return NULL;

void **aligned_ptr = (void**)(((uintptr_t)ptr + sizeof(void*) + alignment - 1) & ~(alignment - 1));
aligned_ptr&#91;-1] = ptr;
return aligned_ptr;
}

Function Pointers and Callback Mechanisms

1. Object-Oriented Style Programming

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Virtual function table simulation
* Implements object-oriented programming in C
*/

// Virtual function table simulation
typedef struct {
void (*destroy)(void *self);
void (*print)(void *self);
int (*compare)(void *self, void *other);
} vtable_t;

typedef struct {
vtable_t *vtable;
// Concrete data
} object_t;

// Polymorphic invocation
#define CALL_METHOD(obj, method, ...) \
((obj)->vtable->method((obj), ##__VA_ARGS__))

2. State Machine Implementation

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
/**
* State machine implementation
* Provides flexible state management mechanism
*/

typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_PAUSED,
STATE_STOPPED
} state_t;

typedef struct {
state_t current_state;
int (*handlers&#91;4])(void *context, int event);
} state_machine_t;

int handle_idle(void *context, int event) {
switch (event) {
case EVENT_START:
return STATE_RUNNING;
default:
return STATE_IDLE;
}
}

3. Plugin System Design

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Plugin system design
* Supports dynamic loading and functionality extension
*/

typedef struct {
const char *name;
int version;
int (*init)(void);
void (*cleanup)(void);
void* (*create_instance)(void);
} plugin_interface_t;

// Dynamic plugin loading
#ifdef _WIN32
#include <windows.h>
#define LOAD_PLUGIN(name) LoadLibrary(name)
#define GET_SYMBOL(handle, name) GetProcAddress(handle, name)
#else
#include <dlfcn.h>
#define LOAD_PLUGIN(name) dlopen(name, RTLD_LAZY)
#define GET_SYMBOL(handle, name) dlsym(handle, name)
#endif

Data Structure Design

1. Linked List Implementation

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
/**
* Doubly linked list implementation
* Provides efficient insertion and deletion operations
*/

typedef struct list_node {
void *data;
struct list_node *next;
struct list_node *prev;
} list_node_t;

typedef struct {
list_node_t head;
size_t size;
void (*destructor)(void*);
} list_t;

// Doubly linked list operations
void list_insert_after(list_t *list, list_node_t *node, void *data) {
list_node_t *new_node = malloc(sizeof(list_node_t));
new_node->data = data;
new_node->next = node->next;
new_node->prev = node;

if (node->next) node->next->prev = new_node;
node->next = new_node;
list->size++;
}

2. Hash Table Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Hash table implementation
* Provides fast key-value storage and retrieval
*/

typedef struct hash_entry {
char *key;
void *value;
struct hash_entry *next;
} hash_entry_t;

typedef struct {
hash_entry_t **buckets;
size_t bucket_count;
size_t size;
unsigned int (*hash_func)(const char*);
} hash_table_t;

unsigned int djb2_hash(const char *str) {
unsigned int hash = 5381;
int c;
while ((c = *str++)) hash = ((hash << 5) + hash) + c;
return hash;
}

3. Ring Buffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Ring buffer implementation
* Suitable for producer-consumer patterns
*/

typedef struct {
char *buffer;
size_t size;
size_t read_pos;
size_t write_pos;
int full;
} ring_buffer_t;

int ring_buffer_write(ring_buffer_t *rb, const char *data, size_t len) {
size_t available = rb->size - ring_buffer_size(rb);
if (len > available) return -1;

for (size_t i = 0; i < len; i++) {
rb->buffer&#91;rb->write_pos] = data&#91;i];
rb->write_pos = (rb->write_pos + 1) % rb->size;
if (rb->write_pos == rb->read_pos) rb->full = 1;
}
return len;
}

Concurrency and Multithreading

1. Thread-Safe Data Structures

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
/**
* Thread-safe counter
* Provides atomic operations and conditional waiting
*/

#include <pthread.h>

typedef struct {
int value;
pthread_mutex_t mutex;
pthread_cond_t cond;
} thread_safe_counter_t;

void counter_increment(thread_safe_counter_t *counter) {
pthread_mutex_lock(&counter->mutex);
counter->value++;
pthread_cond_signal(&counter->cond);
pthread_mutex_unlock(&counter->mutex);
}

int counter_wait_for(thread_safe_counter_t *counter, int target) {
pthread_mutex_lock(&counter->mutex);
while (counter->value < target) {
pthread_cond_wait(&counter->cond, &counter->mutex);
}
pthread_mutex_unlock(&counter->mutex);
return counter->value;
}

2. Read-Write Lock Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Read-write lock implementation
* Supports multiple readers/single writer concurrency control
*/

typedef struct {
pthread_mutex_t mutex;
pthread_cond_t read_cond;
pthread_cond_t write_cond;
int readers;
int writers;
int waiting_writers;
} rwlock_t;

void rwlock_rdlock(rwlock_t *rwlock) {
pthread_mutex_lock(&rwlock->mutex);
while (rwlock->writers > 0 || rwlock->waiting_writers > 0) {
pthread_cond_wait(&rwlock->read_cond, &rwlock->mutex);
}
rwlock->readers++;
pthread_mutex_unlock(&rwlock->mutex);
}

3. Lock-Free Programming

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Lock-free programming tools
* Implements high-performance concurrency using atomic operations
*/

#include <stdatomic.h>

typedef struct {
atomic_int value;
} atomic_counter_t;

void atomic_counter_increment(atomic_counter_t *counter) {
atomic_fetch_add(&counter->value, 1);
}

int atomic_counter_get(atomic_counter_t *counter) {
return atomic_load(&counter->value);
}

Error Handling and Exception Mechanisms

1. Error Code System

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
/**
* Error code system
* Provides structured error handling mechanism
*/

typedef enum {
ERROR_SUCCESS = 0,
ERROR_INVALID_PARAM = -1,
ERROR_OUT_OF_MEMORY = -2,
ERROR_FILE_NOT_FOUND = -3,
ERROR_PERMISSION_DENIED = -4
} error_code_t;

#define RETURN_ON_ERROR(expr) do { \
error_code_t err = (expr); \
if (err != ERROR_SUCCESS) return err; \
} while(0)

// Context-aware error handling
typedef struct {
error_code_t code;
const char *message;
const char *file;
int line;
} error_context_t;

2. Exception Simulation Mechanism

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
/**
* Exception simulation mechanism
* Implements exception handling using setjmp/longjmp
*/

#include <setjmp.h>

typedef struct {
jmp_buf jump_buffer;
int error_code;
const char *error_message;
} exception_context_t;

static __thread exception_context_t *current_exception = NULL;

#define TRY \
do { \
exception_context_t __exception_ctx; \
__exception_ctx.error_code = 0; \
if (setjmp(__exception_ctx.jump_buffer) == 0) { \
current_exception = &__exception_ctx;

#define CATCH(error_var) \
} else { \
error_var = current_exception->error_code;

#define END_TRY \
} \
current_exception = NULL; \
} while(0);

#define THROW(code, message) \
do { \
if (current_exception) { \
current_exception->error_code = code; \
current_exception->error_message = message; \
longjmp(current_exception->jump_buffer, 1); \
} \
} while(0)

3. Resource Management (RAII)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* RAII resource management
* Ensures automatic resource cleanup
*/

typedef struct {
void *resource;
void (*cleanup)(void*);
} raii_guard_t;

#define RAII_VAR(type, name, init, cleanup_func) \
type name = init; \
raii_guard_t __guard_##name = {&name, (void(*)(void*))cleanup_func}; \
__attribute__((cleanup(raii_cleanup))) raii_guard_t *__raii_##name = &__guard_##name;

static void raii_cleanup(raii_guard_t **guard) {
if ((*guard)->resource && (*guard)->cleanup) {
(*guard)->cleanup((*guard)->resource);
}
}

Performance Optimization Techniques

1. Cache-Friendly Data Structures

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Cache-friendly data structures
* Optimizes memory layout for better cache hit rates
*/

// Struct packing optimization
struct __attribute__((packed)) packed_struct {
char a;
int b;
short c;
};

// Cache line alignment
#define CACHE_LINE_SIZE 64
struct __attribute__((aligned(CACHE_LINE_SIZE))) cache_aligned_struct {
int data&#91;16];
};

2. Branch Prediction Optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Branch prediction optimization
* Uses compiler hints to improve execution efficiency
*/

// Static branch prediction
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

void optimized_function(int *array, size_t size) {
if (unlikely(size == 0)) return;

for (size_t i = 0; likely(i < size); i++) {
process_element(array&#91;i]);
}
}

3. Inline Assembly Optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Inline assembly optimization
* Directly uses CPU instructions for performance gains
*/

// Read Time-Stamp Counter
static inline uint64_t rdtsc(void) {
uint32_t lo, hi;
__asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}

// Memory barrier
#define MEMORY_BARRIER() __asm__ __volatile__("" ::: "memory")

4. SIMD Optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* SIMD optimization
* Leverages vector instructions for parallel data processing
*/

#ifdef __SSE2__
#include <emmintrin.h>

void vector_add(float *a, float *b, float *result, size_t n) {
size_t i = 0;
for (; i + 4 <= n; i += 4) {
__m128 va = _mm_load_ps(&a&#91;i]);
__m128 vb = _mm_load_ps(&b&#91;i]);
__m128 vr = _mm_add_ps(va, vb);
_mm_store_ps(&result&#91;i], vr);
}
// Process remaining elements
for (; i < n; i++) {
result&#91;i] = a&#91;i] + b&#91;i];
}
}
#endif

Debugging and Testing Techniques

1. Debug Macros

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
/**
* Debugging utility macros
* Provides convenient debugging and profiling capabilities
*/

#ifdef DEBUG
#define DBG_PRINT(fmt, ...) \
fprintf(stderr, "&#91;DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define ASSERT(condition) \
do { \
if (!(condition)) { \
fprintf(stderr, "Assertion failed: %s at %s:%d\n", \
#condition, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
#else
#define DBG_PRINT(fmt, ...) do {} while(0)
#define ASSERT(condition) do {} while(0)
#endif

// Performance timing
#define TIME_IT(code, result_var) \
do { \
clock_t start = clock(); \
code; \
result_var = ((double)(clock() - start)) / CLOCKS_PER_SEC; \
} while(0)

2. Unit Testing Framework

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
/**
* Unit testing framework
* Provides structured testing support
*/

typedef struct {
const char *name;
void (*test_func)(void);
int passed;
int failed;
} test_case_t;

#define TEST_CASE(name) \
static void test_##name(void); \
static test_case_t test_case_##name = {#name, test_##name, 0, 0}; \
static void test_##name(void)

#define ASSERT_EQ(expected, actual) \
do { \
if ((expected) != (actual)) { \
fprintf(stderr, "Assertion failed: %s != %s at %s:%d\n", \
#expected, #actual, __FILE__, __LINE__); \
current_test->failed++; \
} else { \
current_test->passed++; \
} \
} while(0)

3. Memory Leak Detection

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
/**
* Memory leak detection
* Tracks memory allocations and deallocations
*/

#ifdef DEBUG_MEMORY
static size_t total_allocated = 0;
static size_t allocation_count = 0;

void* debug_malloc(size_t size, const char *file, int line) {
void *ptr = malloc(size + sizeof(size_t));
if (ptr) {
*(size_t*)ptr = size;
total_allocated += size;
allocation_count++;
printf("ALLOC: %zu bytes at %s:%d\n", size, file, line);
return (char*)ptr + sizeof(size_t);
}
return NULL;
}

void debug_free(void *ptr, const char *file, int line) {
if (ptr) {
size_t *size_ptr = (size_t*)((char*)ptr - sizeof(size_t));
total_allocated -= *size_ptr;
allocation_count--;
printf("FREE: %zu bytes at %s:%d\n", *size_ptr, file, line);
free(size_ptr);
}
}

#define malloc(size) debug_malloc(size, __FILE__, __LINE__)
#define free(ptr) debug_free(ptr, __FILE__, __LINE__)
#endif

Cross-Platform Programming

1. Platform Abstraction Layer

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
/**
* Platform abstraction layer
* Provides unified cross-platform interfaces
*/

// Thread abstraction
#ifdef _WIN32
#include <windows.h>
typedef HANDLE thread_t;
typedef CRITICAL_SECTION mutex_t;
#define THREAD_CREATE(thread, func, arg) \
(thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, NULL))
#define THREAD_JOIN(thread) WaitForSingleObject(thread, INFINITE)
#define MUTEX_INIT(mutex) InitializeCriticalSection(mutex)
#define MUTEX_LOCK(mutex) EnterCriticalSection(mutex)
#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(mutex)
#else
#include <pthread.h>
typedef pthread_t thread_t;
typedef pthread_mutex_t mutex_t;
#define THREAD_CREATE(thread, func, arg) pthread_create(&thread, NULL, func, arg)
#define THREAD_JOIN(thread) pthread_join(thread, NULL)
#define MUTEX_INIT(mutex) pthread_mutex_init(mutex, NULL)
#define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex)
#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex)
#endif

2. File Path Handling

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
/**
* File path handling
* Provides cross-platform path operations
*/

#ifdef _WIN32
#define PATH_SEPARATOR '\\'
#define PATH_SEPARATOR_STR "\\"
#else
#define PATH_SEPARATOR '/'
#define PATH_SEPARATOR_STR "/"
#endif

char* join_path(const char *dir, const char *file) {
size_t dir_len = strlen(dir);
size_t file_len = strlen(file);
char *result = malloc(dir_len + file_len + 2);

strcpy(result, dir);
if (dir&#91;dir_len - 1] != PATH_SEPARATOR) {
strcat(result, PATH_SEPARATOR_STR);
}
strcat(result, file);
return result;
}

3. Endianness Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Endianness handling
* Ensures data correctness during network transmission
*/

// Network byte order conversion
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define IS_BIG_ENDIAN 1
#else
#define IS_BIG_ENDIAN 0
#endif

static inline uint32_t swap_endian_32(uint32_t val) {
return ((val & 0x000000FF) << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
((val & 0xFF000000) >> 24);
}

#define hton32(x) (IS_BIG_ENDIAN ? (x) : swap_endian_32(x))
#define ntoh32(x) hton32(x)

Secure Programming Practices

1. Buffer Overflow Protection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Buffer overflow protection
* Provides safe string manipulation functions
*/

// Safe string operations
size_t safe_strncpy(char *dest, size_t dest_size, const char *src, size_t count) {
if (dest_size == 0) return 0;

size_t copy_len = (count < dest_size - 1) ? count : dest_size - 1;
memcpy(dest, src, copy_len);
dest&#91;copy_len] = '\0';
return copy_len;
}

// Formatted string safety checks
#define SAFE_PRINTF(buffer, size, format, ...) \
do { \
int __result = snprintf(buffer, size, format, ##__VA_ARGS__); \
if (__result < 0 || (size_t)__result >= size) { \
/* Handle overflow */ \
buffer&#91;size - 1] = '\0'; \
} \
} while(0)

2. Input Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Input validation
* Prevents security issues caused by malicious input
*/

// Integer overflow check
static inline int safe_add(int a, int b, int *result) {
if ((b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)) {
return -1; // Overflow
}
*result = a + b;
return 0;
}

// Pointer validation
#define VALIDATE_PTR(ptr) \
do { \
if (!(ptr)) { \
return ERROR_INVALID_PARAM; \
} \
} while(0)

3. Secure Random Numbers

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
/**
* Secure random number generation
* Provides cryptographically secure random numbers
*/

#include <time.h>
#include <stdlib.h>

// Cryptographically secure random numbers (requires platform support)
#ifdef __linux__
#include <sys/random.h>
int secure_random_bytes(void *buf, size_t len) {
return getrandom(buf, len, 0) == (ssize_t)len ? 0 : -1;
}
#else
// Simple pseudo-random number generator
static unsigned long long rand_state = 1;

void srand64(unsigned long long seed) {
rand_state = seed;
}

unsigned long long rand64(void) {
rand_state = rand_state * 6364136223846793005ULL + 1;
return rand_state;
}
#endif

Comprehensive Demonstration Examples

Event-Driven Architecture Demo

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
/**
* Event-Driven Architecture - Event Type Definitions
* Used for building flexible event handling systems
*/
typedef enum {
EVENT_NONE = 0,
EVENT_TIMER,
EVENT_NETWORK,
EVENT_USER,
EVENT_SYSTEM
} event_type_t;

/**
* Event structure
* Contains event type, timestamp, and user data
*/
typedef struct {
event_type_t type;
uint64_t timestamp;
void *data;
size_t data_size;
} event_t;

/**
* Event handler function pointer type
* @param event Event pointer
* @param context User context
* @return Processing result
*/
typedef int (*event_handler_t)(event_t *event, void *context);

/**
* Event listener structure
* Stores event type and corresponding handler
*/
typedef struct {
event_type_t type;
event_handler_t handler;
void *context;
int priority; // Processing priority
} event_listener_t;

/**
* Event loop structure
* Manages event queue and listeners
*/
typedef struct {
event_listener_t *listeners;
size_t listener_count;
size_t listener_capacity;

event_t *event_queue;
size_t queue_head;
size_t queue_tail;
size_t queue_size;
size_t queue_capacity;

int running;
pthread_mutex_t mutex;
pthread_cond_t cond;
} event_loop_t;

// &#91;Implementation code continues...]

Lock-Free Queue Implementation

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
/**
* Lock-free queue implementation
* Implements thread-safe lock-free queue using CAS operations
*/

#include <stdatomic.h>

/**
* Queue node structure
*/
typedef struct queue_node {
void *data;
_Atomic(struct queue_node*) next;
} queue_node_t;

/**
* Lock-free queue structure
*/
typedef struct {
_Atomic(queue_node_t*) head;
_Atomic(queue_node_t*) tail;
atomic_size_t size;
} lockfree_queue_t;

// &#91;Implementation code continues...]

Cache-Friendly Data Structures

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
/**
* Cache-friendly data structure implementation
* Optimizes memory layout to improve cache hit rates
*/

/**
* Cache line size definition
*/
#define CACHE_LINE_SIZE 64

/**
* Cache alignment macro
*/
#define CACHE_ALIGNED __attribute__((aligned(CACHE_LINE_SIZE)))

/**
* SoA (Structure of Arrays) vector structure
* Separates related data storage to improve cache efficiency
*/
typedef struct {
float *x; // X-coordinate array
float *y; // Y-coordinate array
float *z; // Z-coordinate array
int *id; // ID array
size_t capacity; // Capacity
size_t size; // Current size
char padding&#91;CACHE_LINE_SIZE - sizeof(size_t)*2 - sizeof(char*)*4]; // Padding to cache line boundary
} soa_vector_t;

// &#91;Implementation code continues...]

Secure String Operations Library

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Secure string operations library
* Provides buffer-overflow-safe string functions
*/

#include <string.h>
#include <stdio.h>
#include <stdarg.h>

/**
* Secure string structure
* Contains length information to prevent overflows
*/
typedef struct {
char *data;
size_t length;
size_t capacity;
int is_secure; // Whether security checks are enabled
} secure_string_t;

// &#91;Implementation code continues...]

Comprehensive Demo Function

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
/**
* Event-Driven Architecture Demo Example
*/
void demo_event_driven_architecture() {
printf("=== Event-Driven Architecture Demo ===\n");

// Create event loop
event_loop_t *loop = event_loop_create(100);
if (!loop) {
printf("Failed to create event loop\n");
return;
}

// Add event listeners
event_loop_add_listener(loop, EVENT_TIMER, timer_handler, NULL, 0);
event_loop_add_listener(loop, EVENT_NETWORK, network_handler, NULL, 0);
event_loop_add_listener(loop, EVENT_USER, user_handler, NULL, 0);

// Start event loop thread
pthread_t loop_thread;
pthread_create(&loop_thread, NULL, (void*(*)(void*))event_loop_run, loop);

// Post some test events
for (int i = 0; i < 5; i++) {
event_t event = {0};
event.timestamp = time(NULL);

// Post different event types
switch (i % 3) {
case 0:
event.type = EVENT_TIMER;
printf("Posting timer event %d\n", i);
break;
case 1: {
event.type = EVENT_NETWORK;
const char *msg = "Hello Network!";
event.data = strdup(msg);
event.data_size = strlen(msg);
printf("Posting network event %d\n", i);
break;
}
case 2:
event.type = EVENT_USER;
event.data = malloc(sizeof(int));
*(int*)event.data = i;
event.data_size = sizeof(int);
printf("Posting user event %d\n", i);
break;
}

event_loop_post(loop, &event);
sleep(1);
}

// Cleanup event data
sleep(2);

// Stop and destroy event loop
event_loop_stop(loop);
pthread_join(loop_thread, NULL);
event_loop_destroy(loop);

printf("=== Demo Complete ===\n\n");
}

// &#91;Other demo functions continue...]

Appendix: Best Practices Summary

Coding Standards

​​Naming Conventions​​: Use clear names, avoid abbreviations

​​Comment Style​​: Use Doxygen-style comments

​​Error Handling​​: Always check return values

​​Memory Management​​: Follow RAII principles

​​Thread Safety​​: Clearly identify thread-safe functions

Performance Optimization Principles

​​Measure Before Optimizing​​: Use profiling tools

​​Algorithm First​​: Choose appropriate data structures and algorithms

​​Avoid Premature Optimization​​: Maintain code readability

​​Cache-Friendly​​: Consider data locality

​​Compiler Optimization​​: Use compiler optimization flags appropriately

Secure Coding Principles

​​Input Validation​​: Never trust external input

​​Bounds Checking​​: Prevent buffer overflows

​​Principle of Least Privilege​​: Use minimum necessary privileges

​​Safe Functions​​: Use secure string functions

​​Code Reviews​​: Conduct regular security code reviews

This comprehensive guide to advanced C programming techniques covers all important aspects from basic macros to complex concurrent programming, providing rich code examples and best practices to help developers write high-quality, high-performance, and secure C code.

capset系统调用及示例

capget - 获取进程能力

函数介绍

capget系统调用用于获取进程的能力状态信息。能力是一种细粒度的权限控制机制,将传统的超级用户权限分解为独立的权限单元。

函数原型

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

int capget(cap_user_header_t hdrp, cap_user_data_t datap);

功能

获取指定进程的能力集,包括有效能力、允许能力和可继承能力。

参数

  • cap_user_header_t hdrp: 指向头部结构的指针,包含版本和进程ID

  • cap_user_data_t datap: 指向能力数据结构的指针

返回值

  • 成功时返回0

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

相似函数

  • capset(): 设置进程能力

  • prctl(): 进程控制函数

示例代码

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

// 系统调用包装
static int capget_wrapper(cap_user_header_t hdrp, cap_user_data_t datap) {
return syscall(__NR_capget, hdrp, datap);
}

// 打印能力位图
void print_capabilities(const char *label, __u32 caps) {
printf("%s: 0x%08x (", label, caps);

if (caps == 0) {
printf("none");
} else {
int first = 1;
for (int i = 0; i < 32; i++) {
if (caps & (1 << i)) {
if (!first) printf(" ");
first = 0;
switch (i) {
case 0: printf("CAP_CHOWN"); break;
case 1: printf("CAP_DAC_OVERRIDE"); break;
case 2: printf("CAP_DAC_READ_SEARCH"); break;
case 3: printf("CAP_FOWNER"); break;
case 4: printf("CAP_FSETID"); break;
case 5: printf("CAP_KILL"); break;
case 6: printf("CAP_SETGID"); break;
case 7: printf("CAP_SETUID"); break;
case 8: printf("CAP_SETPCAP"); break;
case 9: printf("CAP_LINUX_IMMUTABLE"); break;
case 10: printf("CAP_NET_BIND_SERVICE"); break;
case 11: printf("CAP_NET_BROADCAST"); break;
case 12: printf("CAP_NET_ADMIN"); break;
case 13: printf("CAP_SYS_MODULE"); break;
case 14: printf("CAP_SYS_RAWIO"); break;
case 15: printf("CAP_SYS_CHROOT"); break;
case 16: printf("CAP_SYS_PTRACE"); break;
case 17: printf("CAP_SYS_PACCT"); break;
case 18: printf("CAP_SYS_ADMIN"); break;
case 19: printf("CAP_SYS_BOOT"); break;
case 20: printf("CAP_SYS_NICE"); break;
case 21: printf("CAP_SYS_RESOURCE"); break;
case 22: printf("CAP_SYS_TIME"); break;
case 23: printf("CAP_SYS_TTY_CONFIG"); break;
case 24: printf("CAP_MKNOD"); break;
case 25: printf("CAP_LEASE"); break;
case 26: printf("CAP_AUDIT_WRITE"); break;
case 27: printf("CAP_AUDIT_CONTROL"); break;
case 28: printf("CAP_SETFCAP"); break;
case 29: printf("CAP_MAC_OVERRIDE"); break;
case 30: printf("CAP_MAC_ADMIN"); break;
case 31: printf("CAP_SYSLOG"); break;
default: printf("CAP_%d", i); break;
}
}
}
}
printf(")\n");
}

int main() {
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data&#91;2];

printf("=== Capget 函数示例 ===\n");
printf("当前进程 PID: %d\n", getpid());
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 设置头部信息
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0; // 当前进程

// 获取能力信息
if (capget_wrapper(&hdr, data) == -1) {
perror("capget 失败");
exit(EXIT_FAILURE);
}

printf("\n进程能力信息:\n");
printf("能力版本: 0x%08x\n", hdr.version);
printf("进程 PID: %d\n", hdr.pid);

// 显示能力集
printf("\n能力集详情:\n");
print_capabilities("有效能力 (Effective)", data&#91;0].effective);
print_capabilities("允许能力 (Permitted)", data&#91;0].permitted);
print_capabilities("可继承能力 (Inheritable)", data&#91;0].inheritable);

// 分析当前能力状态
printf("\n能力状态分析:\n");
if (geteuid() == 0) {
printf("✓ 当前进程是 root 用户\n");
if (data&#91;0].effective == 0 && data&#91;0].permitted == 0) {
printf(" 但所有能力都被丢弃\n");
} else {
printf(" 拥有完整的 root 能力\n");
}
} else {
printf("✓ 当前进程是非特权用户\n");
if (data&#91;0].effective == 0) {
printf(" 没有有效能力\n");
} else {
printf(" 拥有一些特定能力\n");
}
}

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

// 无效版本号
struct __user_cap_header_struct bad_hdr;
bad_hdr.version = 0x12345678;
bad_hdr.pid = 0;

if (capget_wrapper(&bad_hdr, data) == -1) {
if (errno == EINVAL) {
printf("无效版本号错误处理正确: %s\n", strerror(errno));
}
}

// 无效指针
hdr.version = _LINUX_CAPABILITY_VERSION_3;
if (capget_wrapper(&hdr, NULL) == -1) {
if (errno == EFAULT) {
printf("无效指针错误处理正确: %s\n", strerror(errno));
}
}

return 0;
}

  1. capset - 设置进程能力

函数介绍

capset系统调用用于设置调用进程的能力状态。它允许进程修改自己的能力集,但只能降低或保持当前能力,不能提升能力。

函数原型

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

int capset(cap_user_header_t hdrp, const cap_user_data_t datap);

功能

设置调用进程的能力状态,包括有效能力、允许能力和可继承能力。

参数

  • cap_user_header_t hdrp: 指向头部结构的指针

  • const cap_user_data_t datap: 指向能力数据结构的指针

返回值

  • 成功时返回0

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

相似函数

  • capget(): 获取进程能力

  • prctl(): 进程控制函数

示例代码

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

// 系统调用包装
static int capget_wrapper(cap_user_header_t hdrp, cap_user_data_t datap) {
return syscall(__NR_capget, hdrp, datap);
}

static int capset_wrapper(cap_user_header_t hdrp, const cap_user_data_t datap) {
return syscall(__NR_capset, hdrp, datap);
}

// 检查是否具有特定能力
int has_capability(__u32 caps, int cap) {
return (caps & (1 << cap)) != 0;
}

int main() {
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data&#91;2];

printf("=== Capset 函数示例 ===\n");
printf("当前进程 PID: %d\n", getpid());
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 获取当前能力
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0;

if (capget_wrapper(&hdr, data) == -1) {
perror("capget 失败");
exit(EXIT_FAILURE);
}

printf("\n修改前的能力状态:\n");
printf(" 有效能力: 0x%08x\n", data&#91;0].effective);
printf(" 允许能力: 0x%08x\n", data&#91;0].permitted);
printf(" 继承能力: 0x%08x\n", data&#91;0].inheritable);

// 演示能力丢弃(只能丢弃,不能增加)
printf("\n能力修改演示:\n");

// 保存原始状态
__u32 original_effective = data&#91;0].effective;
__u32 original_permitted = data&#91;0].permitted;

// 检查是否具有某些能力
if (has_capability(data&#91;0].effective, CAP_CHOWN)) {
printf(" 当前具有 CAP_CHOWN 能力\n");

// 尝试丢弃该能力
data&#91;0].effective &= ~(1 << CAP_CHOWN);

if (capset_wrapper(&hdr, data) == -1) {
printf(" capset 丢弃能力失败: %s\n", strerror(errno));
} else {
printf(" 成功丢弃 CAP_CHOWN 能力\n");
}
} else {
printf(" 当前不具有 CAP_CHOWN 能力\n");
}

// 再次获取能力状态验证
if (capget_wrapper(&hdr, data) == -1) {
perror("重新获取能力失败");
} else {
printf("\n修改后的有效能力: 0x%08x\n", data&#91;0].effective);

// 恢复原始状态
data&#91;0].effective = original_effective;
data&#91;0].permitted = original_permitted;

if (capset_wrapper(&hdr, data) == -1) {
printf("恢复原始状态失败: %s\n", strerror(errno));
} else {
printf("已恢复原始能力状态\n");
}
}

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

// 权限不足错误
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0;

if (capget_wrapper(&hdr, data) == 0) {
// 尝试设置不允许的能力(会失败)
__u32 temp_effective = data&#91;0].effective;
data&#91;0].effective |= (1 << CAP_SYS_MODULE); // 尝试增加能力

if (capset_wrapper(&hdr, data) == -1) {
if (errno == EPERM) {
printf("权限不足错误处理正确: %s\n", strerror(errno));
printf("说明: 不能通过 capset 提升能力\n");
}
}

// 恢复状态
data&#91;0].effective = temp_effective;
}

// 无效参数错误
hdr.version = 0x12345678; // 无效版本
if (capset_wrapper(&hdr, data) == -1) {
if (errno == EINVAL) {
printf("无效参数错误处理正确: %s\n", strerror(errno));
}
}

// 实际应用场景演示
printf("\n实际应用场景:\n");
printf("1. 网络服务器安全降权:\n");
printf(" - 启动时绑定特权端口(需要 CAP_NET_BIND_SERVICE)\n");
printf(" - 完成绑定后丢弃该能力\n");
printf(" - 切换到非特权用户运行\n\n");

printf("2. 最小权限原则:\n");
printf(" - 只保留执行任务必需的能力\n");
printf(" - 降低被攻击时的安全风险\n\n");

printf("3. 容器安全:\n");
printf(" - 限制容器内进程的能力\n");
printf(" - 防止容器逃逸攻击\n");

return 0;
}

chdir系统调用及示例

chdir - 改变当前工作目录

函数介绍

chdir系统调用用于改变进程的当前工作目录。成功调用后,进程的所有相对路径操作都基于新的工作目录进行。

函数原型

1
2
3
4
#include <unistd.h>

int chdir(const char *path);

功能

改变进程当前工作目录到指定路径。

参数

  • const char *path: 目标目录的路径名(可以是相对路径或绝对路径)

返回值

  • 成功时返回0

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

  • EACCES: 权限不足

  • EIO: I/O错误

  • ELOOP: 符号链接循环

  • ENAMETOOLONG: 路径名过长

  • ENOENT: 目录不存在

  • ENOTDIR: 路径不是目录

  • EROFS: 目录在只读文件系统上

相似函数

  • fchdir(): 通过文件描述符改变当前工作目录

  • getcwd(): 获取当前工作目录

示例代码

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
426
427
428
429
430
431
432
433
434
435
436
437
438
439
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>

int main() {
char buffer&#91;PATH_MAX];
char original_dir&#91;PATH_MAX];

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

// 保存原始目录
if (getcwd(original_dir, sizeof(original_dir)) == NULL) {
perror("获取原始目录失败");
exit(EXIT_FAILURE);
}
printf("原始工作目录: %s\n", original_dir);

// 示例1: 基本的目录切换操作
printf("\n示例1: 基本的目录切换操作\n");

// 切换到根目录
if (chdir("/") == -1) {
perror("切换到根目录失败");
} else {
printf("成功切换到根目录\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 切换回原始目录
if (chdir(original_dir) == -1) {
perror("返回原始目录失败");
} else {
printf("成功返回原始目录\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 示例2: 创建测试环境
printf("\n示例2: 创建测试环境\n");

// 创建测试目录结构
const char *base_dir = "test_chdir_base";
const char *sub_dir1 = "test_chdir_base/subdir1";
const char *sub_dir2 = "test_chdir_base/subdir2";
const char *deep_dir = "test_chdir_base/subdir1/deepdir";

// 创建目录
if (mkdir(base_dir, 0755) == -1 && errno != EEXIST) {
perror("创建基础目录失败");
} else {
printf("创建基础目录: %s\n", base_dir);
}

if (mkdir(sub_dir1, 0755) == -1 && errno != EEXIST) {
perror("创建子目录1失败");
} else {
printf("创建子目录1: %s\n", sub_dir1);
}

if (mkdir(sub_dir2, 0755) == -1 && errno != EEXIST) {
perror("创建子目录2失败");
} else {
printf("创建子目录2: %s\n", sub_dir2);
}

if (mkdir(deep_dir, 0755) == -1 && errno != EEXIST) {
perror("创建深层目录失败");
} else {
printf("创建深层目录: %s\n", deep_dir);
}

// 在目录中创建测试文件
int fd = open("test_chdir_base/test_file.txt", O_CREAT | O_WRONLY, 0644);
if (fd != -1) {
const char *content = "Test file content for chdir demonstration";
write(fd, content, strlen(content));
close(fd);
printf("创建测试文件: test_chdir_base/test_file.txt\n");
}

// 示例3: 绝对路径和相对路径切换
printf("\n示例3: 绝对路径和相对路径切换\n");

// 使用绝对路径切换
char absolute_path&#91;PATH_MAX * 2];
snprintf(absolute_path, sizeof(absolute_path), "%s/%s", original_dir, base_dir);
printf("使用绝对路径切换: %s\n", absolute_path);

if (chdir(absolute_path) == -1) {
perror("使用绝对路径切换失败");
} else {
printf("绝对路径切换成功\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 使用相对路径切换到子目录
printf("使用相对路径切换到子目录1\n");
if (chdir("subdir1") == -1) {
perror("切换到子目录1失败");
} else {
printf("切换到子目录1成功\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 使用相对路径返回上级目录
printf("使用相对路径返回上级目录\n");
if (chdir("..") == -1) {
perror("返回上级目录失败");
} else {
printf("返回上级目录成功\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 切换到深层目录
printf("切换到深层目录\n");
if (chdir("subdir1/deepdir") == -1) {
perror("切换到深层目录失败");
} else {
printf("切换到深层目录成功\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 使用多个..返回
printf("使用多个..返回原始目录\n");
if (chdir("../../..") == -1) {
perror("多级返回失败");
} else {
printf("多级返回成功\n");
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("当前目录: %s\n", buffer);
}
}

// 示例4: 目录切换的副作用演示
printf("\n示例4: 目录切换的副作用演示\n");

// 切换到测试目录
if (chdir(base_dir) == -1) {
perror("切换到测试目录失败");
} else {
printf("切换到测试目录\n");

// 在当前目录创建文件
fd = open("created_in_cwd.txt", O_CREAT | O_WRONLY, 0644);
if (fd != -1) {
const char *file_content = "File created in current working directory";
write(fd, file_content, strlen(file_content));
close(fd);
printf("在当前目录创建文件: created_in_cwd.txt\n");
}

// 列出当前目录文件
printf("当前目录文件:\n");
system("ls -la");

// 切换目录后再次查看
if (chdir("subdir1") == -1) {
perror("切换到子目录失败");
} else {
printf("\n切换到子目录后:\n");
system("ls -la");
}
}

// 返回原始目录
if (chdir(original_dir) == -1) {
perror("返回原始目录失败");
}

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

// 尝试切换到不存在的目录
if (chdir("/nonexistent/directory") == -1) {
printf("切换到不存在的目录: %s\n", strerror(errno));
}

// 尝试切换到文件而不是目录
if (chdir("/etc/passwd") == -1) {
printf("切换到文件而非目录: %s\n", strerror(errno));
}

// 尝试切换到没有权限的目录
if (chdir("/root") == -1) {
printf("切换到无权限目录: %s\n", strerror(errno));
}

// 尝试使用过长的路径名
char long_path&#91;PATH_MAX + 100];
memset(long_path, 'a', sizeof(long_path) - 1);
long_path&#91;sizeof(long_path) - 1] = '\0';
if (chdir(long_path) == -1) {
printf("使用过长路径名: %s\n", strerror(errno));
}

// 示例6: 实际应用场景
printf("\n示例6: 实际应用场景\n");

// 场景1: 程序初始化时切换到工作目录
printf("场景1: 程序工作目录设置\n");
const char *work_dir = "/tmp";
if (chdir(work_dir) == -1) {
printf("无法切换到工作目录 %s: %s\n", work_dir, strerror(errno));
printf("使用当前目录作为工作目录\n");
} else {
printf("成功切换到工作目录: %s\n", work_dir);
}

// 场景2: 备份脚本中的目录操作
printf("场景2: 备份操作模拟\n");
char backup_dir&#91;PATH_MAX];
snprintf(backup_dir, sizeof(backup_dir), "%s/backup_test", original_dir);

// 创建备份目录
if (mkdir(backup_dir, 0755) == -1 && errno != EEXIST) {
perror("创建备份目录失败");
} else {
printf("创建备份目录: %s\n", backup_dir);

// 切换到备份目录
if (chdir(backup_dir) == -1) {
perror("切换到备份目录失败");
} else {
printf("切换到备份目录进行操作\n");

// 模拟备份操作
system("echo 'Backup operation in progress...' > backup.log");
system("date >> backup.log");
printf("备份操作记录已保存\n");
}
}

// 场景3: 构建系统中的目录管理
printf("场景3: 构建系统目录管理\n");
struct {
const char *dir_name;
const char *purpose;
} build_dirs&#91;] = {
{"src", "源代码目录"},
{"include", "头文件目录"},
{"lib", "库文件目录"},
{"bin", "可执行文件目录"},
{"obj", "目标文件目录"}
};

// 切换到基础目录
if (chdir(base_dir) == -1) {
perror("切换到基础目录失败");
} else {
printf("在 %s 中创建构建目录结构:\n", base_dir);

for (int i = 0; i < 5; i++) {
if (mkdir(build_dirs&#91;i].dir_name, 0755) == -1 && errno != EEXIST) {
printf(" 创建 %s 失败: %s\n", build_dirs&#91;i].dir_name, strerror(errno));
} else {
printf(" 创建 %s (%s)\n", build_dirs&#91;i].dir_name, build_dirs&#91;i].purpose);
}
}
}

// 场景4: Web服务器目录切换
printf("场景4: Web服务器目录安全\n");
const char *web_root = "/var/www";
printf("Web服务器尝试切换到根目录: %s\n", web_root);

// 检查目录是否存在和可访问
if (access(web_root, F_OK) == 0) {
if (access(web_root, R_OK | X_OK) == 0) {
printf("目录存在且可访问\n");
// 在实际应用中会进行chdir操作
} else {
printf("目录存在但权限不足\n");
}
} else {
printf("目录不存在或无法访问\n");
}

// 示例7: 目录切换的安全考虑
printf("\n示例7: 目录切换的安全考虑\n");

// 保存原始目录文件描述符(用于安全返回)
int original_fd = open(".", O_RDONLY);
if (original_fd != -1) {
printf("保存原始目录文件描述符: %d\n", original_fd);

// 执行目录切换
if (chdir(base_dir) == -1) {
perror("切换目录失败");
} else {
printf("切换到测试目录\n");

// 执行一些操作
system("pwd");

// 使用文件描述符安全返回
if (fchdir(original_fd) == -1) {
perror("使用文件描述符返回失败");
} else {
printf("使用文件描述符安全返回原始目录\n");
system("pwd");
}
}

close(original_fd);
}

// 示例8: 相对路径解析演示
printf("\n示例8: 相对路径解析\n");

if (chdir(base_dir) == -1) {
perror("切换到测试目录失败");
} else {
printf("当前目录: ");
system("pwd");

// 相对路径解析示例
struct {
const char *relative_path;
const char *description;
} paths&#91;] = {
{".", "当前目录"},
{"..", "上级目录"},
{"./subdir1", "当前目录下的子目录"},
{"../subdir2", "上级目录下的另一个子目录"},
{"subdir1/./deepdir", "带.的路径"},
{"subdir1/../subdir2", "带..的路径"}
};

for (int i = 0; i < 6; i++) {
printf("路径 '%s' (%s):\n", paths&#91;i].relative_path, paths&#91;i].description);

// 保存当前位置
int save_fd = open(".", O_RDONLY);
if (save_fd != -1) {
// 尝试切换
if (chdir(paths&#91;i].relative_path) == 0) {
printf(" 切换成功: ");
system("pwd");

// 返回原位置
if (fchdir(save_fd) == -1) {
perror(" 返回失败");
}
} else {
printf(" 切换失败: %s\n", strerror(errno));
}
close(save_fd);
}
printf("\n");
}
}

// 返回原始目录
if (chdir(original_dir) == -1) {
perror("最终返回原始目录失败");
}

// 清理测试资源
printf("\n清理测试资源...\n");

// 删除测试文件
char test_file_path&#91;PATH_MAX * 2];
snprintf(test_file_path, sizeof(test_file_path), "%s/%s/created_in_cwd.txt",
original_dir, base_dir);
if (access(test_file_path, F_OK) == 0) {
unlink(test_file_path);
printf("删除测试文件\n");
}

// 删除备份目录和文件
char backup_file_path&#91;PATH_MAX * 2];
snprintf(backup_file_path, sizeof(backup_file_path), "%s/backup.log", backup_dir);
if (access(backup_file_path, F_OK) == 0) {
unlink(backup_file_path);
}
if (access(backup_dir, F_OK) == 0) {
rmdir(backup_dir);
printf("删除备份目录\n");
}

// 删除构建目录
if (chdir(base_dir) == 0) {
for (int i = 0; i < 5; i++) {
rmdir(build_dirs&#91;i].dir_name);
}
chdir(original_dir);
}

// 删除测试目录结构
char deep_dir_path&#91;PATH_MAX * 2];
snprintf(deep_dir_path, sizeof(deep_dir_path), "%s/%s", original_dir, deep_dir);
if (access(deep_dir_path, F_OK) == 0) {
rmdir(deep_dir_path);
}

char subdir1_path&#91;PATH_MAX * 2];
snprintf(subdir1_path, sizeof(subdir1_path), "%s/%s", original_dir, sub_dir1);
if (access(subdir1_path, F_OK) == 0) {
rmdir(subdir1_path);
}

char subdir2_path&#91;PATH_MAX * 2];
snprintf(subdir2_path, sizeof(subdir2_path), "%s/%s", original_dir, sub_dir2);
if (access(subdir2_path, F_OK) == 0) {
rmdir(subdir2_path);
}

char test_file&#91;PATH_MAX * 2];
snprintf(test_file, sizeof(test_file), "%s/%s/test_file.txt", original_dir, base_dir);
if (access(test_file, F_OK) == 0) {
unlink(test_file);
}

char base_dir_path&#91;PATH_MAX * 2];
snprintf(base_dir_path, sizeof(base_dir_path), "%s/%s", original_dir, base_dir);
if (access(base_dir_path, F_OK) == 0) {
rmdir(base_dir_path);
printf("删除测试目录结构完成\n");
}

return 0;
}

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

chdir 系统调用, 改变当前工作目录, chdir 函数用法, Linux chdir 命令, 系统调用 chdir 示例, chdir 详解, 如何使用 chdir, chdir 函数介绍, Linux 系统调用 chdir, chdir 实现原理