chmod系统调用及示例

我们继续学习 Linux 系统编程中的重要函数。这次我们介绍 chmod 函数,它用于改变文件的访问权限。

chmod 函数简介

1. 函数介绍

chmod 是一个 Linux 系统调用,用于改变文件或目录的访问权限(也称为文件模式位)。这些权限决定了哪些用户可以读取、写入或执行文件。

文件权限是 Unix/Linux 系统安全模型的基础。每个文件都有三组权限位:所有者(user)、所属组(group)和其他用户(others)。每组权限又包含三种基本权限:读(read, r)、写(write, w)和执行(execute, x)。

通过 chmod,具有适当权限的用户(通常是文件所有者或 root)可以调整这些权限,以控制对文件的访问。例如,一个用户可能希望保护一个私密文件,使其只能被自己读取;或者希望让一个脚本文件对所有用户都可执行。

2. 函数原型

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

int chmod(const char *pathname, mode_t mode);

3. 功能

  • 改变文件权限: 将由 pathname 指定的文件或目录的访问权限设置为 mode 参数指定的值。

  • 设置绝对权限: mode 参数通常是一个八进制数(如 0644, 0755)或通过位运算组合的符号常量(如 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)。

4. 参数

  • const char *pathname: 指向一个以空字符 (\0) 结尾的字符串,该字符串包含了要更改权限的文件或目录的路径名。这可以是相对路径或绝对路径。

mode_t mode: 指定新的文件权限。这个参数可以有两种表示方式:

八进制表示法:

  • 最常见的形式,如 0644, 0755, 0600。

  • 第一个数字 0 表示这是一个八进制数。

  • 接下来的三位数字分别代表所有者(user)、组(group)、其他用户(others)的权限。

每一位的值是读(4)、写(2)、执行(1)的组合:

  • 7 (4+2+1) = 读+写+执行 (rwx)

  • 6 (4+2) = 读+写 (rw-)

  • 5 (4+1) = 读+执行 (r-x)

  • 4 (4) = 只读 (r–)

  • 0 = 无权限 (—)

例如:

  • 0644 表示所有者:读写 (6),组和其他用户:只读 (4)。常用于普通文件。

  • 0755 表示所有者:读写执行 (7),组和其他用户:读执行 (5)。常用于可执行文件或目录。

  • 0600 表示所有者:读写 (6),组和其他用户:无权限 (0)。常用于私密文件。

符号常量表示法:

  • 使用 <sys/stat.h> 中定义的宏进行位运算组合。

用户类别:

  • S_IRWXU: 所有者的读、写、执行权限

  • S_IRUSR: 所有者的读权限

  • S_IWUSR: 所有者的写权限

  • S_IXUSR: 所有者的执行权限

组类别:

  • S_IRWXG: 组的读、写、执行权限

  • S_IRGRP: 组的读权限

  • S_IWGRP: 组的写权限

  • S_IXGRP: 组的执行权限

其他用户类别:

  • S_IRWXO: 其他用户的读、写、执行权限

  • S_IROTH: 其他用户的读权限

  • S_IWOTH: 其他用户的写权限

  • S_IXOTH: 其他用户的执行权限

特殊位:

  • S_ISUID: 设置用户ID位 (set-user-ID)

  • S_ISGID: 设置组ID位 (set-group-ID)

  • S_ISVTX: 粘滞位 (sticky bit)

例如:

  • S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 等价于 0644。

  • S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH 等价于 0755。

5. 返回值

  • 成功时: 返回 0。

失败时:

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

  • EACCES: 搜索路径名中的某个目录被拒绝。

  • EROFS: 路径名存在于只读文件系统上。

  • EIO: 执行 I/O 错误。

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

  • ENAMETOOLONG: 路径名过长。

  • ENOENT: 文件不存在。

  • ENOMEM: 路径名无法分配内存。

  • ENOTDIR: 路径名前缀不是一个目录。

  • EPERM: 操作不被文件系统或操作系统允许。例如,尝试在某些文件系统上设置 set-group-ID 位。

  • EFAULT: pathname 指针指向进程地址空间之外。

  • EINVAL: mode 参数无效。

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

  • fchmod(int fd, mode_t mode): 与 chmod 功能相同,但通过已打开的文件描述符而不是路径名来指定文件。这可以避免路径解析。

  • fchmodat(int dirfd, const char *pathname, mode_t mode, int flags): 更现代的函数,允许使用相对路径(相对于 dirfd 描述符对应的目录)并提供额外的标志。

  • umask(mode_t mask): 设置进程的文件权限掩码,它会影响后续创建的文件的默认权限。

  • stat, lstat, fstat: 这些函数可以用来获取文件的当前权限,而不是设置它们。

7. 示例代码

示例 1:基本的权限更改

这个例子演示了如何使用 chmod 来更改文件的权限,包括八进制和符号常量两种方式。

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
#include <sys/stat.h>  // chmod, stat, struct stat
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <errno.h> // errno
#include <string.h> // strerror

// 辅助函数:将 mode_t 转换为可读的权限字符串
void print_permissions(mode_t mode) {
char perms&#91;11];
// 初始化字符数组
strcpy(perms, "----------");

// 用户权限
if (mode & S_IRUSR) perms&#91;1] = 'r';
if (mode & S_IWUSR) perms&#91;2] = 'w';
if (mode & S_IXUSR) perms&#91;3] = 'x';

// 组权限
if (mode & S_IRGRP) perms&#91;4] = 'r';
if (mode & S_IWGRP) perms&#91;5] = 'w';
if (mode & S_IXGRP) perms&#91;6] = 'x';

// 其他用户权限
if (mode & S_IROTH) perms&#91;7] = 'r';
if (mode & S_IWOTH) perms&#91;8] = 'w';
if (mode & S_IXOTH) perms&#91;9] = 'x';

// 特殊位
if (mode & S_ISUID) perms&#91;3] = (perms&#91;3] == 'x') ? 's' : 'S';
if (mode & S_ISGID) perms&#91;6] = (perms&#91;6] == 'x') ? 's' : 'S';
if (mode & S_ISVTX) perms&#91;9] = (perms&#91;9] == 'x') ? 't' : 'T';

printf("%s", perms);
}

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

printf("文件 '%s' 的信息:\n", pathname);
printf(" Inode: %ld\n", sb.st_ino);
printf(" 权限: ", pathname);
print_permissions(sb.st_mode);
printf(" (八进制: %o)\n", sb.st_mode & 0777);
printf(" 大小: %ld 字节\n", sb.st_size);
}

int main(int argc, char *argv&#91;]) {
if (argc != 3) {
fprintf(stderr, "用法: %s <文件路径> <新权限>\n", argv&#91;0]);
fprintf(stderr, " 权限可以是八进制 (如 0644) 或符号 (如 u+r)\n");
fprintf(stderr, " 示例: %s myfile.txt 0644\n", argv&#91;0]);
fprintf(stderr, " %s script.sh 0755\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

const char *pathname = argv&#91;1];
const char *mode_str = argv&#91;2];

printf("准备更改文件 '%s' 的权限\n", pathname);

// 打印更改前的信息
print_file_info(pathname);

// 解析权限模式
mode_t new_mode;
char *endptr;

// 尝试解析为八进制数
new_mode = (mode_t) strtol(mode_str, &endptr, 8);
if (*endptr != '\0') {
fprintf(stderr, "错误: 不支持的权限格式 '%s'。请使用八进制数(如 0644)。\n", mode_str);
exit(EXIT_FAILURE);
}

printf("\n新的权限模式: ");
print_permissions(new_mode);
printf(" (八进制: %o)\n", new_mode);

// 执行 chmod 操作
if (chmod(pathname, new_mode) == -1) {
perror("chmod 失败");
exit(EXIT_FAILURE);
}

printf("\nchmod 操作成功!\n");

// 打印更改后的信息
print_file_info(pathname);

return 0;
}

代码解释:

定义了两个辅助函数:

  • print_permissions: 将 mode_t 类型的权限值转换为人类可读的字符串(如 -rw-r–r–)。

  • print_file_info: 使用 stat 获取并打印文件的详细信息,包括权限。

main 函数接受文件路径和权限字符串作为参数。

它使用 strtol 将权限字符串解析为八进制数。

调用 print_file_info 显示更改前的状态。

调用 chmod(pathname, new_mode) 执行权限更改。

如果成功,再次调用 print_file_info 显示更改后的状态。

示例 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
#define _GNU_SOURCE
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <fnmatch.h> // 用于模式匹配

// 递归更改目录下匹配模式的文件权限
int change_permissions_recursive(const char *dir_path, const char *pattern, mode_t mode, int verbose) {
DIR *dir;
struct dirent *entry;
char full_path&#91;1024];
int changed_count = 0;
int error_count = 0;

dir = opendir(dir_path);
if (!dir) {
fprintf(stderr, "无法打开目录 '%s': %s\n", dir_path, strerror(errno));
return -1;
}

while ((entry = readdir(dir)) != NULL) {
// 跳过 . 和 ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

// 构造完整路径
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);

// 检查是否匹配模式
if (fnmatch(pattern, entry->d_name, 0) == 0) {
// 匹配,尝试更改权限
if (chmod(full_path, mode) == -1) {
fprintf(stderr, "警告: 无法更改 '%s' 的权限: %s\n", full_path, strerror(errno));
error_count++;
} else {
if (verbose) {
printf("已更改 '%s' 的权限\n", full_path);
}
changed_count++;
}
}

// 如果是目录,则递归处理
struct stat sb;
if (stat(full_path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
int sub_result = change_permissions_recursive(full_path, pattern, mode, verbose);
if (sub_result >= 0) {
changed_count += sub_result;
} else {
error_count++;
}
}
}

closedir(dir);

if (error_count > 0) {
return -1;
}
return changed_count;
}

// 打印权限更改的摘要
void print_mode_summary(mode_t mode) {
printf("权限设置为: ");
// 用户权限
printf((mode & S_IRUSR) ? "r" : "-");
printf((mode & S_IWUSR) ? "w" : "-");
printf((mode & S_IXUSR) ? "x" : "-");
// 组权限
printf((mode & S_IRGRP) ? "r" : "-");
printf((mode & S_IWGRP) ? "w" : "-");
printf((mode & S_IXGRP) ? "x" : "-");
// 其他用户权限
printf((mode & S_IROTH) ? "r" : "-");
printf((mode & S_IWOTH) ? "w" : "-");
printf((mode & S_IXOTH) ? "x" : "-");
printf(" (八进制: %04o)\n", mode);
}

int main(int argc, char *argv&#91;]) {
if (argc < 4) {
fprintf(stderr, "用法: %s <目录路径> <文件模式> <权限> &#91;-r] &#91;-v] &#91;-p pattern]\n", argv&#91;0]);
fprintf(stderr, " -r: 递归处理子目录\n");
fprintf(stderr, " -v: 详细输出\n");
fprintf(stderr, " -p pattern: 只处理匹配模式的文件 (支持通配符)\n");
fprintf(stderr, " 示例: %s /home/user *.txt 0644 -r -v\n", argv&#91;0]);
fprintf(stderr, " %s /var/log 0600 -p \"*.log\"\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

const char *dir_path = argv&#91;1];
const char *pattern = argv&#91;2];
const char *mode_str = argv&#91;3];
int recursive = 0;
int verbose = 0;
const char *file_pattern = "*"; // 默认匹配所有文件

// 解析选项
for (int i = 4; i < argc; i++) {
if (strcmp(argv&#91;i], "-r") == 0) {
recursive = 1;
} else if (strcmp(argv&#91;i], "-v") == 0) {
verbose = 1;
} else if (strcmp(argv&#91;i], "-p") == 0 && i + 1 < argc) {
file_pattern = argv&#91;++i];
} else {
fprintf(stderr, "未知选项: %s\n", argv&#91;i]);
exit(EXIT_FAILURE);
}
}

// 解析权限模式
mode_t mode;
char *endptr;
mode = (mode_t) strtol(mode_str, &endptr, 8);
if (*endptr != '\0') {
fprintf(stderr, "错误: 无效的权限模式 '%s'。请使用八进制数(如 0644)。\n", mode_str);
exit(EXIT_FAILURE);
}

printf("=== 批量权限更改工具 ===\n");
printf("目录: %s\n", dir_path);
printf("文件模式: %s\n", pattern);
printf("文件名匹配: %s\n", file_pattern);
print_mode_summary(mode);
printf("递归: %s\n", recursive ? "是" : "否");
printf("详细输出: %s\n", verbose ? "是" : "否");

// 执行权限更改
int result;
if (recursive) {
result = change_permissions_recursive(dir_path, file_pattern, mode, verbose);
} else {
// 非递归处理
DIR *dir = opendir(dir_path);
if (!dir) {
perror("opendir");
exit(EXIT_FAILURE);
}

struct dirent *entry;
char full_path&#91;1024];
int changed_count = 0;
int error_count = 0;

while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

// 检查文件名是否匹配模式
if (fnmatch(pattern, entry->d_name, 0) == 0) {
// 检查文件名是否匹配文件模式
if (fnmatch(file_pattern, entry->d_name, 0) == 0) {
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
if (chmod(full_path, mode) == -1) {
fprintf(stderr, "警告: 无法更改 '%s' 的权限: %s\n", full_path, strerror(errno));
error_count++;
} else {
if (verbose) {
printf("已更改 '%s' 的权限\n", full_path);
}
changed_count++;
}
}
}
}
closedir(dir);

if (error_count > 0) {
result = -1;
} else {
result = changed_count;
}
}

if (result == -1) {
fprintf(stderr, "\n权限更改过程中遇到错误。\n");
exit(EXIT_FAILURE);
} else {
printf("\n权限更改完成。成功更改了 %d 个文件的权限。\n", result);
}

return 0;
}

代码解释:

change_permissions_recursive 函数实现了递归的权限更改功能,使用 opendir 和 readdir 遍历目录。

它使用 fnmatch 函数来支持通配符模式匹配(如 *.txt, *.log)。

print_mode_summary 函数以人类可读的方式显示权限设置。

main 函数处理命令行参数,支持递归(-r)、详细输出(-v)和文件名模式匹配(-p)选项。

程序会统计成功更改的文件数量和错误数量。

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

// 创建具有特定权限的测试文件
void create_test_files() {
printf("=== 创建测试文件 ===\n");

// 1. 创建普通文件
FILE *fp = fopen("normal_file.txt", "w");
if (fp) {
fprintf(fp, "This is a normal file for permission testing.\n");
fclose(fp);
printf("创建普通文件: normal_file.txt\n");
}

// 2. 创建私密文件
fp = fopen("private_file.txt", "w");
if (fp) {
fprintf(fp, "This is a private file containing sensitive data.\n");
fclose(fp);
printf("创建私密文件: private_file.txt\n");
}

// 3. 创建脚本文件
fp = fopen("test_script.sh", "w");
if (fp) {
fprintf(fp, "#!/bin/bash\necho \"This is a test script.\"\n");
fclose(fp);
printf("创建脚本文件: test_script.sh\n");
}

// 4. 创建目录
if (mkdir("test_directory", 0755) == 0) {
printf("创建目录: test_directory\n");
}

printf("\n");
}

// 演示安全的权限设置
void demonstrate_secure_permissions() {
printf("=== 安全权限设置演示 ===\n");

// 1. 设置普通文件权限 (0644)
printf("1. 设置普通文件权限为 0644 (rw-r--r--)\n");
if (chmod("normal_file.txt", 0644) == 0) {
printf(" 成功: normal_file.txt 现在是 rw-r--r--\n");
} else {
printf(" 失败: %s\n", strerror(errno));
}

// 2. 设置私密文件权限 (0600)
printf("2. 设置私密文件权限为 0600 (rw-------)\n");
if (chmod("private_file.txt", 0600) == 0) {
printf(" 成功: private_file.txt 现在是 rw-------\n");
} else {
printf(" 失败: %s\n", strerror(errno));
}

// 3. 设置脚本文件权限 (0755)
printf("3. 设置脚本文件权限为 0755 (rwxr-xr-x)\n");
if (chmod("test_script.sh", 0755) == 0) {
printf(" 成功: test_script.sh 现在是 rwxr-xr-x\033&#91;0m\n");
} else {
printf(" 失败: %s\n", strerror(errno));
}

// 4. 设置目录权限 (0755)
printf("4. 设置目录权限为 0755 (rwxr-xr-x)\n");
if (chmod("test_directory", 0755) == 0) {
printf(" 成功: test_directory 现在是 rwxr-xr-x\033&#91;0m\n");
} else {
printf(" 失败: %s\n", strerror(errno));
}

printf("\n");
}

// 演示危险的权限设置
void demonstrate_dangerous_permissions() {
printf("=== 危险权限设置警告 ===\n");

printf("以下权限设置可能存在安全风险:\n");

// 1. 世界可写的文件
printf("1. 世界可写的普通文件 (0666)\n");
printf(" 风险: 任何用户都可以修改文件内容\n");
printf(" 建议: 使用 0644 代替\n");

// 2. 世界可执行的文件
printf("2. 世界可执行的敏感脚本 (0777)\n");
printf(" 风险: 任何用户都可以执行,可能存在安全漏洞\n");
printf(" 建议: 使用 0755 并确保脚本安全\n");

// 3. 私密文件设置不当
printf("3. 私密文件权限过于宽松 (0644)\n");
printf(" 风险: 组用户和其他用户可以读取私密信息\n");
printf(" 建议: 使用 0600 确保只有所有者可访问\n");

printf("\n");
}

// 演示权限检查
void demonstrate_permission_checking() {
printf("=== 权限检查最佳实践 ===\n");

struct stat sb;

// 检查私密文件权限
if (stat("private_file.txt", &sb) == 0) {
mode_t mode = sb.st_mode & 0777;
printf("检查 private_file.txt 当前权限: %04o\n", mode);

if (mode == 0600) {
printf("✓ 权限设置正确,只有所有者可读写\n");
} else if (mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
printf("✗ 警告: 权限过于宽松,组用户或其他用户有访问权限\n");
} else {
printf("- 权限设置合理\n");
}
}

// 检查脚本文件权限
if (stat("test_script.sh", &sb) == 0) {
mode_t mode = sb.st_mode & 0777;
printf("检查 test_script.sh 当前权限: %04o\n", mode);

if (mode & S_IXUSR) {
printf("✓ 所有者有执行权限\n");
} else {
printf("✗ 所有者没有执行权限,脚本可能无法运行\n");
}
}

printf("\n");
}

// 清理测试文件
void cleanup_test_files() {
printf("=== 清理测试文件 ===\n");
unlink("normal_file.txt");
unlink("private_file.txt");
unlink("test_script.sh");
rmdir("test_directory");
printf("清理完成\n");
}

int main() {
printf("当前用户: UID=%d, GID=%d\n", getuid(), getgid());
printf("\n");

// 创建测试文件
create_test_files();

// 演示安全权限设置
demonstrate_secure_permissions();

// 演示危险权限设置
demonstrate_dangerous_permissions();

// 演示权限检查
demonstrate_permission_checking();

// 清理
cleanup_test_files();

printf("\n=== 权限设置最佳实践总结 ===\n");
printf("普通文件: 0644 (rw-r--r--)\n");
printf("私密文件: 0600 (rw-------)\n");
printf("可执行文件: 0755 (rwxr-xr-x)\n");
printf("私密可执行文件: 0700 (rwx------)\n");
printf("目录: 0755 (rwxr-xr-x)\n");
printf("私密目录: 0700 (rwx------)\n");
printf("\n安全建议:\n");
printf("1. 遵循最小权限原则\n");
printf("2. 定期检查重要文件的权限\n");
printf("3. 避免使用 0777 或 0666 等过于宽松的权限\n");
printf("4. 对于敏感文件,使用 0600 或 0700\n");
printf("5. 理解权限位的含义,避免误操作\n");

return 0;
}

代码解释:

create_test_files 函数创建了几种不同类型的测试文件。

demonstrate_secure_permissions 演示了如何为不同类型的文件设置安全的权限。

demonstrate_dangerous_permissions 警告了一些常见的危险权限设置。

demonstrate_permission_checking 展示了如何检查现有文件的权限是否合理。

cleanup_test_files 负责清理创建的测试文件。

main 函数协调整个演示过程,并在最后总结权限设置的最佳实践。

编译和运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 编译示例
gcc -o chmod_example1 chmod_example1.c
gcc -o chmod_example2 chmod_example2.c -lpthread
gcc -o chmod_example3 chmod_example3.c

# 运行示例
# 示例1: 基本用法
touch testfile.txt
./chmod_example1 testfile.txt 0644
./chmod_example1 script.sh 0755

# 示例2: 批量处理
mkdir testdir
touch testdir/file1.txt testdir/file2.log
./chmod_example2 testdir "*.txt" 0644 -r -v

# 示例3: 安全演示
./chmod_example3

总结:

chmod 函数是 Linux 文件系统权限管理的核心工具。掌握其使用方法对于系统安全和文件访问控制至关重要。在使用时应遵循最小权限原则,根据文件的实际用途设置合适的权限,并定期检查重要文件的权限设置,以防止安全漏洞。

chown系统调用及示例

我们继续学习 Linux 系统编程中的重要函数 chown 函数,它用于改变文件的所有者和所属组。

chrown 函数

1. 函数介绍

chown 是一个 Linux 系统调用,用于改变文件的所有者用户 ID (UID) 和/或组 ID (GID)。这使得具有适当权限的用户(通常是 root 或文件的当前所有者)可以将文件的归属权转移给其他用户或组。

这对于系统管理、权限控制和文件共享非常重要。例如,系统管理员可能需要将一个文件的所有权从一个用户转移到另一个用户,或者将文件的组所有权更改为一个特定的组,以便该组的成员可以访问它。

需要注意的是,只有特权进程(有效用户 ID 为 0,通常是 root)可以将文件的所有者更改为任意用户。非特权进程通常只能将文件的所有者设置为进程的有效用户 ID(即,不能将文件给别人,但可以放弃文件的所有权给自己,或者在已经是所有者时更改组)。

2. 函数原型

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

int chown(const char *pathname, uid_t owner, gid_t group);

3. 功能

  • 改变文件所有者: 将由 pathname 指定的文件的所有者 UID 更改为 owner。

  • 改变文件所属组: 将由 pathname 指定的文件的组 GID 更改为 group。

  • 同时改变: 可以同时改变所有者和所属组。

  • 选择性改变: 如果 owner 或 group 被设置为特殊值 -1(或 (uid_t) -1 / (gid_t) -1),则相应的 ID 不会被更改。

4. 参数

  • const char *pathname: 指向一个以空字符 (\0) 结尾的字符串,该字符串包含了要更改所有权的文件或目录的路径名。这可以是相对路径或绝对路径。

uid_t owner: 新的所有者用户 ID。

  • 如果是 (uid_t) -1,则不更改文件的所有者。

  • 如果是有效的 UID(如 0, 1000, 1001 等),则尝试将文件所有者更改为该 UID。

gid_t group: 新的所属组 ID。

  • 如果是 (gid_t) -1,则不更改文件的所属组。

  • 如果是有效的 GID(如 0, 100, 1001 等),则尝试将文件所属组更改为该 GID。

5. 返回值

  • 成功时: 返回 0。

失败时:

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

  • EACCES: 搜索路径名中的某个目录被拒绝。

  • EIO: 执行 I/O 错误。

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

  • ENAMETOOLONG: 路径名过长。

  • ENOENT: 文件不存在。

  • ENOMEM: 路径名无法分配内存。

  • ENOTDIR: 路径名前缀不是一个目录。

  • EPERM: 调用进程没有权限更改所有权。最常见的原因是非特权用户试图将文件所有者更改为其他用户。

  • EROFS: 路径名存在于只读文件系统上。

  • EFAULT: pathname 指针指向进程地址空间之外。

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

  • fchown(int fd, uid_t owner, gid_t group): 与 chown 功能相同,但通过已打开的文件描述符而不是路径名来指定文件。这可以避免路径解析,并且在某些情况下更高效或更安全。

  • lchown(const char *pathname, uid_t owner, gid_t group): 与 chown 类似,但如果 pathname 是一个符号链接,lchown 会更改符号链接本身的所有权,而不是它指向的目标文件的所有权。chown 会跟随符号链接。

  • fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags): 更现代的函数,允许使用相对路径(相对于 dirfd 描述符对应的目录)并提供额外的标志(如 AT_SYMLINK_NOFOLLOW)。

7. 示例代码

示例 1:基本的所有权更改

这个例子演示了如何使用 chown 来更改文件的所有者和/或组。

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
#include <unistd.h>     // chown
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <sys/stat.h> // struct stat, stat
#include <pwd.h> // getpwuid
#include <grp.h> // getgrgid
#include <errno.h> // errno
#include <string.h> // strerror

// 辅助函数:打印文件的当前所有权
void print_file_owner(const char *pathname) {
struct stat sb;
if (stat(pathname, &sb) == -1) {
perror("stat");
return;
}

struct passwd *pw = getpwuid(sb.st_uid);
struct group *gr = getgrgid(sb.st_gid);

printf("文件 '%s' 的当前所有权:\n", pathname);
printf(" UID: %d", sb.st_uid);
if (pw) {
printf(" (用户: %s)", pw->pw_name);
}
printf("\n");

printf(" GID: %d", sb.st_gid);
if (gr) {
printf(" (组: %s)", gr->gr_name);
}
printf("\n");
}

int main(int argc, char *argv&#91;]) {
if (argc != 4) {
fprintf(stderr, "用法: %s <文件路径> <新UID> <新GID>\n", argv&#91;0]);
fprintf(stderr, " 使用 -1 表示不更改相应的ID。\n");
fprintf(stderr, " 示例: %s myfile.txt 1000 1000\n", argv&#91;0]);
fprintf(stderr, " %s myfile.txt -1 1000 (仅更改组)\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

const char *pathname = argv&#91;1];
uid_t new_uid;
gid_t new_gid;

// 解析 UID 和 GID 参数
if (strcmp(argv&#91;2], "-1") == 0) {
new_uid = (uid_t) -1; // 不更改 UID
} else {
new_uid = (uid_t) atoi(argv&#91;2]);
}

if (strcmp(argv&#91;3], "-1") == 0) {
new_gid = (gid_t) -1; // 不更改 GID
} else {
new_gid = (gid_t) atoi(argv&#91;3]);
}

printf("准备更改文件 '%s' 的所有权:\n", pathname);
printf(" 新 UID: %d (不更改则为-1)\n", (int)new_uid);
printf(" 新 GID: %d (不更改则为-1)\n", (int)new_gid);

// 打印更改前的所有权
print_file_owner(pathname);

// 执行 chown 操作
if (chown(pathname, new_uid, new_gid) == -1) {
perror("chown 失败");
exit(EXIT_FAILURE);
}

printf("\nchown 操作成功!\n");

// 打印更改后的所有权
print_file_owner(pathname);

return 0;
}

代码解释:

定义了一个辅助函数 print_file_owner,它使用 stat 获取文件信息,并使用 getpwuid 和 getgrgid 将 UID/GID 解析为用户名和组名,然后打印出来。

main 函数接受三个命令行参数:文件路径、新 UID、新 GID。

它解析 -1 为 “不更改”,其他值转换为对应的 uid_t/gid_t。

调用 print_file_owner 显示更改前的状态。

调用 chown(pathname, new_uid, new_gid) 执行所有权更改。

如果成功,再次调用 print_file_owner 显示更改后的状态。

示例 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
#define _GNU_SOURCE // 为了使用一些 GNU 扩展
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h> // 用于遍历目录

// 检查当前用户是否为 root (UID 0)
int is_root() {
return (geteuid() == 0);
}

// 递归更改目录下所有文件的所有权
int change_ownership_recursive(const char *dir_path, uid_t uid, gid_t gid) {
DIR *dir;
struct dirent *entry;
char full_path&#91;1024];

dir = opendir(dir_path);
if (!dir) {
perror("opendir");
return -1;
}

while ((entry = readdir(dir)) != NULL) {
// 跳过 . 和 ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

// 构造完整路径
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);

// 更改当前文件/目录的所有权
if (chown(full_path, uid, gid) == -1) {
fprintf(stderr, "警告: 无法更改 '%s' 的所有权: %s\n", full_path, strerror(errno));
// 不要因为单个文件失败而停止整个过程
} else {
printf("已更改 '%s' 的所有权\n", full_path);
}

// 如果是目录,则递归处理
struct stat sb;
if (stat(full_path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
change_ownership_recursive(full_path, uid, gid);
}
}

closedir(dir);
return 0;
}

int main(int argc, char *argv&#91;]) {
if (argc < 4) {
fprintf(stderr, "用法: %s <目录路径> <用户名或UID> <组名或GID> &#91;-r]\n", argv&#91;0]);
fprintf(stderr, " -r: 递归更改子目录中所有文件\n");
fprintf(stderr, " 使用 -1 表示不更改 UID 或 GID\n");
fprintf(stderr, " 示例: %s /home/newuser alice developers\n", argv&#91;0]);
fprintf(stderr, " %s /data -1 mygroup -r\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

if (!is_root()) {
fprintf(stderr, "警告: 你可能需要 root 权限来更改文件所有权。\n");
fprintf(stderr, "当前有效 UID: %d\n", geteuid());
}

const char *path = argv&#91;1];
const char *user_str = argv&#91;2];
const char *group_str = argv&#91;3];
int recursive = 0;

// 检查是否有 -r 标志
for (int i = 4; i < argc; i++) {
if (strcmp(argv&#91;i], "-r") == 0) {
recursive = 1;
break;
}
}

uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;

// 解析用户
if (strcmp(user_str, "-1") != 0) {
struct passwd *pw = getpwnam(user_str); // 按用户名查找
if (pw) {
uid = pw->pw_uid;
} else {
// 尝试按 UID 解析
char *endptr;
uid = (uid_t) strtoul(user_str, &endptr, 10);
if (*endptr != '\0') {
fprintf(stderr, "错误: 无效的用户名或 UID: %s\n", user_str);
exit(EXIT_FAILURE);
}
}
}

// 解析组
if (strcmp(group_str, "-1") != 0) {
struct group *gr = getgrnam(group_str); // 按组名查找
if (gr) {
gid = gr->gr_gid;
} else {
// 尝试按 GID 解析
char *endptr;
gid = (gid_t) strtoul(group_str, &endptr, 10);
if (*endptr != '\0') {
fprintf(stderr, "错误: 无效的组名或 GID: %s\n", group_str);
exit(EXIT_FAILURE);
}
}
}

printf("准备更改 '%s' 的所有权:\n", path);
printf(" UID: %d (%s)\n", (int)uid, (uid==(uid_t)-1) ? "不更改" : user_str);
printf(" GID: %d (%s)\n", (int)gid, (gid==(gid_t)-1) ? "不更改" : group_str);
printf(" 递归: %s\n", recursive ? "是" : "否");

// 执行所有权更改
int result;
if (recursive) {
result = change_ownership_recursive(path, uid, gid);
} else {
result = chown(path, uid, gid);
if (result != -1) {
printf("已更改 '%s' 的所有权\n", path);
}
}

if (result == -1) {
perror("chown 失败");
exit(EXIT_FAILURE);
}

printf("所有权更改操作完成。\n");
return 0;
}

代码解释:

is_root 函数检查当前进程的有效用户 ID 是否为 0 (root)。

change_ownership_recursive 函数使用 opendir, readdir 遍历目录,并对每个文件/子目录递归调用 chown。

main 函数处理命令行参数,支持按用户名/组名或 UID/GID 指定,并支持递归选项 -r。

它使用 getpwnam 和 getgrnam 将用户名和组名解析为 UID/GID。

根据是否指定 -r 标志,选择调用普通的 chown 或递归函数。

示例 3:错误处理和权限检查

这个例子重点演示 chown 可能遇到的各种错误情况及其处理。

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

void demonstrate_chown_errors() {
printf("=== Chown 错误处理演示 ===\n");

// 1. 尝试更改不存在的文件
printf("\n1. 尝试更改不存在的文件:\n");
if (chown("/nonexistent/file.txt", 1000, 1000) == -1) {
printf(" 错误: %s\n", strerror(errno));
// 通常返回 ENOENT
}

// 2. 创建一个测试文件
const char *test_file = "chown_test.txt";
FILE *fp = fopen(test_file, "w");
if (fp) {
fprintf(fp, "Test file for chown\n");
fclose(fp);
printf("\n2. 创建测试文件: %s\n", test_file);
} else {
perror("创建测试文件失败");
return;
}

// 3. 非特权用户尝试将文件给其他用户 (通常会失败)
printf("\n3. 非特权用户尝试将文件所有权转移给其他用户:\n");
uid_t current_uid = getuid();
uid_t target_uid = (current_uid == 1000) ? 1001 : 1000; // 假设另一个用户

printf(" 当前用户 UID: %d\n", current_uid);
printf(" 尝试更改为 UID: %d\n", target_uid);

if (chown(test_file, target_uid, (gid_t)-1) == -1) {
printf(" 错误: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 说明: 非特权用户不能将文件所有权转移给其他用户\n");
}
} else {
printf(" 更改成功 (这在非 root 用户下不太可能)\n");
}

// 4. 尝试更改只读文件系统上的文件
printf("\n4. 尝试更改只读文件系统上的文件:\n");
// 注意:这需要一个实际的只读文件系统挂载点来测试
// 在 /proc 或 /sys 上尝试通常会返回 EROFS
if (chown("/proc/version", 0, 0) == -1) {
printf(" 错误: %s\n", strerror(errno));
if (errno == EROFS) {
printf(" 说明: 不能更改只读文件系统上的文件所有权\n");
}
}

// 5. 正常的组更改(如果可能)
printf("\n5. 尝试更改文件组所有权:\n");
gid_t current_gid = getgid();
printf(" 当前组 GID: %d\n", current_gid);

// 尝试更改为自己所在的组(更可能成功)
if (chown(test_file, (uid_t)-1, current_gid) == -1) {
printf(" 更改组失败: %s\n", strerror(errno));
} else {
printf(" 组所有权更改成功\n");
}

// 清理测试文件
unlink(test_file);
printf("\n6. 清理完成\n");
}

int main() {
printf("当前进程信息:\n");
printf(" 实际 UID: %d\n", getuid());
printf(" 有效 UID: %d\n", geteuid());
printf(" 实际 GID: %d\n", getgid());
printf(" 有效 GID: %d\n", getegid());

demonstrate_chown_errors();

printf("\n=== 总结 ===\n");
printf("chown 常见错误:\n");
printf(" EPERM: 权限不足(非 root 用户试图更改所有者)\n");
printf(" ENOENT: 文件不存在\n");
printf(" EROFS: 只读文件系统\n");
printf(" EACCES: 搜索路径被拒绝\n");
printf(" EIO: I/O 错误\n\n");

printf("权限规则:\n");
printf(" - Root 用户可以更改任何文件的所有者和组\n");
printf(" - 普通用户通常只能更改自己拥有的文件的组\n");
printf(" - 普通用户不能将文件所有权转移给其他用户\n");

return 0;
}

代码解释:

demonstrate_chown_errors 函数依次演示了 chown 可能遇到的各种典型错误。

首先尝试操作不存在的文件,展示 ENOENT 错误。

创建测试文件用于后续演示。

演示非特权用户尝试将文件所有权转移给其他用户的 EPERM 错误。

尝试更改只读文件系统上文件的 EROFS 错误。

展示正常的组更改操作。

最后清理测试文件并总结常见的错误类型和权限规则。

编译和运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 编译示例
gcc -o chown_example1 chown_example1.c
gcc -o chown_example2 chown_example2.c
gcc -o chown_example3 chown_example3.c

# 运行示例 (需要适当权限)
# 示例1: 基本用法
touch testfile.txt
./chown_example1 testfile.txt 1000 1000 # 需要 root 权限
./chown_example1 testfile.txt -1 1000 # 可能不需要 root 权限

# 示例2: 系统管理
./chown_example2 /tmp/mydir alice developers -r

# 示例3: 错误处理
./chown_example3

总结:

chown 函数是 Linux 系统管理中不可或缺的工具,用于精确控制文件和目录的归属权。理解其参数、返回值和权限模型对于编写健壮的系统程序至关重要。务必注意权限限制和潜在的错误情况,并在实际使用中谨慎操作,特别是在生产环境中。

chroot系统调用及示例

chroot函数详解

  1. 函数介绍

chroot函数是Linux系统中用于改变进程根目录的系统调用函数,它的名字来源于”change root”。可以把chroot想象成一个”虚拟监狱管理员”,它能够为进程创建一个隔离的文件系统环境,让进程认为某个指定的目录就是文件系统的根目录(/)。

chroot通过改变进程的根目录视图,创建了一个受限的执行环境。在这个环境中,进程无法访问指定根目录之外的文件系统,从而提供了一定程度的安全隔离。这就像给进程戴上了一副”有色眼镜”,让它只能看到特定范围内的文件系统。

重要说明: chroot本身不是安全边界,经验丰富的攻击者可能通过各种方式”跳出”chroot环境。

使用场景:

  • 系统维护和修复

  • 软件构建环境隔离

  • 测试环境搭建

  • 简单的沙箱环境

  • 系统恢复和救援

  • 旧版容器技术的基础

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

int chroot(const char *path);

  1. 功能

chroot函数的主要功能是改变调用进程及其子进程的根目录。调用成功后,指定的目录将成为新的文件系统根目录(/),所有相对路径和绝对路径的解析都会基于这个新的根目录。

  1. 参数

path: 新的根目录路径

  • 类型:const char*

  • 含义:指向新根目录的路径字符串

  • 该路径必须是一个已存在的目录

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

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

  • EACCES:权限不足(需要CAP_SYS_CHROOT能力)

  • EBUSY:当前目录是文件系统的根目录且忙

  • EFAULT:path指向无效内存

  • EIO:I/O错误

  • ELOOP:符号链接循环

  • ENAMETOOLONG:路径名过长

  • ENOENT:目录不存在

  • ENOTDIR:path不是目录

  • EPERM:操作不被允许

  1. 相似函数或关联函数
  • chdir(): 改变当前工作目录

  • pivot_root(): 更现代的根目录切换函数

  • mount(): 挂载文件系统

  • unshare(): 创建新的命名空间

  • clone(): 创建进程时指定命名空间

  • setuid()/setgid(): 改变用户/组ID

  • capset(): 设置进程能力

  1. 示例代码

示例1:基础chroot使用 - 简单环境切换

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

// 创建chroot环境
int create_chroot_environment(const char* chroot_path) {
printf("创建chroot环境: %s\n", chroot_path);

// 创建根目录
if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
perror("创建根目录失败");
return -1;
}

// 创建基本目录结构
const char* dirs&#91;] = {
"bin", "lib", "lib64", "usr", "etc", "dev", "tmp", "proc"
};

for (int i = 0; i < 8; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
return -1;
}
}

// 创建基本设备文件(简化版本)
char dev_path&#91;256];
snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);

// 创建null设备节点
char null_path&#91;256];
snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败: %s\n", strerror(errno));
}

// 创建zero设备节点
char zero_path&#91;256];
snprintf(zero_path, sizeof(zero_path), "%s/dev/zero", chroot_path);
if (mknod(zero_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/zero失败: %s\n", strerror(errno));
}

printf("chroot环境创建完成\n");
return 0;
}

// 显示当前目录结构
void show_directory_tree(const char* path, int depth) {
DIR* dir = opendir(path);
if (dir == NULL) {
printf("无法打开目录: %s\n", path);
return;
}

// 显示缩进
for (int i = 0; i < depth; i++) {
printf(" ");
}
printf("%s/\n", path);

struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

// 显示文件/目录
for (int i = 0; i < depth + 1; i++) {
printf(" ");
}
printf("%s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
}

closedir(dir);
}

// 显示文件系统信息
void show_filesystem_info() {
char cwd&#91;1024];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("当前工作目录: %s\n", cwd);
}

// 显示根目录内容
printf("根目录内容:\n");
DIR* root_dir = opendir("/");
if (root_dir) {
struct dirent* entry;
int count = 0;
while ((entry = readdir(root_dir)) != NULL && count < 10) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" %s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
count++;
}
}
if (count >= 10) {
printf(" ... (更多文件)\n");
}
closedir(root_dir);
}
}

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

const char* chroot_dir = "/tmp/my_chroot";

// 检查是否具有root权限
if (geteuid() != 0) {
printf("警告: chroot需要root权限运行\n");
printf("请使用sudo运行此程序\n");
exit(EXIT_FAILURE);
}

// 创建chroot环境
if (create_chroot_environment(chroot_dir) == -1) {
exit(EXIT_FAILURE);
}

printf("\n1. chroot前的文件系统状态:\n");
show_filesystem_info();
show_directory_tree(chroot_dir, 0);

// 获取当前工作目录
char original_cwd&#91;1024];
if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) {
perror("获取当前目录失败");
exit(EXIT_FAILURE);
}
printf("原始工作目录: %s\n", original_cwd);

// 执行chroot
printf("\n2. 执行chroot操作:\n");
printf("切换根目录到: %s\n", chroot_dir);

if (chroot(chroot_dir) == -1) {
perror("chroot失败");
exit(EXIT_FAILURE);
}

printf("✓ chroot操作成功\n");

// 改变工作目录到新的根目录
if (chdir("/") == -1) {
perror("改变工作目录失败");
exit(EXIT_FAILURE);
}

printf("\n3. chroot后的文件系统状态:\n");
show_filesystem_info();

// 创建一些测试文件
printf("\n4. 在chroot环境中创建文件:\n");
int fd = open("/test_file.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "这是chroot环境中的测试文件\n", 26);
close(fd);
printf("创建文件: /test_file.txt\n");
}

// 创建目录
if (mkdir("/mydir", 0755) == 0) {
printf("创建目录: /mydir\n");
}

// 显示chroot环境内容
show_directory_tree("/", 0);

// 尝试访问原始系统文件(应该失败)
printf("\n5. 尝试访问原始系统文件:\n");
if (access("/etc/passwd", F_OK) == -1) {
printf("✓ 无法访问原始系统文件 /etc/passwd (预期行为)\n");
} else {
printf("✗ 仍然可以访问原始系统文件\n");
}

// 清理测试文件
unlink("/test_file.txt");
rmdir("/mydir");

printf("\n=== 基础chroot演示完成 ===\n");
printf("注意: 此程序在chroot环境中结束\n");

return 0;
}

示例2:安全chroot实现 - 防止逃逸

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

// 安全chroot函数
int secure_chroot(const char* new_root) {
struct stat root_stat, cwd_stat;
char cwd&#91;4096];

printf("执行安全chroot到: %s\n", new_root);

// 1. 验证新根目录存在且是目录
if (stat(new_root, &root_stat) == -1) {
perror("无法访问根目录");
return -1;
}

if (!S_ISDIR(root_stat.st_mode)) {
fprintf(stderr, "指定路径不是目录\n");
return -1;
}

// 2. 验证新根目录权限
if (access(new_root, R_OK | X_OK) == -1) {
perror("根目录权限不足");
return -1;
}

// 3. 改变当前工作目录到根目录
if (chdir(new_root) == -1) {
perror("改变到根目录失败");
return -1;
}

// 4. 获取当前目录的inode信息
if (getcwd(cwd, sizeof(cwd)) == NULL) {
perror("获取当前目录失败");
return -1;
}

if (stat(".", &cwd_stat) == -1) {
perror("获取当前目录状态失败");
return -1;
}

// 5. 执行chroot
if (chroot(".") == -1) {
perror("chroot失败");
return -1;
}

// 6. 再次改变到根目录(防止某些逃逸技术)
if (chdir("/") == -1) {
perror("最终改变目录失败");
return -1;
}

printf("✓ 安全chroot完成\n");
return 0;
}

// 在chroot环境中运行的函数
void run_in_chroot() {
printf("\n=== 在chroot环境中运行 ===\n");

// 显示环境信息
printf("进程ID: %d\n", getpid());
printf("父进程ID: %d\n", getppid());

char cwd&#91;4096];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("当前工作目录: %s\n", cwd);
}

// 显示用户信息
printf("用户ID: %d\n", getuid());
printf("有效用户ID: %d\n", geteuid());
printf("组ID: %d\n", getgid());

// 显示根目录内容
printf("根目录内容:\n");
DIR* dir = opendir("/");
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" %s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");
}
}
closedir(dir);
}

// 创建测试文件
int fd = open("/chroot_test.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* test_content = "Chroot环境测试文件\n创建时间: ";
write(fd, test_content, strlen(test_content));

// 添加时间戳
time_t now = time(NULL);
char time_str&#91;64];
snprintf(time_str, sizeof(time_str), "%s", ctime(&now));
// 移除换行符
char* newline = strchr(time_str, '\n');
if (newline) *newline = '\0';
write(fd, time_str, strlen(time_str));
write(fd, "\n", 1);
close(fd);
printf("创建测试文件: /chroot_test.txt\n");
}

// 显示测试文件内容
fd = open("/chroot_test.txt", O_RDONLY);
if (fd != -1) {
char buffer&#91;256];
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);
}

// 演示环境隔离
printf("\n环境隔离测试:\n");

// 尝试访问原始系统文件
const char* system_files&#91;] = {
"/etc/passwd",
"/etc/shadow",
"/proc/1/cmdline",
"/sys/kernel",
"/dev/sda"
};

for (int i = 0; i < 5; i++) {
if (access(system_files&#91;i], F_OK) == 0) {
printf(" 能够访问: %s\n", system_files&#91;i]);
} else {
printf(" 无法访问: %s (%s)\n", system_files&#91;i], strerror(errno));
}
}

// 清理测试文件
unlink("/chroot_test.txt");
}

// 演示chroot逃逸防护
void demonstrate_escape_protection() {
printf("\n=== chroot逃逸防护演示 ===\n");

// 这些是常见的chroot逃逸尝试
printf("尝试常见的逃逸方法:\n");

// 1. 尝试通过..访问上级目录
if (chdir("..") == 0) {
char cwd&#91;4096];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf(" cd .. 后的目录: %s\n", cwd);
}
// 回到根目录
chdir("/");
} else {
printf(" cd .. 失败 (预期行为)\n");
}

// 2. 尝试通过绝对路径访问
if (access("/etc/passwd", F_OK) == 0) {
printf(" 能够访问 /etc/passwd (可能存在问题)\n");
} else {
printf(" 无法访问 /etc/passwd (正常隔离)\n");
}

// 3. 尝试创建符号链接到外部
if (symlink("/etc/passwd", "/passwd_link") == 0) {
printf(" 创建符号链接成功\n");
// 测试符号链接是否有效
if (access("/passwd_link", F_OK) == 0) {
printf(" 符号链接指向有效文件\n");
} else {
printf(" 符号链接无效或被隔离\n");
}
unlink("/passwd_link");
} else {
printf(" 无法创建符号链接 (正常)\n");
}
}

int main() {
printf("=== 安全chroot实现示例 ===\n");

// 检查权限
if (geteuid() != 0) {
printf("错误: 此程序需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/secure_chroot";

// 创建安全的chroot环境
printf("1. 创建安全chroot环境:\n");

// 创建基本目录结构
const char* dirs&#91;] = {"bin", "etc", "dev", "tmp", "usr", "lib", "lib64"};
if (mkdir(chroot_path, 0755) == -1 && errno != EEXIST) {
perror("创建根目录失败");
exit(EXIT_FAILURE);
}

for (int i = 0; i < 7; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
exit(EXIT_FAILURE);
}
}

// 创建基本设备文件
char null_path&#91;256];
snprintf(null_path, sizeof(null_path), "%s/dev/null", chroot_path);
if (mknod(null_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败\n");
}

printf("chroot环境创建完成: %s\n", chroot_path);

// 执行安全chroot
printf("\n2. 执行安全chroot:\n");
if (secure_chroot(chroot_path) == -1) {
exit(EXIT_FAILURE);
}

// 在chroot环境中运行
run_in_chroot();

// 演示逃逸防护
demonstrate_escape_protection();

// 显示最终状态
printf("\n=== chroot环境演示完成 ===\n");
printf("当前仍在chroot环境中\n");

return 0;
}

示例3:chroot环境构建与程序执行

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

// 复制文件到chroot环境
int copy_file_to_chroot(const char* src, const char* dst_chroot, const char* dst_path) {
char full_dst_path&#91;512];
snprintf(full_dst_path, sizeof(full_dst_path), "%s%s", dst_chroot, dst_path);

// 确保目标目录存在
char* last_slash = strrchr(full_dst_path, '/');
if (last_slash) {
*last_slash = '\0';
// 创建目录(简化实现)
mkdir(full_dst_path, 0755);
*last_slash = '/';
}

// 打开源文件
int src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
printf("警告: 无法打开源文件 %s: %s\n", src, strerror(errno));
return -1;
}

// 创建目标文件
int dst_fd = open(full_dst_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
if (dst_fd == -1) {
printf("警告: 无法创建目标文件 %s: %s\n", full_dst_path, strerror(errno));
close(src_fd);
return -1;
}

// 复制文件内容
char buffer&#91;8192];
ssize_t bytes_read, bytes_written;

while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("写入目标文件失败");
close(src_fd);
close(dst_fd);
return -1;
}
}

close(src_fd);
close(dst_fd);

printf("复制文件: %s -> %s\n", src, full_dst_path);
return 0;
}

// 构建基本的chroot环境
int build_basic_chroot(const char* chroot_path) {
printf("构建基本chroot环境: %s\n", chroot_path);

// 创建目录结构
const char* dirs&#91;] = {
"", "bin", "sbin", "etc", "dev", "usr", "usr/bin",
"usr/sbin", "lib", "lib64", "tmp", "var", "var/tmp"
};

for (int i = 0; i < 13; i++) {
char full_path&#91;256];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
if (errno != EEXIST) {
printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
}
}
}

// 创建基本设备文件
char dev_path&#91;256];
snprintf(dev_path, sizeof(dev_path), "%s/dev/null", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 3)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/null失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/zero", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 5)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/zero失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/random", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 8)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/random失败\n");
}

snprintf(dev_path, sizeof(dev_path), "%s/dev/urandom", chroot_path);
if (mknod(dev_path, S_IFCHR | 0666, makedev(1, 9)) == -1 && errno != EEXIST) {
printf("警告: 创建/dev/urandom失败\n");
}

// 复制基本命令(根据系统实际情况调整)
printf("复制基本命令...\n");

// 复制shell
copy_file_to_chroot("/bin/sh", chroot_path, "/bin/sh");

// 复制基本命令
const char* basic_commands&#91;] = {
"/bin/ls", "/bin/cat", "/bin/echo", "/bin/pwd",
"/usr/bin/id", "/bin/ps"
};

for (int i = 0; i < 6; i++) {
copy_file_to_chroot(basic_commands&#91;i], chroot_path, basic_commands&#91;i]);
}

// 创建基本配置文件
char etc_passwd&#91;256];
snprintf(etc_passwd, sizeof(etc_passwd), "%s/etc/passwd", chroot_path);
int fd = open(etc_passwd, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* passwd_content =
"root:x:0:0:root:/root:/bin/sh\n"
"nobody:x:65534:65534:nobody:/:/bin/sh\n";
write(fd, passwd_content, strlen(passwd_content));
close(fd);
printf("创建 /etc/passwd\n");
}

char etc_group&#91;256];
snprintf(etc_group, sizeof(etc_group), "%s/etc/group", chroot_path);
fd = open(etc_group, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* group_content =
"root:x:0:\n"
"nobody:x:65534:\n";
write(fd, group_content, strlen(group_content));
close(fd);
printf("创建 /etc/group\n");
}

printf("基本chroot环境构建完成\n");
return 0;
}

// 在chroot环境中执行命令
int execute_in_chroot(const char* chroot_path, const char* command) {
pid_t pid = fork();

if (pid == -1) {
perror("fork失败");
return -1;
}

if (pid == 0) {
// 子进程
// 执行chroot
if (chroot(chroot_path) == -1) {
perror("chroot失败");
exit(EXIT_FAILURE);
}

// 改变到根目录
if (chdir("/") == -1) {
perror("chdir失败");
exit(EXIT_FAILURE);
}

// 执行命令
execl("/bin/sh", "sh", "-c", command, (char*)NULL);
perror("执行命令失败");
exit(EXIT_FAILURE);
} else {
// 父进程等待子进程结束
int status;
waitpid(pid, &status, 0);

if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("命令执行完成,退出码: %d\n", exit_code);
return exit_code;
} else if (WIFSIGNALED(status)) {
int signal = WTERMSIG(status);
printf("命令被信号终止: %d\n", signal);
return -1;
}
}

return 0;
}

// 显示chroot环境内容
void show_chroot_contents(const char* chroot_path) {
printf("\nchroot环境内容:\n");

DIR* dir = opendir(chroot_path);
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" /%s%s\n", entry->d_name,
entry->d_type == DT_DIR ? "/" : "");

// 显示子目录内容(仅一层)
if (entry->d_type == DT_DIR) {
char sub_path&#91;512];
snprintf(sub_path, sizeof(sub_path), "%s/%s", chroot_path, entry->d_name);
DIR* sub_dir = opendir(sub_path);
if (sub_dir) {
struct dirent* sub_entry;
int count = 0;
while ((sub_entry = readdir(sub_dir)) != NULL && count < 5) {
if (strcmp(sub_entry->d_name, ".") != 0 &&
strcmp(sub_entry->d_name, "..") != 0) {
printf(" %s%s\n", sub_entry->d_name,
sub_entry->d_type == DT_DIR ? "/" : "");
count++;
}
}
if (count >= 5) {
printf(" ...\n");
}
closedir(sub_dir);
}
}
}
}
closedir(dir);
}
}

int main() {
printf("=== chroot环境构建与程序执行示例 ===\n");

if (geteuid() != 0) {
printf("错误: 此程序需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/full_chroot";

// 构建chroot环境
printf("1. 构建完整的chroot环境:\n");
if (build_basic_chroot(chroot_path) == -1) {
exit(EXIT_FAILURE);
}

show_chroot_contents(chroot_path);

// 在chroot环境中执行命令
printf("\n2. 在chroot环境中执行命令:\n");

// 执行基本命令
const char* commands&#91;] = {
"echo 'Hello from chroot!'",
"ls -la /",
"pwd",
"id",
"cat /etc/passwd"
};

for (int i = 0; i < 5; i++) {
printf("\n执行命令: %s\n", commands&#91;i]);
printf("--- 输出开始 ---\n");
execute_in_chroot(chroot_path, commands&#91;i]);
printf("--- 输出结束 ---\n");
}

// 创建和运行简单脚本
printf("\n3. 创建和运行脚本:\n");

// 创建脚本文件
char script_path&#91;256];
snprintf(script_path, sizeof(script_path), "%s/test_script.sh", chroot_path);
int fd = open(script_path, O_CREAT | O_WRONLY | O_TRUNC, 0755);
if (fd != -1) {
const char* script_content =
"#!/bin/sh\n"
"echo '=== 测试脚本开始 ==='\n"
"echo '当前时间:' $(date)\n"
"echo '当前用户:' $(id)\n"
"echo '当前目录:' $(pwd)\n"
"ls -la /\n"
"echo '=== 测试脚本结束 ==='\n";
write(fd, script_content, strlen(script_content));
close(fd);
printf("创建测试脚本: /test_script.sh\n");
}

// 执行脚本
printf("执行测试脚本:\n");
printf("--- 脚本输出开始 ---\n");
execute_in_chroot(chroot_path, "/test_script.sh");
printf("--- 脚本输出结束 ---\n");

// 演示安全性
printf("\n4. 安全性演示:\n");

// 尝试访问宿主系统文件
printf("尝试访问宿主系统文件:\n");
const char* dangerous_commands&#91;] = {
"ls -la /etc",
"cat /etc/shadow 2>/dev/null || echo '无法访问/etc/shadow'",
"ls -la /root 2>/dev/null || echo '无法访问/root'",
"find /proc -maxdepth 2 2>/dev/null | head -5"
};

for (int i = 0; i < 4; i++) {
printf("\n执行安全测试命令: %s\n", dangerous_commands&#91;i]);
printf("--- 输出开始 ---\n");
execute_in_chroot(chroot_path, dangerous_commands&#91;i]);
printf("--- 输出结束 ---\n");
}

// 清理测试脚本
unlink(script_path);

printf("\n=== chroot环境演示完成 ===\n");
printf("环境路径: %s\n", chroot_path);
printf("注意: 环境文件仍保留在系统中\n");

return 0;
}

示例4:chroot高级应用 - 系统维护工具

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 <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <mntent.h>

// chroot环境管理器
typedef struct {
char path&#91;512];
int is_active;
pid_t original_pid;
time_t create_time;
} chroot_manager_t;

static chroot_manager_t manager = {0};

// 创建完整的系统恢复环境
int create_recovery_environment(const char* chroot_path) {
printf("创建系统恢复环境: %s\n", chroot_path);

// 创建完整的目录结构
const char* essential_dirs&#91;] = {
"", "bin", "sbin", "etc", "dev", "proc", "sys", "tmp",
"var", "var/log", "var/run", "usr", "usr/bin", "usr/sbin",
"usr/lib", "lib", "lib64", "mnt", "media", "root", "home"
};

for (int i = 0; i < 21; i++) {
char full_path&#91;512];
snprintf(full_path, sizeof(full_path), "%s/%s", chroot_path, essential_dirs&#91;i]);
if (mkdir(full_path, 0755) == -1 && errno != EEXIST) {
printf("警告: 创建目录失败 %s: %s\n", full_path, strerror(errno));
}
}

// 创建设备文件
printf("创建基本设备文件...\n");
struct {
const char* path;
int major, minor;
mode_t mode;
} devices&#91;] = {
{"/dev/null", 1, 3, S_IFCHR | 0666},
{"/dev/zero", 1, 5, S_IFCHR | 0666},
{"/dev/full", 1, 7, S_IFCHR | 0666},
{"/dev/random", 1, 8, S_IFCHR | 0666},
{"/dev/urandom", 1, 9, S_IFCHR | 0666},
{"/dev/tty", 5, 0, S_IFCHR | 0666}
};

for (int i = 0; i < 6; i++) {
char full_path&#91;512];
snprintf(full_path, sizeof(full_path), "%s%s", chroot_path, devices&#91;i].path);
if (mknod(full_path, devices&#91;i].mode, makedev(devices&#91;i].major, devices&#91;i].minor)) == -1 && errno != EEXIST) {
printf("警告: 创建设备文件失败 %s: %s\n", full_path, strerror(errno));
}
}

// 复制系统管理工具
printf("复制系统管理工具...\n");
const char* sysadmin_tools&#91;] = {
"/bin/sh", "/bin/bash", "/bin/ls", "/bin/cat", "/bin/cp",
"/bin/mv", "/bin/rm", "/bin/mkdir", "/bin/rmdir", "/bin/ln",
"/bin/find", "/bin/grep", "/bin/ps", "/bin/kill", "/sbin/ifconfig",
"/sbin/ip", "/sbin/fsck", "/sbin/mkfs", "/bin/mount", "/bin/umount",
"/usr/bin/vi", "/usr/bin/nano", "/bin/tar", "/usr/bin/gzip",
"/usr/bin/bzip2", "/bin/df", "/bin/du", "/usr/bin/top"
};

int copied_count = 0;
for (int i = 0; i < 28; i++) {
if (access(sysadmin_tools&#91;i], F_OK) == 0) {
if (copy_file_to_chroot(sysadmin_tools&#91;i], chroot_path, sysadmin_tools&#91;i]) == 0) {
copied_count++;
}
}
}
printf("成功复制 %d 个系统工具\n", copied_count);

// 创建配置文件
printf("创建基本配置文件...\n");

// /etc/passwd
char passwd_path&#91;512];
snprintf(passwd_path, sizeof(passwd_path), "%s/etc/passwd", chroot_path);
int fd = open(passwd_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* passwd_content =
"root:x:0:0:root:/root:/bin/bash\n"
"admin:x:1000:1000:Admin User:/home/admin:/bin/bash\n";
write(fd, passwd_content, strlen(passwd_content));
close(fd);
}

// /etc/group
char group_path&#91;512];
snprintf(group_path, sizeof(group_path), "%s/etc/group", chroot_path);
fd = open(group_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* group_content =
"root:x:0:\n"
"admin:x:1000:\n";
write(fd, group_content, strlen(group_content));
close(fd);
}

// /etc/hosts
char hosts_path&#91;512];
snprintf(hosts_path, sizeof(hosts_path), "%s/etc/hosts", chroot_path);
fd = open(hosts_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* hosts_content =
"127.0.0.1\tlocalhost\n"
"::1\tlocalhost ip6-localhost ip6-loopback\n";
write(fd, hosts_content, strlen(hosts_content));
close(fd);
}

printf("系统恢复环境创建完成\n");
return 0;
}

// 在chroot中挂载特殊文件系统
int mount_special_filesystems(const char* chroot_path) {
printf("挂载特殊文件系统...\n");

char proc_path&#91;512], sys_path&#91;512], dev_path&#91;512];
snprintf(proc_path, sizeof(proc_path), "%s/proc", chroot_path);
snprintf(sys_path, sizeof(sys_path), "%s/sys", chroot_path);
snprintf(dev_path, sizeof(dev_path), "%s/dev", chroot_path);

// 挂载/proc
if (mount("proc", proc_path, "proc", 0, NULL) == -1) {
printf("警告: 挂载/proc失败: %s\n", strerror(errno));
} else {
printf("挂载 /proc 到 %s\n", proc_path);
}

// 挂载/sys
if (mount("sysfs", sys_path, "sysfs", 0, NULL) == -1) {
printf("警告: 挂载/sys失败: %s\n", strerror(errno));
} else {
printf("挂载 /sys 到 %s\n", sys_path);
}

// 创建并挂载tmpfs到/tmp
char tmp_path&#91;512];
snprintf(tmp_path, sizeof(tmp_path), "%s/tmp", chroot_path);
if (mount("tmpfs", tmp_path, "tmpfs", 0, "size=100M") == -1) {
printf("警告: 挂载/tmp失败: %s\n", strerror(errno));
} else {
printf("挂载 tmpfs 到 %s\n", tmp_path);
}

return 0;
}

// 卸载特殊文件系统
int unmount_special_filesystems(const char* chroot_path) {
printf("卸载特殊文件系统...\n");

char mounts&#91;]&#91;512] = {
"%s/tmp",
"%s/sys",
"%s/proc"
};

for (int i = 0; i < 3; i++) {
char mount_point&#91;512];
snprintf(mount_point, sizeof(mount_point), mounts&#91;i], chroot_path);
if (umount(mount_point) == -1) {
if (errno != EINVAL) { // 忽略未挂载的错误
printf("警告: 卸载 %s 失败: %s\n", mount_point, strerror(errno));
}
} else {
printf("卸载 %s\n", mount_point);
}
}

return 0;
}

// 初始化chroot管理器
int init_chroot_manager(const char* chroot_path) {
strncpy(manager.path, chroot_path, sizeof(manager.path) - 1);
manager.is_active = 0;
manager.original_pid = getpid();
manager.create_time = time(NULL);

printf("初始化chroot管理器\n");
printf(" 环境路径: %s\n", manager.path);
printf(" 管理器PID: %d\n", manager.original_pid);

return 0;
}

// 激活chroot环境
int activate_chroot_environment() {
if (manager.is_active) {
printf("chroot环境已激活\n");
return 0;
}

printf("激活chroot环境: %s\n", manager.path);

// 挂载特殊文件系统
mount_special_filesystems(manager.path);

// 执行chroot
if (chroot(manager.path) == -1) {
perror("chroot失败");
return -1;
}

// 改变到根目录
if (chdir("/") == -1) {
perror("chdir失败");
return -1;
}

manager.is_active = 1;
printf("✓ chroot环境已激活\n");

return 0;
}

// 交互式shell
int start_interactive_shell() {
printf("\n=== 启动交互式shell ===\n");
printf("提示: 输入 'exit' 退出shell\n");
printf("当前环境: chroot @ %s\n", manager.path);
printf("========================\n");

// 启动shell
execl("/bin/bash", "bash", "--norc", "--noprofile", (char*)NULL);

// 如果execl失败
perror("启动shell失败");
return -1;
}

// 执行系统维护任务
int perform_system_maintenance() {
printf("=== 系统维护任务 ===\n");

// 检查文件系统
printf("1. 检查文件系统:\n");
system("df -h");

// 检查磁盘使用情况
printf("\n2. 磁盘使用情况:\n");
system("du -sh /* 2>/dev/null | head -10");

// 检查进程
printf("\n3. 当前进程:\n");
system("ps aux --forest | head -15");

// 检查网络
printf("\n4. 网络状态:\n");
system("ip link show | head -10");

// 检查系统日志
printf("\n5. 系统日志检查:\n");
system("dmesg | tail -10");

return 0;
}

int main(int argc, char* argv&#91;]) {
printf("=== chroot高级应用 - 系统维护工具 ===\n");

if (geteuid() != 0) {
printf("错误: 此工具需要root权限运行\n");
exit(EXIT_FAILURE);
}

const char* chroot_path = "/tmp/recovery_chroot";

// 初始化管理器
init_chroot_manager(chroot_path);

// 检查命令行参数
if (argc > 1) {
if (strcmp(argv&#91;1], "create") == 0) {
// 创建恢复环境
printf("创建恢复环境...\n");
if (create_recovery_environment(chroot_path) == -1) {
exit(EXIT_FAILURE);
}
printf("恢复环境创建完成: %s\n", chroot_path);
return 0;
} else if (strcmp(argv&#91;1], "shell") == 0) {
// 激活并启动shell
printf("启动恢复shell...\n");
if (activate_chroot_environment() == -1) {
exit(EXIT_FAILURE);
}
start_interactive_shell();
return 0;
} else if (strcmp(argv&#91;1], "maintain") == 0) {
// 执行维护任务
if (activate_chroot_environment() == -1) {
exit(EXIT_FAILURE);
}
perform_system_maintenance();
return 0;
} else {
printf("用法: %s &#91;create|shell|maintain]\n", argv&#91;0]);
printf(" create - 创建恢复环境\n");
printf(" shell - 启动交互式shell\n");
printf(" maintain - 执行系统维护任务\n");
return 1;
}
}

// 交互式菜单
printf("\n系统维护工具菜单:\n");
printf("1. 创建恢复环境\n");
printf("2. 启动恢复shell\n");
printf("3. 执行系统维护\n");
printf("4. 退出\n");

int choice;
printf("请选择操作 (1-4): ");
if (scanf("%d", &choice) != 1) {
printf("输入错误\n");
return 1;
}

switch (choice) {
case 1:
printf("创建恢复环境...\n");
create_recovery_environment(chroot_path);
break;
case 2:
printf("启动恢复shell...\n");
activate_chroot_environment();
start_interactive_shell();
break;
case 3:
printf("执行系统维护...\n");
activate_chroot_environment();
perform_system_maintenance();
break;
case 4:
printf("退出工具\n");
break;
default:
printf("无效选择\n");
return 1;
}

printf("\n=== 系统维护工具结束 ===\n");

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 编译示例1
sudo gcc -o chroot_example1 chroot_example1.c
sudo ./chroot_example1

# 编译示例2
sudo gcc -o chroot_example2 chroot_example2.c
sudo ./chroot_example2

# 编译示例3
sudo gcc -o chroot_example3 chroot_example3.c
sudo ./chroot_example3

# 编译示例4
sudo gcc -o chroot_example4 chroot_example4.c
sudo ./chroot_example4 create
sudo ./chroot_example4 shell

重要注意事项

权限要求: chroot需要CAP_SYS_CHROOT能力,通常需要root权限

安全性限制: chroot不是安全边界,不能完全防止逃逸

目录验证: 必须确保新根目录的安全性和完整性

文件描述符: chroot不影响已打开的文件描述符

符号链接: 注意处理符号链接可能带来的安全问题

设备文件: 需要正确创建必要的设备文件

库依赖: 确保所需的共享库在chroot环境中可用

最佳实践

权限最小化: 在chroot后尽快降低权限

环境清理: 清理不必要的环境变量和文件描述符

目录验证: 验证新根目录的完整性和安全性

设备文件: 只创建必要的设备文件

库依赖: 确保所有依赖库都在chroot环境中

监控审计: 监控chroot环境中的活动

定期更新: 定期更新chroot环境中的软件包

通过这些示例,你可以理解chroot在系统管理和安全隔离方面的应用,虽然现代容器技术已经提供了更好的解决方案,但chroot仍然是一个重要的系统管理工具。

chroot系统调用详解, chroot函数使用示例, linux chroot命令说明, chroot函数作用与用法, 如何使用chroot改变根目录, chroot系统调用原理, linux中chroot函数详解, chroot命令教程, chroot函数参数解释, chroot实现隔离环境方法

clock_adjtime、clock_getres、clock_gettime、clock_nanosleep、clock_settime系统调用及示例

clock_adjtime - 调整时钟参数

函数介绍

clock_adjtime系统调用用于调整指定时钟的参数,主要用于精密时间同步。它可以设置时钟的频率调整、时间偏移等参数,常用于NTP客户端实现。

函数原型

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

int clock_adjtime(clockid_t clk_id, struct timex *buf);

功能

调整指定时钟的参数,包括频率、时间偏移等,用于精密时间同步。

参数

clockid_t clk_id: 时钟ID

  • CLOCK_REALTIME: 系统实时钟

  • CLOCK_TAI: 国际原子时

struct timex *buf: 指向timex结构体的指针,包含调整参数

返回值

  • 成功时返回状态码

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

特殊限制

  • 需要CAP_SYS_TIME能力

  • 通常需要root权限

相似函数

  • adjtimex(): 调整系统时钟

  • settimeofday(): 设置系统时间

示例代码

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

// 系统调用包装
static int clock_adjtime_wrapper(clockid_t clk_id, struct timex *buf) {
return syscall(__NR_clock_adjtime, clk_id, buf);
}

int main() {
struct timex tx;
int result;

printf("=== Clock_adjtime 函数示例 ===\n");
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 示例1: 获取当前时钟状态
printf("\n示例1: 获取时钟状态\n");

memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 仅查询状态

result = clock_adjtime_wrapper(CLOCK_REALTIME, &tx);
if (result == -1) {
if (errno == EPERM) {
printf(" 权限不足获取时钟状态: %s\n", strerror(errno));
printf(" 说明: 需要CAP_SYS_TIME能力或root权限\n");
} else {
printf(" 获取时钟状态失败: %s\n", strerror(errno));
}
} else {
printf(" 时钟状态获取成功\n");
printf(" 状态码: %d\n", result);
printf(" 频率偏移: %ld\n", tx.freq);
printf(" 最大误差: %ld\n", tx.maxerror);
printf(" 估计误差: %ld\n", tx.esterror);
}

// 示例2: 查询时钟参数(不修改)
printf("\n示例2: 查询时钟参数\n");

memset(&tx, 0, sizeof(tx));
tx.modes = 0;

result = clock_adjtime_wrapper(CLOCK_REALTIME, &tx);
if (result != -1) {
printf(" 时钟参数:\n");
printf(" 状态: %d\n", tx.status);
printf(" 频率偏移: %ld ppm\n", tx.freq / 65536); // 转换为ppm
printf(" 时间常数: %ld\n", tx.constant);
printf(" 精度: %ld ns\n", tx.precision);
printf(" 容差: %ld ppm\n", tx.tolerance / 65536); // 转换为ppm
}

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

// 使用无效的时钟ID
memset(&tx, 0, sizeof(tx));
result = clock_adjtime_wrapper(999, &tx);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时钟ID错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的指针
result = clock_adjtime_wrapper(CLOCK_REALTIME, NULL);
if (result == -1) {
if (errno == EFAULT) {
printf(" 无效指针错误处理正确: %s\n", strerror(errno));
}
}

// 示例4: 时钟类型说明
printf("\n示例4: 支持的时钟类型\n");
printf("CLOCK_REALTIME: 系统实时钟(wall-clock time)\n");
printf(" - 可以被手动设置\n");
printf(" - 受NTP调整影响\n");
printf(" - 用于日常时间表示\n\n");

printf("CLOCK_TAI: 国际原子时\n");
printf(" - 连续时间,无闰秒\n");
printf(" - 与时钟实时相差固定偏移\n");
printf(" - 用于精密时间计算\n\n");

// 示例5: NTP相关参数说明
printf("示例5: NTP相关参数说明\n");
printf("timex结构体重要字段:\n");
printf(" modes: 指定要设置的参数\n");
printf(" offset: 时间偏移(纳秒)\n");
printf(" freq: 频率偏移(scaled ppm)\n");
printf(" maxerror: 最大误差估计\n");
printf(" esterror: 误差估计\n");
printf(" status: 时钟状态标志\n");
printf(" constant: PLL时间常数\n");
printf(" precision: 时钟精度\n");
printf(" tolerance: 频率容差\n\n");

// 示例6: 权限和安全考虑
printf("示例6: 权限和安全考虑\n");
printf("使用clock_adjtime需要:\n");
printf("1. CAP_SYS_TIME能力\n");
printf("2. 或者root权限\n");
printf("3. 某些操作可能需要额外权限\n\n");

printf("安全注意事项:\n");
printf("1. 不当的时间调整可能影响系统稳定性\n");
printf("2. 频率调整过大可能导致时间跳跃\n");
printf("3. 应谨慎设置时间参数\n");
printf("4. 建议使用NTP守护进程进行时间同步\n\n");

// 示例7: 实际应用场景
printf("示例7: 实际应用场景\n");
printf("clock_adjtime主要用于:\n");
printf("1. NTP客户端实现\n");
printf("2. 精密时间同步服务\n");
printf("3. 科学计算时间校准\n");
printf("4. 金融系统时间同步\n");
printf("5. 分布式系统时间协调\n\n");

printf("典型使用流程:\n");
printf("1. 查询当前时钟状态\n");
printf("2. 计算需要的调整参数\n");
printf("3. 应用调整参数\n");
printf("4. 监控调整效果\n");
printf("5. 必要时进行微调\n\n");

printf("总结:\n");
printf("clock_adjtime是用于精密时钟调整的系统调用\n");
printf("主要用于NTP客户端和时间同步服务\n");
printf("需要适当的权限才能使用\n");
printf("不当使用可能导致系统时间异常\n");

return 0;
}

clock_getres - 获取时钟精度

函数介绍

clock_getres系统调用用于获取指定时钟的精度(分辨率)。它返回时钟能够表示的最小时间间隔。

函数原型

1
2
3
4
#include <time.h>

int clock_getres(clockid_t clk_id, struct timespec *res);

功能

获取指定时钟的精度,即能够表示的最小时间间隔。

参数

  • clockid_t clk_id: 时钟ID

  • struct timespec *res: 指向timespec结构体的指针,用于存储精度信息

返回值

  • 成功时返回0

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

特似函数

  • clock_gettime(): 获取时钟时间

  • gettimeofday(): 获取系统时间

示例代码

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

int main() {
struct timespec res;
int result;

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

// 示例1: 获取各种时钟的精度
printf("\n示例1: 不同时钟的精度\n");

// CLOCK_REALTIME
result = clock_getres(CLOCK_REALTIME, &res);
if (result == -1) {
printf(" CLOCK_REALTIME精度获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_REALTIME精度: %ld.%09ld 秒\n", res.tv_sec, res.tv_nsec);
printf(" 即: %ld 纳秒\n", res.tv_nsec);
}

// CLOCK_MONOTONIC
result = clock_getres(CLOCK_MONOTONIC, &res);
if (result == -1) {
printf(" CLOCK_MONOTONIC精度获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_MONOTONIC精度: %ld.%09ld 秒\n", res.tv_sec, res.tv_nsec);
printf(" 即: %ld 纳秒\n", res.tv_nsec);
}

// CLOCK_PROCESS_CPUTIME_ID
result = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &res);
if (result == -1) {
printf(" CLOCK_PROCESS_CPUTIME_ID精度获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_PROCESS_CPUTIME_ID精度: %ld.%09ld 秒\n", res.tv_sec, res.tv_nsec);
printf(" 即: %ld 纳秒\n", res.tv_nsec);
}

// CLOCK_THREAD_CPUTIME_ID
result = clock_getres(CLOCK_THREAD_CPUTIME_ID, &res);
if (result == -1) {
printf(" CLOCK_THREAD_CPUTIME_ID精度获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_THREAD_CPUTIME_ID精度: %ld.%09ld 秒\n", res.tv_sec, res.tv_nsec);
printf(" 即: %ld 纳秒\n", res.tv_nsec);
}

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

// 使用无效的时钟ID
result = clock_getres(999, &res);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时钟ID错误处理正确: %s\n", strerror(errno));
}
}

// 使用NULL指针
result = clock_getres(CLOCK_REALTIME, NULL);
if (result == 0) {
printf(" NULL指针参数被接受(用于查询时钟是否存在)\n");
}

// 示例3: 时钟类型说明
printf("\n示例3: 支持的时钟类型\n");
printf("CLOCK_REALTIME: 系统实时钟\n");
printf(" - 可以被设置和调整\n");
printf(" - 受NTP和手动调整影响\n");
printf(" - 用于获取当前时间\n\n");

printf("CLOCK_MONOTONIC: 单调时钟\n");
printf(" - 不会倒退\n");
printf(" - 不受系统时间调整影响\n");
printf(" - 用于测量时间间隔\n\n");

printf("CLOCK_PROCESS_CPUTIME_ID: 进程CPU时间\n");
printf(" - 测量进程使用的CPU时间\n");
printf(" - 通常有较高精度\n\n");

printf("CLOCK_THREAD_CPUTIME_ID: 线程CPU时间\n");
printf(" - 测量线程使用的CPU时间\n");
printf(" - 用于性能分析\n\n");

// 示例4: 精度对比
printf("示例4: 不同时钟精度对比\n");

clockid_t clocks&#91;] = {
CLOCK_REALTIME,
CLOCK_MONOTONIC,
CLOCK_PROCESS_CPUTIME_ID,
CLOCK_THREAD_CPUTIME_ID
};

const char *clock_names&#91;] = {
"CLOCK_REALTIME",
"CLOCK_MONOTONIC",
"CLOCK_PROCESS_CPUTIME_ID",
"CLOCK_THREAD_CPUTIME_ID"
};

for (int i = 0; i < 4; i++) {
if (clock_getres(clocks&#91;i], &res) == 0) {
printf(" %-25s: %10ld ns\n", clock_names&#91;i], res.tv_nsec);
}
}

// 示例5: 实际应用演示
printf("\n示例5: 实际应用演示\n");
printf("时钟精度对程序设计的影响:\n");

// 演示高精度计时
struct timespec start, end, diff;
if (clock_getres(CLOCK_MONOTONIC, &res) == 0) {
printf(" 使用CLOCK_MONOTONIC进行高精度计时:\n");
printf(" 理论精度: %ld 纳秒\n", res.tv_nsec);

// 进行简单计时演示
if (clock_gettime(CLOCK_MONOTONIC, &start) == 0) {
// 执行一些操作
volatile int sum = 0;
for (int i = 0; i < 1000; i++) {
sum += i;
}

if (clock_gettime(CLOCK_MONOTONIC, &end) == 0) {
// 计算时间差
if (end.tv_nsec < start.tv_nsec) {
diff.tv_sec = end.tv_sec - start.tv_sec - 1;
diff.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
} else {
diff.tv_sec = end.tv_sec - start.tv_sec;
diff.tv_nsec = end.tv_nsec - start.tv_nsec;
}

printf(" 实际测量时间: %ld.%09ld 秒\n", diff.tv_sec, diff.tv_nsec);
}
}
}

// 示例6: 性能考虑
printf("\n示例6: 性能考虑\n");
printf("不同精度时钟的性能特点:\n");
printf("1. 高精度时钟通常开销较大\n");
printf("2. 需要在精度和性能间平衡\n");
printf("3. 选择合适的时钟类型很重要\n");
printf("4. 避免不必要的高精度要求\n\n");

printf("时钟选择建议:\n");
printf("- 一般计时: CLOCK_REALTIME\n");
printf("- 性能测量: CLOCK_MONOTONIC\n");
printf("- CPU使用率: CLOCK_PROCESS_CPUTIME_ID\n");
printf("- 线程性能: CLOCK_THREAD_CPUTIME_ID\n\n");

printf("总结:\n");
printf("clock_getres用于查询时钟精度\n");
printf("不同类型的时钟有不同的精度\n");
printf("了解时钟精度有助于正确使用计时函数\n");
printf("合理选择时钟类型可以提高程序性能\n");

return 0;
}

clock_gettime - 获取时钟时间

函数介绍

clock_gettime系统调用用于获取指定时钟的当前时间。它比传统的gettimeofday提供了更高的精度和更多的时钟类型选择。

函数原型

1
2
3
4
#include <time.h>

int clock_gettime(clockid_t clk_id, struct timespec *tp);

功能

获取指定时钟的当前时间值。

参数

  • clockid_t clk_id: 时钟ID

  • struct timespec *tp: 指向timespec结构体的指针,用于存储时间信息

返回值

  • 成功时返回0

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

相似函数

  • gettimeofday(): 获取系统时间

  • time(): 获取秒级时间

  • clock_getres(): 获取时钟精度

示例代码

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

int main() {
struct timespec ts;
int result;

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

// 示例1: 获取各种时钟的时间
printf("\n示例1: 不同时钟的时间\n");

// CLOCK_REALTIME
result = clock_gettime(CLOCK_REALTIME, &ts);
if (result == -1) {
printf(" CLOCK_REALTIME时间获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_REALTIME时间: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);
printf(" 对应日期: %s", ctime(&ts.tv_sec));
}

// CLOCK_MONOTONIC
result = clock_gettime(CLOCK_MONOTONIC, &ts);
if (result == -1) {
printf(" CLOCK_MONOTONIC时间获取失败: %s\n", strerror(errno));
} else {
printf(" CLOCK_MONOTONIC时间: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);
}

// CLOCK_PROCESS_CPUTIME_ID
result = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
if (result == -1) {
printf(" CLOCK_PROCESS_CPUTIME_ID时间获取失败: %s\n", strerror(errno));
} else {
printf(" 进程CPU时间: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);
}

// CLOCK_THREAD_CPUTIME_ID
result = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
if (result == -1) {
printf(" CLOCK_THREAD_CPUTIME_ID时间获取失败: %s\n", strerror(errno));
} else {
printf(" 线程CPU时间: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);
}

// 示例2: 高精度计时演示
printf("\n示例2: 高精度计时演示\n");

struct timespec start, end, elapsed;

// 使用CLOCK_MONOTONIC进行计时
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
perror(" 获取开始时间失败");
} else {
printf(" 开始时间: %ld.%09ld 秒\n", start.tv_sec, start.tv_nsec);

// 执行一些操作
printf(" 执行计算操作...\n");
volatile long sum = 0;
for (long i = 0; i < 1000000; i++) {
sum += i;
}

if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) {
perror(" 获取结束时间失败");
} else {
printf(" 结束时间: %ld.%09ld 秒\n", end.tv_sec, end.tv_nsec);

// 计算耗时
if (end.tv_nsec < start.tv_nsec) {
elapsed.tv_sec = end.tv_sec - start.tv_sec - 1;
elapsed.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
} else {
elapsed.tv_sec = end.tv_sec - start.tv_sec;
elapsed.tv_nsec = end.tv_nsec - start.tv_nsec;
}

printf(" 计算耗时: %ld.%09ld 秒\n", elapsed.tv_sec, elapsed.tv_nsec);
printf(" 即: %ld 纳秒\n", elapsed.tv_sec * 1000000000 + elapsed.tv_nsec);
}
}

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

// 使用无效的时钟ID
result = clock_gettime(999, &ts);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时钟ID错误处理正确: %s\n", strerror(errno));
}
}

// 使用NULL指针
result = clock_gettime(CLOCK_REALTIME, NULL);
if (result == -1) {
if (errno == EFAULT) {
printf(" NULL指针错误处理正确: %s\n", strerror(errno));
}
}

// 示例4: 时间格式转换
printf("\n示例4: 时间格式转换\n");

if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
printf(" 原始时间: %ld.%09ld 秒\n", ts.tv_sec, ts.tv_nsec);

// 转换为毫秒
long milliseconds = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
printf(" 毫秒表示: %ld ms\n", milliseconds);

// 转换为微秒
long microseconds = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
printf(" 微秒表示: %ld μs\n", microseconds);

// 转换为纳秒
long long nanoseconds = (long long)ts.tv_sec * 1000000000 + ts.tv_nsec;
printf(" 纳秒表示: %lld ns\n", nanoseconds);
}

// 示例5: 不同时钟的特性对比
printf("\n示例5: 不同时钟特性对比\n");

printf("CLOCK_REALTIME特性:\n");
printf(" - 表示实际时间(墙上时钟)\n");
printf(" - 可以被系统管理员修改\n");
printf(" - 受NTP同步影响\n");
printf(" - 适用于获取当前日期时间\n\n");

printf("CLOCK_MONOTONIC特性:\n");
printf(" - 单调递增,不会倒退\n");
printf(" - 不受系统时间调整影响\n");
printf(" - 适用于测量时间间隔\n");
printf(" - 进程启动时通常为0\n\n");

printf("CLOCK_PROCESS_CPUTIME_ID特性:\n");
printf(" - 测量进程使用的CPU时间\n");
printf(" - 包括所有线程的CPU时间\n");
printf(" - 不包括睡眠时间\n");
printf(" - 适用于性能分析\n\n");

printf("CLOCK_THREAD_CPUTIME_ID特性:\n");
printf(" - 测量当前线程使用的CPU时间\n");
printf(" - 不包括睡眠时间\n");
printf(" - 适用于线程性能分析\n\n");

// 示例6: 实际应用场景
printf("示例6: 实际应用场景\n");

// 场景1: 性能基准测试
printf("场景1: 性能基准测试\n");
if (clock_gettime(CLOCK_MONOTONIC, &start) == 0) {
// 模拟算法执行
volatile int dummy = 0;
for (int i = 0; i < 1000000; i++) {
dummy += i * i;
}

if (clock_gettime(CLOCK_MONOTONIC, &end) == 0) {
long long duration = (end.tv_sec - start.tv_sec) * 1000000000LL +
(end.tv_nsec - start.tv_nsec);
printf(" 算法执行时间: %lld 纳秒\n", duration);
}
}

// 场景2: 超时控制
printf("\n场景2: 超时控制\n");
if (clock_gettime(CLOCK_MONOTONIC, &start) == 0) {
long timeout_ns = 5000000000LL; // 5秒超时

// 模拟等待操作
while (1) {
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
long long elapsed = (ts.tv_sec - start.tv_sec) * 1000000000LL +
(ts.tv_nsec - start.tv_nsec);
if (elapsed >= timeout_ns) {
printf(" 操作超时(5秒)\n");
break;
}
}
usleep(100000); // 休眠100ms
}
}

// 场景3: CPU使用率监控
printf("\n场景3: CPU使用率监控\n");
struct timespec cpu_start, cpu_end;
struct timespec wall_start, wall_end;

if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_start) == 0 &&
clock_gettime(CLOCK_MONOTONIC, &wall_start) == 0) {

// 执行一些CPU密集型操作
volatile double result = 1.0;
for (int i = 0; i < 1000000; i++) {
result *= 1.000001;
}

if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_end) == 0 &&
clock_gettime(CLOCK_MONOTONIC, &wall_end) == 0) {

long long cpu_time = (cpu_end.tv_sec - cpu_start.tv_sec) * 1000000000LL +
(cpu_end.tv_nsec - cpu_start.tv_nsec);
long long wall_time = (wall_end.tv_sec - wall_start.tv_sec) * 1000000000LL +
(wall_end.tv_nsec - wall_start.tv_nsec);

double cpu_usage = (double)cpu_time / wall_time * 100;
printf(" CPU使用率: %.2f%%\n", cpu_usage);
printf(" CPU时间: %lld 纳秒\n", cpu_time);
printf(" 墙钟时间: %lld 纳秒\n", wall_time);
}
}

// 示例7: 时区和本地时间
printf("\n示例7: 时区和本地时间\n");

if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
printf(" UTC时间: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);

// 转换为本地时间
struct tm *local_tm = localtime(&ts.tv_sec);
if (local_tm) {
char time_str&#91;100];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", local_tm);
printf(" 本地时间: %s.%09ld\n", time_str, ts.tv_nsec);
}
}

printf("\n总结:\n");
printf("clock_gettime是现代Linux系统中推荐的高精度计时函数\n");
printf("支持多种时钟类型,满足不同应用场景需求\n");
printf("提供纳秒级精度,优于传统的time和gettimeofday函数\n");
printf("正确使用时钟类型对程序性能和正确性很重要\n");

return 0;
}

clock_nanosleep - 高精度睡眠

函数介绍

clock_nanosleep系统调用提供纳秒级精度的睡眠功能,支持绝对时间和相对时间两种模式,比传统的nanosleep更加灵活。

函数原型

1
2
3
4
5
6
#include <time.h>

int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request,
struct timespec *remain);

功能

使进程睡眠指定的时间,支持高精度纳秒级睡眠。

参数

  • clockid_t clock_id: 时钟ID

int flags: 标志位

  • 0: 相对时间睡眠

  • TIMER_ABSTIME: 绝对时间睡眠

const struct timespec *request: 请求睡眠的时间

struct timespec *remain: 剩余时间(被信号中断时)

返回值

  • 成功时返回0

  • 被信号中断时返回-1,并设置errno为EINTR

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

相似函数

  • nanosleep(): 纳秒级睡眠

  • sleep(): 秒级睡眠

  • usleep(): 微秒级睡眠

示例代码

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

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

int main() {
struct timespec request, remain, start, end;
int result;

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

// 示例1: 相对时间睡眠
printf("\n示例1: 相对时间睡眠\n");

// 睡眠100毫秒
request.tv_sec = 0;
request.tv_nsec = 100000000; // 100毫秒 = 100,000,000纳秒

if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
perror(" 获取开始时间失败");
}

printf(" 开始睡眠: %ld.%09ld 秒\n", start.tv_sec, start.tv_nsec);
result = clock_nanosleep(CLOCK_MONOTONIC, 0, &request, NULL);

if (result == -1) {
if (errno == EINTR) {
printf(" 睡眠被信号中断\n");
} else {
printf(" 睡眠失败: %s\n", strerror(errno));
}
} else {
printf(" 睡眠完成\n");
if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) {
perror(" 获取结束时间失败");
} else {
long long actual_sleep = (end.tv_sec - start.tv_sec) * 1000000000LL +
(end.tv_nsec - start.tv_nsec);
printf(" 实际睡眠时间: %lld 纳秒\n", actual_sleep);
}
}

// 示例2: 绝对时间睡眠
printf("\n示例2: 绝对时间睡眠\n");

// 获取当前时间
if (clock_gettime(CLOCK_REALTIME, &start) == 0) {
printf(" 当前时间: %ld.%09ld 秒\n", start.tv_sec, start.tv_nsec);

// 设置绝对睡眠时间(当前时间+2秒)
struct timespec absolute_time;
absolute_time.tv_sec = start.tv_sec + 2;
absolute_time.tv_nsec = start.tv_nsec;

printf(" 绝对睡眠时间: %ld.%09ld 秒\n", absolute_time.tv_sec, absolute_time.tv_nsec);

result = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &absolute_time, NULL);
if (result == -1) {
if (errno == EINTR) {
printf(" 绝对时间睡眠被信号中断\n");
} else {
printf(" 绝对时间睡眠失败: %s\n", strerror(errno));
}
} else {
printf(" 绝对时间睡眠完成\n");
}
}

// 示例3: 被信号中断的睡眠
printf("\n示例3: 被信号中断的睡眠\n");

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

// 启动另一个线程发送信号
pid_t pid = fork();
if (pid == 0) {
// 子进程:延迟发送信号
sleep(1);
kill(getppid(), SIGUSR1);
exit(0);
} else if (pid > 0) {
// 父进程:长时间睡眠
request.tv_sec = 5;
request.tv_nsec = 0;

printf(" 开始5秒睡眠,1秒后会被信号中断\n");
result = clock_nanosleep(CLOCK_MONOTONIC, 0, &request, &remain);

if (result == -1 && errno == EINTR) {
printf(" 睡眠被信号中断\n");
printf(" 剩余时间: %ld.%09ld 秒\n", remain.tv_sec, remain.tv_nsec);
}

wait(NULL); // 等待子进程结束
}

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

// 使用无效的时钟ID
request.tv_sec = 1;
request.tv_nsec = 0;
result = clock_nanosleep(999, 0, &request, NULL);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时钟ID错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的时间值
request.tv_sec = -1;
request.tv_nsec = 0;
result = clock_nanosleep(CLOCK_MONOTONIC, 0, &request, NULL);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时间值错误处理正确: %s\n", strerror(errno));
}
}

// 使用过大的纳秒值
request.tv_sec = 0;
request.tv_nsec = 1000000000; // 10亿纳秒 = 1秒,但应该 < 1秒
result = clock_nanosleep(CLOCK_MONOTONIC, 0, &request, NULL);
if (result == -1) {
if (errno == EINVAL) {
printf(" 纳秒值过大错误处理正确: %s\n", strerror(errno));
}
}

// 示例5: 不同时钟的睡眠效果
printf("\n示例5: 不同时钟的睡眠效果\n");

printf("CLOCK_REALTIME睡眠:\n");
printf(" - 基于系统实时时间\n");
printf(" - 受系统时间调整影响\n");
printf(" - 适用于绝对时间睡眠\n\n");

printf("CLOCK_MONOTONIC睡眠:\n");
printf(" - 基于单调递增时间\n");
printf(" - 不受系统时间调整影响\n");
printf(" - 适用于相对时间睡眠\n\n");

// 示例6: 高精度定时器演示
printf("示例6: 高精度定时器演示\n");

printf("创建100毫秒间隔的定时器循环:\n");
struct timespec interval;
interval.tv_sec = 0;
interval.tv_nsec = 100000000; // 100毫秒

for (int i = 0; i < 5; i++) {
if (clock_gettime(CLOCK_MONOTONIC, &start) == 0) {
printf(" 第%d次: 时间 %ld.%09ld\n", i+1, start.tv_sec, start.tv_nsec);
}

result = clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL);
if (result == -1) {
if (errno == EINTR) {
printf(" 第%d次: 睡眠被中断\n", i+1);
break;
}
}
}

// 示例7: 睡眠精度测试
printf("\n示例7: 睡眠精度测试\n");

struct timespec sleep_times&#91;] = {
{0, 1000}, // 1微秒
{0, 10000}, // 10微秒
{0, 100000}, // 100微秒
{0, 1000000}, // 1毫秒
{0, 10000000}, // 10毫秒
{0, 100000000}, // 100毫秒
{1, 0} // 1秒
};

const char *time_labels&#91;] = {
"1微秒", "10微秒", "100微秒", "1毫秒", "10毫秒", "100毫秒", "1秒"
};

printf("睡眠精度测试结果:\n");
for (int i = 0; i < 7; i++) {
if (clock_gettime(CLOCK_MONOTONIC, &start) == 0) {
result = clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep_times&#91;i], NULL);
if (clock_gettime(CLOCK_MONOTONIC, &end) == 0) {
long long actual = (end.tv_sec - start.tv_sec) * 1000000000LL +
(end.tv_nsec - start.tv_nsec);
long long requested = sleep_times&#91;i].tv_sec * 1000000000LL +
sleep_times&#91;i].tv_nsec;
long long diff = actual - requested;

printf(" %-8s: 请求%8lld ns, 实际%8lld ns, 误差%+6lld ns\n",
time_labels&#91;i], requested, actual, diff);
}
}
}

// 示例8: 实际应用场景
printf("\n示例8: 实际应用场景\n");

// 场景1: 实时系统定时
printf("场景1: 实时系统定时\n");
printf("在实时应用中确保精确的时间间隔\n");

// 场景2: 性能基准测试
printf("\n场景2: 性能基准测试\n");
printf("提供精确的延迟控制用于性能测试\n");

// 场景3: 动画和游戏循环
printf("\n场景3: 动画和游戏循环\n");
printf("维持稳定的帧率和更新频率\n");

// 场景4: 网络超时控制
printf("\n场景4: 网络超时控制\n");
printf("实现精确的网络操作超时机制\n");

printf("\n总结:\n");
printf("clock_nanosleep提供纳秒级精度的睡眠功能\n");
printf("支持相对时间和绝对时间两种模式\n");
printf("比传统sleep函数更加灵活和精确\n");
printf("正确处理信号中断和剩余时间计算\n");
printf("适用于需要高精度时间控制的应用场景\n");

return 0;
}

clock_settime - 设置时钟时间

函数介绍

clock_settime系统调用用于设置指定时钟的时间值。它允许程序修改系统时钟,主要用于时间同步和系统管理。

函数原型

1
2
3
4
#include <time.h>

int clock_settime(clockid_t clk_id, const struct timespec *tp);

功能

设置指定时钟的时间值。

参数

  • clockid_t clk_id: 时钟ID(通常为CLOCK_REALTIME)

  • const struct timespec *tp: 指向timespec结构体的指针,包含新的时间值

返回值

  • 成功时返回0

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

特殊限制

  • 需要CAP_SYS_TIME能力或root权限

  • 通常只能设置CLOCK_REALTIME时钟

相似函数

  • settimeofday(): 设置系统时间

  • stime(): 设置系统时间(已废弃)

示例代码

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

int main() {
struct timespec current_time, new_time;
int result;

printf("=== Clock_settime 函数示例 ===\n");
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 示例1: 获取当前时间
printf("\n示例1: 获取当前时间\n");

if (clock_gettime(CLOCK_REALTIME, &current_time) == -1) {
perror(" 获取当前时间失败");
} else {
printf(" 当前系统时间: %ld.%09ld 秒\n", current_time.tv_sec, current_time.tv_nsec);
printf(" 对应日期: %s", ctime(&current_time.tv_sec));
}

// 示例2: 权限检查
printf("\n示例2: 权限检查\n");

// 尝试设置时间(通常会失败)
new_time.tv_sec = current_time.tv_sec;
new_time.tv_nsec = current_time.tv_nsec;

result = clock_settime(CLOCK_REALTIME, &new_time);
if (result == -1) {
if (errno == EPERM) {
printf(" 权限不足设置时间: %s\n", strerror(errno));
printf(" 说明: 需要CAP_SYS_TIME能力或root权限\n");
} else {
printf(" 设置时间失败: %s\n", strerror(errno));
}
} else {
printf(" 时间设置成功\n");
}

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

// 使用无效的时钟ID
result = clock_settime(999, &new_time);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时钟ID错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的时间值
struct timespec invalid_time;
invalid_time.tv_sec = -1;
invalid_time.tv_nsec = 0;

result = clock_settime(CLOCK_REALTIME, &invalid_time);
if (result == -1) {
if (errno == EINVAL) {
printf(" 无效时间值错误处理正确: %s\n", strerror(errno));
}
}

// 使用过大的纳秒值
invalid_time.tv_sec = current_time.tv_sec;
invalid_time.tv_nsec = 1000000000; // 10亿纳秒,应该 < 1秒

result = clock_settime(CLOCK_REALTIME, &invalid_time);
if (result == -1) {
if (errno == EINVAL) {
printf(" 纳秒值过大错误处理正确: %s\n", strerror(errno));
}
}

// 使用NULL指针
result = clock_settime(CLOCK_REALTIME, NULL);
if (result == -1) {
if (errno == EFAULT) {
printf(" NULL指针错误处理正确: %s\n", strerror(errno));
}
}

// 示例4: 支持的时钟类型
printf("\n示例4: 支持的时钟类型\n");

printf("CLOCK_REALTIME:\n");
printf(" - 系统实时钟\n");
printf(" - 可以被设置\n");
printf(" - 用于表示当前时间\n\n");

printf("其他时钟类型:\n");
printf(" - CLOCK_MONOTONIC: 通常不能设置\n");
printf(" - CLOCK_PROCESS_CPUTIME_ID: 不能设置\n");
printf(" - CLOCK_THREAD_CPUTIME_ID: 不能设置\n\n");

// 示例5: 时间格式转换
printf("示例5: 时间格式转换\n");

if (clock_gettime(CLOCK_REALTIME, &current_time) == 0) {
printf(" 当前时间: %ld.%09ld 秒\n", current_time.tv_sec, current_time.tv_nsec);

// 从日期字符串转换为time_t
struct tm time_info;
strptime("2024-01-01 12:00:00", "%Y-%m-%d %H:%M:%S", &time_info);
time_t new_time_t = mktime(&time_info);

printf(" 转换时间: %s", ctime(&new_time_t));

// 转换为timespec格式
struct timespec converted_time;
converted_time.tv_sec = new_time_t;
converted_time.tv_nsec = 0;

printf(" timespec格式: %ld.%09ld 秒\n", converted_time.tv_sec, converted_time.tv_nsec);
}

// 示例6: 时区考虑
printf("\n示例6: 时区考虑\n");

if (clock_gettime(CLOCK_REALTIME, &current_time) == 0) {
printf(" UTC时间: %ld.%09ld 秒\n", current_time.tv_sec, current_time.tv_nsec);

// 获取本地时区偏移
struct tm *utc_tm = gmtime(&current_time.tv_sec);
struct tm *local_tm = localtime(&current_time.tv_sec);

time_t utc_time = mktime(utc_tm);
time_t local_time = mktime(local_tm);

long tz_offset = local_time - utc_time;
printf(" 时区偏移: %+ld 秒\n", tz_offset);
}

// 示例7: 安全考虑
printf("\n示例7: 安全考虑\n");
printf("使用clock_settime的安全注意事项:\n");
printf("1. 需要适当的权限(CAP_SYS_TIME或root)\n");
printf("2. 不当的时间设置可能影响系统稳定性\n");
printf("3. 时间跳跃可能影响依赖时间的应用程序\n");
printf("4. 应该使用NTP等标准时间同步服务\n");
printf("5. 在生产环境中谨慎使用\n\n");

// 示例8: 实际应用场景
printf("示例8: 实际应用场景\n");

// 场景1: NTP客户端
printf("场景1: NTP客户端\n");
printf(" - 从NTP服务器获取时间\n");
printf(" - 调整系统时钟\n");
printf(" - 保持时间同步\n\n");

// 场景2: 系统初始化
printf("场景2: 系统初始化\n");
printf(" - 设置初始系统时间\n");
printf(" - 从硬件时钟同步\n");
printf(" - 恢复时间设置\n\n");

// 场景3: 调试和测试
printf("场景3: 调试和测试\n");
printf(" - 设置特定时间进行测试\n");
printf(" - 模拟时间相关场景\n");
printf(" - 性能基准测试\n\n");

// 场景4: 时间同步服务
printf("场景4: 时间同步服务\n");
printf(" - 分布式系统时间协调\n");
printf(" - 数据库事务时间戳\n");
printf(" - 日志时间同步\n\n");

// 示例9: 替代方案
printf("示例9: 替代方案\n");
printf("现代时间管理推荐使用:\n");
printf("1. NTP守护进程(ntpd)\n");
printf("2. systemd-timesyncd\n");
printf("3. chrony\n");
printf("4. chronyd\n");
printf("5. 避免手动设置系统时间\n\n");

printf("总结:\n");
printf("clock_settime用于设置系统时钟时间\n");
printf("需要适当的权限才能使用\n");
printf("主要用于时间同步服务\n");
printf("不当使用可能影响系统稳定性\n");
printf("推荐使用标准的时间同步服务\n");

return 0;
}

clone系统调用及示例

我们继续介绍下一个函数。在 getsockopt 之后,根据您提供的列表,下一个函数是 clone。

clone 函数

1. 函数介绍

clone 是一个 Linux 特有的系统调用,它提供了一种非常灵活且底层的方式来创建新的进程或线程。它比标准的 fork 函数更加强大和复杂,允许调用者精确地控制子进程(或线程)与父进程(调用进程)之间共享哪些资源(如虚拟内存空间、文件描述符表、信号处理程序表等)。

你可以把 clone 想象成一个高度可定制的复制品制造机:

  • 你有一个原始对象(父进程)。

  • 你可以告诉机器(clone):复制这个对象,但让新对象(子进程)和原对象共享某些部件(如内存、文件),而独立拥有另一些部件(如寄存器状态、栈)。

  • 通过设置不同的参数,你可以制造出几乎完全独立的副本(类似 fork),或者共享大量资源的紧密副本(类似线程)。

实际上,Linux 上的 pthread 线程库在底层就是通过调用 clone 来创建线程的。

2. 函数原型

1
2
3
4
5
6
7
8
9
10
11
12
#define _GNU_SOURCE // 必须定义以使用 clone
#include <sched.h> // 必需
#include <signal.h> // 定义了 SIGCHLD 等常量

// 标准形式 (通过宏定义)
int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
/* pid_t *parent_tid, void *tls, pid_t *child_tid */ );

// 更底层的系统调用形式 (通常由库函数包装)
long syscall(SYS_clone, unsigned long flags, void *stack,
int *parent_tid, int *child_tid, unsigned long tls);

注意: clone 的接口比较复杂,并且存在不同版本。上面展示的是最常用的、由 glibc 提供的包装函数形式。

3. 功能

  • 创建新执行流: 创建一个新的执行流(可以看作一个轻量级进程或线程)。

  • 控制资源共享: 通过 flags 参数,精确控制新创建的执行流与调用者共享哪些内核资源。

  • 指定执行函数: 与 pthread_create 类似,clone 允许你指定一个函数 fn,新创建的执行流将从该函数开始执行。

  • 指定栈空间: 调用者必须为新执行流提供一块栈空间(通过 stack 参数),这与 pthread_create 自动分配栈不同。

  • 传递参数: 可以通过 arg 参数向新执行流的入口函数 fn 传递一个参数。

4. 参数

由于 clone 的复杂性,我们重点介绍 glibc 包装函数的常用参数:

int (*fn)(void *): 这是一个函数指针,指向新创建的执行流将要执行的入口函数。

  • 该函数接受一个 void * 类型的参数,并返回一个 int 类型的值。

  • 当这个函数返回时,新创建的执行流(子进程/线程)就会终止。

void *stack: 这是一个指针,指向为新执行流分配的栈空间的顶部(高地址)。

  • 非常重要: 调用者必须自己分配并管理这块栈内存。clone 不会自动分配。

  • 栈是从高地址向低地址增长的,所以这个指针应该指向分配的栈空间的末尾。

int flags: 这是最重要的参数,是一个位掩码(bitmask),用于指定新执行流与父进程共享哪些资源。常用的标志包括:

  • CLONE_VM: 共享虚拟内存空间。如果设置,子进程和父进程将运行在同一个内存地址空间中(类似线程)。

  • CLONE_FS: 共享文件系统信息(根目录、当前工作目录等)。

  • CLONE_FILES: 共享文件描述符表。如果设置,子进程将继承父进程打开的文件描述符,并且后续在任一进程中打开/关闭文件都会影响另一个。

  • CLONE_SIGHAND: 共享信号处理程序表。如果设置,子进程将继承父进程的信号处理设置。

  • CLONE_PTRACE: 如果父进程正在被跟踪(ptrace),则子进程也将被跟踪。

  • CLONE_VFORK: 暂停父进程的执行,直到子进程调用 exec 或 _exit。这模拟了 vfork 的语义。

  • CLONE_PARENT: 新子进程的父进程将是调用进程的父进程,而不是调用进程本身。

  • CLONE_THREAD: 将子进程置于调用进程的线程组中。这通常与 CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND 一起使用来创建线程。

  • CLONE_NEW* (如 CLONE_NEWNS, CLONE_NEWUSER): 用于创建命名空间(Namespace),这是容器技术(如 Docker)的基础。

  • SIGCHLD: 这不是一个 CLONE_* 标志,但它经常与 clone 一起使用(按位或 |)。它指定当子进程退出时,应向父进程发送 SIGCHLD 信号。

void *arg: 这是一个通用指针,它将作为参数传递给入口函数 fn。

… (可变参数): 后面可能还有几个参数,用于更高级的用途(如设置线程本地存储 TLS、获取子进程 ID 等),在基础使用中通常可以忽略或传入 NULL。

5. 返回值

clone 的返回值比较特殊,因为它在父进程和子进程(新创建的执行流)中是不同的:

在父进程中:

  • 如果成功,返回新创建子进程的**线程 ID **(Thread ID, TID)。在 Linux 中,TID 通常与 PID 相同(对于主线程),但对于使用 CLONE_THREAD 创建的线程,它们有相同的 PID 但不同的 TID。

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

在子进程中 (新创建的执行流):

  • 直接执行 fn(arg) 函数。

  • fn 函数的返回值将成为 clone 系统调用在子进程中的返回值。

  • 如果 fn 函数返回,子进程通常应该调用 _exit() 而不是 exit() 来终止,以避免刷新 stdio 缓冲区等可能影响父进程的操作。

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

  • fork: 创建一个新进程,子进程是父进程的一个完整副本,拥有独立的资源。clone 可以通过不设置任何共享标志来模拟 fork 的行为。

  • vfork: 类似于 fork,但在子进程调用 exec 或 _exit 之前会暂停父进程。clone 可以通过设置 CLONE_VFORK 标志来模拟 vfork。

  • pthread_create: POSIX 线程库函数,用于创建线程。在 Linux 上,它底层就是调用 clone,并自动处理栈分配、设置共享标志等。

  • _exit: 子进程在 fn 函数中执行完毕后,应调用 _exit 退出。

  • wait / waitpid: 父进程可以使用这些函数来等待由 clone(设置了 SIGCHLD)创建的子进程结束。

7. 示例代码

示例 1:使用 clone 模拟 fork (不共享任何资源)

这个例子演示了如何使用 clone 来创建一个与父进程几乎完全独立的子进程,效果类似于 fork。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// clone_fork_like.c
#define _GNU_SOURCE // 必须定义以使用 clone
#include <sched.h> // clone
#include <sys/wait.h> // waitpid
#include <unistd.h> // getpid
#include <stdio.h> // printf, perror
#include <stdlib.h> // exit, malloc, free
#include <signal.h> // SIGCHLD
#include <string.h> // strerror
#include <errno.h> // errno

#define STACK_SIZE (1024 * 1024) // 1MB 栈空间

// 子进程要执行的函数
int child_function(void *arg) {
char *msg = (char *)arg;
printf("Child process (TID: %d) executing.\n", getpid());
printf("Child received message: %s\n", msg);

// 子进程可以执行自己的任务
for (int i = 0; i < 3; ++i) {
printf(" Child working... %d\n", i);
sleep(1);
}

printf("Child process (TID: %d) finished.\n", getpid());
// 子进程结束,返回值将成为 clone 在子进程中的返回值
return 42;
}

int main() {
char *stack; // 指向栈空间的指针
char *stack_top; // 指向栈顶的指针 (clone 需要)
pid_t ctid; // 子进程的 TID

// 1. 为子进程分配栈空间
// 注意:栈是从高地址向低地址增长的
stack = malloc(STACK_SIZE);
if (stack == NULL) {
perror("malloc stack failed");
exit(EXIT_FAILURE);
}
// stack 指向分配内存的起始地址
// stack_top 应该指向内存的末尾地址
stack_top = stack + STACK_SIZE;

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

// 2. 调用 clone 创建子进程
// flags = SIGCHLD: 子进程退出时发送 SIGCHLD 信号给父进程
// (没有设置 CLONE_VM, CLONE_FILES 等,所以资源不共享,类似 fork)
ctid = clone(child_function, stack_top, SIGCHLD, "Hello from parent to child!");
// 注意:这里的 SIGCHLD 是一个常见的用法,表示子进程结束后通知父进程

if (ctid == -1) {
perror("clone failed");
free(stack);
exit(EXIT_FAILURE);
}

printf("Parent process (PID: %d) created child with TID: %d\n", getpid(), ctid);

// 3. 父进程继续执行自己的任务
printf("Parent process doing its own work...\n");
for (int i = 0; i < 5; ++i) {
printf(" Parent working... %d\n", i);
sleep(1);
}

// 4. 父进程等待子进程结束
int status;
pid_t wpid = waitpid(ctid, &status, 0); // 等待特定的子进程
if (wpid == -1) {
perror("waitpid failed");
free(stack);
exit(EXIT_FAILURE);
}

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

// 5. 清理资源
free(stack);
printf("Parent process (PID: %d) finished.\n", getpid());

return 0;
}

代码解释:

定义了栈大小 STACK_SIZE 为 1MB。

定义了子进程的入口函数 child_function。这个函数接受一个 void * 参数,打印信息,做一些工作,然后返回 42。

在 main 函数中:

  • 使用 malloc 分配栈空间。

  • 计算栈顶指针 stack_top。因为栈向下增长,clone 需要栈顶地址。

调用 clone(child_function, stack_top, SIGCHLD, “Hello from parent to child!”)。

  • child_function: 子进程入口。

  • stack_top: 子进程的栈顶。

  • SIGCHLD: 标志,表示子进程结束后发送信号。

  • “Hello…”: 传递给 child_function 的参数。

clone 在父进程中返回子进程的 TID。

父进程执行自己的任务。

调用 waitpid(ctid, …) 等待子进程结束。

检查子进程的退出状态。WEXITSTATUS(status) 获取子进程 child_function 的返回值(42)。

释放栈内存。

示例 2:使用 clone 创建共享内存的执行流 (类似线程)

这个例子演示了如何使用 clone 创建一个与父进程共享内存空间的执行流,模拟线程的部分行为。

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
// clone_thread_like.c
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <string.h>

#define STACK_SIZE (1024 * 1024)

// 全局变量,用于演示内存共享
volatile int shared_counter = 0;

// 子执行流函数
int thread_like_function(void *arg) {
char *name = (char *)arg;
printf("Thread-like process '%s' (TID: %d) started.\n", name, getpid());

for (int i = 0; i < 100000; ++i) {
// 修改共享变量
shared_counter++;
}
printf("Thread-like process '%s' finished. Shared counter: %d\n", name, shared_counter);
return 0;
}

int main() {
char *stack1, *stack2;
char *stack_top1, *stack_top2;
pid_t tid1, tid2;

stack1 = malloc(STACK_SIZE);
stack2 = malloc(STACK_SIZE);
if (!stack1 || !stack2) {
perror("malloc stacks failed");
free(stack1);
free(stack2);
exit(EXIT_FAILURE);
}
stack_top1 = stack1 + STACK_SIZE;
stack_top2 = stack2 + STACK_SIZE;

printf("Main process (PID: %d) creating two thread-like processes.\n", getpid());
printf("Initial shared counter: %d\n", shared_counter);

// 创建第一个"线程"
// CLONE_VM: 共享虚拟内存 (包括全局变量 shared_counter)
tid1 = clone(thread_like_function, stack_top1, CLONE_VM | SIGCHLD, "Thread-1");
if (tid1 == -1) {
perror("clone thread 1 failed");
free(stack1);
free(stack2);
exit(EXIT_FAILURE);
}

// 创建第二个"线程"
tid2 = clone(thread_like_function, stack_top2, CLONE_VM | SIGCHLD, "Thread-2");
if (tid2 == -1) {
perror("clone thread 2 failed");
free(stack1);
free(stack2);
exit(EXIT_FAILURE);
}

printf("Main process created TID1: %d, TID2: %d\n", tid1, tid2);

// 等待两个"线程"结束
// 注意:由于共享内存,最后的 shared_counter 值是不确定的(竞态条件)
waitpid(tid1, NULL, 0);
waitpid(tid2, NULL, 0);

printf("Main process finished. Final shared counter: %d (may be < 200000 due to race condition)\n", shared_counter);

free(stack1);
free(stack2);
return 0;
}

代码解释:

定义了一个 volatile int shared_counter 全局变量。volatile 告诉编译器不要优化对它的访问,因为在多执行流环境下它的值可能随时改变。

thread_like_function 是两个”线程”将执行的函数。它们都对 shared_counter 进行大量递增操作。

在 main 函数中:

  • 分配两个独立的栈空间。

  • 调用两次 clone 创建两个执行流。

关键: flags 参数是 CLONE_VM | SIGCHLD。

  • CLONE_VM: 这使得子执行流与父进程共享虚拟内存地址空间。因此,它们访问的 shared_counter 是同一个变量。

父进程等待两个子执行流结束。

重要: 由于两个执行流共享内存并同时修改 shared_counter,而 shared_counter++ 不是原子操作,这会导致竞态条件(Race Condition)。最终的 shared_counter 值很可能小于 200000。这展示了在共享内存编程中进行同步(如使用互斥锁)的重要性。

示例 3:与 pthread_create 的对比

这个例子通过代码片段对比 clone 和更高级的 pthread_create。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使用 clone (底层,复杂)
int thread_func(void *arg) {
// ... thread work ...
return 0;
}
void* wrapper_func(void *arg) {
return (void*)(long)thread_func(arg);
}
// In main:
char *stack = malloc(STACK_SIZE);
char *stack_top = stack + STACK_SIZE;
clone(thread_func, stack_top, CLONE_VM | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD, arg);

// 使用 pthread_create (高层,简单)
void* pthread_func(void *arg) {
// ... thread work ...
return NULL;
}
// In main:
pthread_t thread;
pthread_create(&thread, NULL, pthread_func, arg);
pthread_join(thread, NULL);

解释:

  • clone 需要手动管理栈、设置多个标志位、处理返回值等,非常底层。

  • pthread_create 自动处理了栈分配、设置了正确的共享标志、提供了简单的 pthread_t 标识符和 pthread_join 等待机制,更易于使用。

重要提示与注意事项:

底层且复杂: clone 是一个非常底层的系统调用,直接使用它非常复杂且容易出错。除非有特殊需求(如实现自己的线程库、容器技术),否则应优先使用 fork/vfork 或 pthread_create。

栈管理: 调用者必须自己分配和释放子进程/线程的栈空间。忘记释放会导致内存泄漏。

标志位: flags 参数是 clone 的核心。理解各种 CLONE_* 标志的含义及其组合效果至关重要。

CLONE_THREAD: 如果使用 CLONE_THREAD 创建线程,该线程将成为调用进程的线程组的一部分。线程组中的所有线程具有相同的 PID,但有不同的 TID。对线程组中的任何一个线程调用 exit 会杀死整个线程组。等待线程需要使用 pthread_join 类似的机制,而不是 wait/waitpid。

_exit vs exit: 子进程(线程)在执行函数返回后,应调用 _exit() 而非 exit()。exit() 会刷新 stdio 缓冲区等,可能对共享内存的父进程产生意外影响。

信号: 理解 SIGCHLD 标志以及如何正确等待子进程非常重要。

可移植性: clone 是 Linux 特有的系统调用,在其他 Unix 系统上不可用。

总结:

clone 是 Linux 提供的一个功能强大但使用复杂的系统调用,用于创建新的执行流(进程或线程)。它通过精细的标志位控制资源的共享,是实现线程库和高级进程管理功能(如容器)的基础。虽然直接使用它需要深入了解系统底层知识,但理解其工作原理对于掌握 Linux 进程和线程模型非常有帮助。

close系统调用及示例

继续学习 Linux 系统编程中的基础函数。这次我们介绍 close 函数,它是与 open 相对应的,用于关闭不再需要的文件描述符。

1. 函数介绍

close 是一个 Linux 系统调用,其主要功能是关闭一个由 open、pipe、socket 等系统调用打开的文件描述符 (file descriptor)。关闭文件描述符会释放与之关联的内核资源(如文件表项),并使其可以被进程重新使用(例如,后续的 open 调用可能会返回这个刚刚被关闭的文件描述符值)。

你可以把它想象成离开房间时关上门,并交还钥匙(文件描述符),这样其他人(或你自己稍后)才能再使用这把钥匙(文件描述符号)进入别的房间(打开别的文件)。

2. 函数原型

1
2
3
4
#include <unistd.h>

int close(int fd);

3. 功能

  • 释放资源: 释放与文件描述符 fd 相关的内核资源。

  • 刷新缓冲: 对于某些类型的文件(如普通文件),内核可能会缓存写操作。调用 close 通常会触发将这些缓存的数据写入到实际的存储介质中(虽然不保证 100% 刷新,fsync 可以强制刷新)。

  • 关闭连接: 对于套接字 (socket) 或管道 (pipe),close 会关闭连接的一端。

  • 回收描述符: 使文件描述符 fd 在当前进程中变为无效,该值可以被后续的文件操作(如 open、dup)重新使用。

4. 参数

  • int fd: 这是需要关闭的文件描述符。它应该是之前成功调用 open、creat、pipe、socket、dup 等函数返回的有效文件描述符。

5. 返回值

  • 成功时: 返回 0。

  • 失败时: 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 EBADF 表示 fd 不是一个有效的、已打开的文件描述符)。

重要提示: 必须检查 close 的返回值! 虽然很多人忽略,但 close 是可能失败的。如果 close 失败,可能意味着数据没有被正确写入(例如磁盘空间满、设备故障等)。忽略 close 的错误可能会导致数据丢失或不一致。

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

  • open: 与 close 相对应,用于打开文件并获取文件描述符。

  • read, write: 在文件描述符被 close 之后,不能再对它使用 read 或 write。

  • dup, dup2, fcntl(F_DUPFD): 这些函数可以复制文件描述符。需要注意的是,close 只关闭指定的那个文件描述符副本。只有当一个打开文件的最后一个引用(即最后一个指向该打开文件表项的文件描述符)被关闭时,相关的资源(如文件偏移量、状态标志)才会被真正释放,对于文件来说,数据也才会被刷新。

  • fsync: 在 close 之前显式调用 fsync(fd) 可以确保文件的所有修改都被写入到存储设备,提供更强的数据持久性保证。

7. 示例代码

示例 1:基本的打开、读取、关闭操作

这个例子结合了 open、read 和 close,展示了它们的标准使用流程。

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
#include <unistd.h>  // read, write, close
#include <fcntl.h> // open, O_RDONLY
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <errno.h> // errno

#define BUFFER_SIZE 512

int main() {
int fd; // 文件描述符
char buffer&#91;BUFFER_SIZE]; // 读取缓冲区
ssize_t bytes_read; // 实际读取的字节数
int close_result; // close 的返回值

// 1. 打开文件
fd = open("sample.txt", O_RDONLY);
if (fd == -1) {
perror("Error opening file 'sample.txt'");
exit(EXIT_FAILURE);
}
printf("File 'sample.txt' opened successfully with fd: %d\n", fd);

// 2. 读取文件内容
printf("Reading file content:\n");
while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) {
// 将读取的内容写到标准输出
if (write(STDOUT_FILENO, buffer, bytes_read) != bytes_read) {
perror("Error writing to stdout");
// 即使写 stdout 出错,也要尝试关闭原始文件
close(fd); // 忽略此处 close 的返回值,因为主要错误是 write
exit(EXIT_FAILURE);
}
}

if (bytes_read == -1) {
perror("Error reading file");
// 读取失败,关闭文件
close(fd); // 忽略此处 close 的返回值,因为主要错误是 read
exit(EXIT_FAILURE);
}
// bytes_read == 0, 表示到达文件末尾,正常流程

// 3. 关闭文件 - 这是关键步骤
close_result = close(fd);
if (close_result == -1) {
// close 失败!这是一个严重错误,需要处理
perror("CRITICAL ERROR: Failed to close file 'sample.txt'");
exit(EXIT_FAILURE); // 或者根据应用逻辑决定如何处理
}
printf("File 'sample.txt' closed successfully.\n");

return 0;
}

代码解释:

使用 open 打开文件。

使用 read 和 write 循环读取并打印文件内容。

关键: 在所有可能的退出路径(正常结束、读/写错误)上都调用了 close(fd)。

最重要: 检查了 close(fd) 的返回值。如果返回 -1,则打印严重错误信息并退出。这确保了我们能发现 close 本身可能遇到的问题(如 I/O 错误导致缓冲区刷新失败)。

示例 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
#include <unistd.h>  // close
#include <fcntl.h> // open
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <string.h> // strerror

int main() {
int fd1 = -1, fd2 = -1, fd3 = -1; // 初始化为无效值
int result;

// 打开几个不同的文件
fd1 = open("file1.txt", O_RDONLY);
if (fd1 == -1) {
perror("Failed to open 'file1.txt'");
// fd2, fd3 还未打开,无需关闭
exit(EXIT_FAILURE);
}

fd2 = open("file2.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd2 == -1) {
perror("Failed to open/create 'file2.txt'");
// 关闭之前成功打开的 fd1
if (close(fd1) == -1) {
fprintf(stderr, "Warning: Also failed to close 'file1.txt': %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}

fd3 = open("/etc/passwd", O_RDONLY); // 尝试打开一个系统文件
if (fd3 == -1) {
perror("Failed to open '/etc/passwd'");
// 关闭之前成功打开的 fd1 和 fd2
if (close(fd1) == -1) {
fprintf(stderr, "Warning: Also failed to close 'file1.txt': %s\n", strerror(errno));
}
if (close(fd2) == -1) {
fprintf(stderr, "Warning: Also failed to close 'file2.txt': %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}

printf("All files opened successfully: fd1=%d, fd2=%d, fd3=%d\n", fd1, fd2, fd3);

// ... 这里可以进行文件读写操作 ...

// 程序结束前,关闭所有打开的文件描述符
// 注意:关闭顺序通常不重要,但保持一致性是好习惯
// 关闭时检查每个的返回值

if (fd1 != -1) {
result = close(fd1);
if (result == -1) {
// 主要错误:记录日志或处理
fprintf(stderr, "ERROR: Failed to close 'file1.txt' (fd=%d): %s\n", fd1, strerror(errno));
// 根据应用策略决定是否 exit(EXIT_FAILURE)
} else {
printf("Successfully closed 'file1.txt' (fd=%d)\n", fd1);
}
fd1 = -1; // 关闭后设为无效值,防止重复关闭
}

if (fd2 != -1) {
result = close(fd2);
if (result == -1) {
fprintf(stderr, "ERROR: Failed to close 'file2.txt' (fd=%d): %s\n", fd2, strerror(errno));
} else {
printf("Successfully closed 'file2.txt' (fd=%d)\n", fd2);
}
fd2 = -1;
}

if (fd3 != -1) {
result = close(fd3);
if (result == -1) {
fprintf(stderr, "ERROR: Failed to close '/etc/passwd' (fd=%d): %s\n", fd3, strerror(errno));
} else {
printf("Successfully closed '/etc/passwd' (fd=%d)\n", fd3);
}
fd3 = -1;
}

printf("Program finished closing all files.\n");
return 0;
}

代码解释:

初始化文件描述符变量为 -1(无效值)。

依次尝试打开多个文件。

如果中间某个 open 失败,在退出前会关闭之前成功打开的文件描述符。

在程序正常结束前,有一个清理阶段,遍历所有可能有效的文件描述符(通过检查是否不等于 -1)并调用 close。

关键: 对每一次 close 调用都检查了返回值。如果失败,会打印错误信息。注意这里使用了 strerror(errno) 来获取 errno 对应的可读错误信息。

关闭后,将文件描述符变量设置回 -1,这是一种防止重复关闭的好习惯(虽然重复关闭同一个 已经关闭的 文件描述符通常是安全的,会返回错误 EBADF,但保持清晰的状态是好的实践)。

总结来说,close 是资源管理的关键环节。养成始终检查 close 返回值的习惯对于编写健壮的 Linux 程序至关重要。

connect系统调用及示例

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

1. 函数介绍

connect 是一个 Linux 系统调用,主要用于TCP 客户端(使用 SOCK_STREAM 套接字)来主动建立与服务器的连接。它也可以用于UDP 客户端(使用 SOCK_DGRAM 套接字)来设置默认的目标地址。

你可以把 connect 想象成拨打电话:

你先有了一个电话听筒(通过 socket 创建了套接字)。

你知道你要打给谁(知道服务器的 IP 地址和端口号)。

你按下拨打键(调用 connect)。

电话那头的服务器响铃,接听后,你们之间的通话线路就建立了。

对于 TCP 来说,connect 会触发 TCP 的**三次握手 **(Three-way Handshake) 过程,这是 TCP 协议用来建立可靠连接的标准步骤。

2. 函数原型

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

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

3. 功能

  • **建立连接 **(TCP) 对于 TCP 套接字 (SOCK_STREAM),connect 发起一个连接请求到由 addr 参数指定的服务器地址。它会执行 TCP 三次握手,直到连接成功建立或失败。

  • **设置默认目标 **(UDP) 对于 UDP 套接字 (SOCK_DGRAM),connect 不会发送任何数据包或执行握手。它只是在内核中为该套接字记录下目标地址。之后对该套接字的 write/send 调用将默认发送到这个地址,read/recv 只会接收来自这个地址的数据。这简化了 UDP 客户端的编程,使其行为更像 TCP。

4. 参数

  • int sockfd: 这是之前通过 socket() 系统调用成功创建的套接字文件描述符。

const struct sockaddr *addr: 这是一个指向套接字地址结构的指针,该结构包含了要连接的服务器的地址信息(IP 地址和端口号)。

  • 对于 IPv4,通常使用 struct sockaddr_in。

  • 对于 IPv6,通常使用 struct sockaddr_in6。

  • 在调用时,通常会将具体的地址结构(如 sockaddr_in)强制类型转换为 (struct sockaddr *) 传入。

socklen_t addrlen: 这是 addr 指向的地址结构的大小(以字节为单位)。

  • 对于 struct sockaddr_in,这个值通常是 sizeof(struct sockaddr_in)。

  • 对于 struct sockaddr_in6,这个值通常是 sizeof(struct sockaddr_in6)。

5. 返回值

  • **成功时 **(TCP) 对于 TCP 套接字,连接成功建立后,返回 0。此时,套接字 sockfd 已准备好进行数据传输(read/write)。

  • **成功时 **(UDP) 对于 UDP 套接字,总是立即返回 0,因为它只是设置了默认地址,并不真正“连接”。

  • 失败时: 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 ECONNREFUSED 远程主机拒绝连接,ETIMEDOUT 连接超时,EHOSTUNREACH 主机不可达,EADDRINUSE 本地地址已被使用,EINVAL 套接字状态无效等)。

阻塞与非阻塞:

  • 阻塞套接字(默认):调用 connect 会阻塞(挂起)当前进程,直到连接成功建立或发生错误。对于 TCP,这意味着等待三次握手完成。

非阻塞套接字(通过 SOCK_NONBLOCK 或 fcntl 设置):调用 connect 会立即返回。

  • 如果连接不能立即建立,connect 返回 -1,并将 errno 设置为 EINPROGRESS。

  • 程序需要使用 select、poll 或 epoll 来检查套接字何时变为可写(表示连接完成),然后使用 getsockopt 检查 SO_ERROR 选项来确定连接最终是成功还是失败。

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

  • socket: 用于创建套接字,是 connect 的前置步骤。

  • bind: (服务器端)将套接字绑定到本地地址。客户端通常不需要显式调用 bind。

  • listen / accept: (服务器端)用于监听和接受客户端的连接请求。

  • getpeername: 连接建立后,用于获取对方(peer)的地址信息。

  • getsockname: 用于获取本地套接字的地址信息。

  • close: 关闭套接字,对于 TCP 连接,这会发起断开连接的四次挥手过程。

7. 示例代码

示例 1:基本的 TCP 客户端 connect

这个例子演示了一个典型的 TCP 客户端如何使用 connect 连接到服务器。

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
// tcp_client.c
#include <sys/socket.h> // socket, connect
#include <netinet/in.h> // sockaddr_in
#include <arpa/inet.h> // inet_pton
#include <unistd.h> // close, write, read
#include <stdio.h> // perror, printf, fprintf
#include <stdlib.h> // exit
#include <string.h> // strlen, memset

#define PORT 8080
#define SERVER_IP "127.0.0.1" // 可替换为实际服务器 IP

int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from TCP client";
char buffer&#91;1024] = {0};
int valread;

// 1. 创建 TCP 套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
printf("TCP client socket created (fd: %d)\n", sock);

// 2. 配置服务器地址
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);

// 将 IPv4 地址从文本转换为二进制
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
fprintf(stderr, "Invalid address/ Address not supported: %s\n", SERVER_IP);
close(sock);
exit(EXIT_FAILURE);
}

// 3. 发起连接 (阻塞调用)
printf("Connecting to %s:%d...\n", SERVER_IP, PORT);
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connection failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("Connected to server successfully.\n");

// 4. 发送数据
printf("Sending message: %s\n", hello);
if (write(sock, hello, strlen(hello)) != (ssize_t)strlen(hello)) {
perror("write failed");
// 注意:write 返回 ssize_t,strlen 返回 size_t
// 比较时最好类型一致或强制转换
} else {
printf("Message sent successfully.\n");
}

// 5. 接收响应
printf("Waiting for server response...\n");
valread = read(sock, buffer, sizeof(buffer) - 1);
if (valread > 0) {
buffer&#91;valread] = '\0'; // 确保字符串结束
printf("Received from server: %s\n", buffer);
} else if (valread == 0) {
printf("Server closed the connection.\n");
} else {
perror("read failed");
}

// 6. 关闭套接字
close(sock);
printf("Client socket closed.\n");

return 0;
}

代码解释:

使用 socket(AF_INET, SOCK_STREAM, 0) 创建一个 IPv4 TCP 套接字。

初始化 struct sockaddr_in 结构体 serv_addr,填入服务器的 IP 地址和端口号。

  • 使用 inet_pton(AF_INET, …) 将点分十进制的 IP 字符串转换为网络二进制格式。

  • 使用 htons(PORT) 将端口号从主机字节序转换为网络字节序。

关键步骤: 调用 connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))。

  • 这是一个阻塞调用。程序会在此处暂停,直到连接建立(三次握手完成)或失败。

  • 如果服务器没有运行或无法访问,connect 会失败并返回 -1,同时设置 errno。

连接成功后,使用 write 发送数据到服务器。

使用 read 从服务器接收数据。

通信结束后,调用 close 关闭套接字。

示例 2:UDP 客户端使用 connect 简化通信

这个例子展示了如何在 UDP 客户端中使用 connect 来设置默认目标地址,从而可以使用 read/write 而不是 sendto/recvfrom。

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
// udp_client_with_connect.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PORT 8081
#define SERVER_IP "127.0.0.1"

int main() {
int sock;
struct sockaddr_in serv_addr;
char *message = "Hello UDP server via connect!";
char buffer&#91;1024];
ssize_t bytes_sent, bytes_received;

// 1. 创建 UDP 套接字
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
printf("UDP client socket created (fd: %d)\n", sock);

// 2. 配置服务器地址
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
fprintf(stderr, "Invalid address/ Address not supported\n");
close(sock);
exit(EXIT_FAILURE);
}

// 3. 使用 connect 设置默认目标地址 (UDP 的 connect 不发送数据包)
printf("Connecting UDP socket to %s:%d (sets default destination)...\n", SERVER_IP, PORT);
if (connect(sock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connect failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("UDP socket 'connected' (default destination set).\n");

// 4. 发送数据 (无需指定地址)
// write/send 都可以,因为目标地址已通过 connect 设置
bytes_sent = write(sock, message, strlen(message));
if (bytes_sent < 0) {
perror("write failed");
} else {
printf("Sent %zd bytes to server: %s\n", bytes_sent, message);
}

// 5. 接收数据 (只接收来自已连接地址的数据)
// read/recv 都可以
bytes_received = read(sock, buffer, sizeof(buffer) - 1);
if (bytes_received < 0) {
perror("read failed");
} else if (bytes_received == 0) {
printf("Server closed the (logical) connection.\n");
} else {
buffer&#91;bytes_received] = '\0';
printf("Received %zd bytes from server: %s\n", bytes_received, buffer);
}

// 6. 关闭套接字
close(sock);
printf("UDP client socket closed.\n");

return 0;
}

代码解释:

创建一个 SOCK_DGRAM (UDP) 套接字。

配置服务器地址 serv_addr。

关键步骤: 调用 connect(sock, …). 对于 UDP,这不会发送任何网络数据包。

它只是告诉内核:“对于这个套接字 sock,如果没有特别指定,以后发送的数据就发到 serv_addr,接收的数据也只接受来自 serv_addr 的。”

连接后,可以使用 write/read 或 send/recv 进行数据传输,无需再指定目标地址(不像 sendto/recvfrom 那样)。

这简化了 UDP 客户端的代码,使其用法更接近 TCP。

示例 3:非阻塞 connect (高级用法)

这个例子演示了如何对非阻塞套接字使用 connect,并使用 select 来等待连接完成。

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

#define PORT 8080
#define SERVER_IP "127.0.0.1"
#define TIMEOUT_SEC 5

int main() {
int sock;
struct sockaddr_in serv_addr;
fd_set write_fds;
struct timeval timeout;
int error;
socklen_t len = sizeof(error);

// 1. 创建 TCP 套接字
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}

// 2. 将套接字设置为非阻塞模式
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
perror("fcntl F_GETFL failed");
close(sock);
exit(EXIT_FAILURE);
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl F_SETFL failed");
close(sock);
exit(EXIT_FAILURE);
}
printf("Socket set to non-blocking mode.\n");

// 3. 配置服务器地址
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {
fprintf(stderr, "Invalid address\n");
close(sock);
exit(EXIT_FAILURE);
}

// 4. 发起连接 (非阻塞调用)
printf("Initiating non-blocking connect to %s:%d...\n", SERVER_IP, PORT);
int conn_result = connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

if (conn_result < 0) {
if (errno != EINPROGRESS) {
perror("connect failed with unexpected error");
close(sock);
exit(EXIT_FAILURE);
}
// 如果 errno == EINPROGRESS, 连接正在进行中
printf("Connect in progress...\n");
} else {
// 立即成功 (罕见)
printf("Connect succeeded immediately.\n");
close(sock);
return 0;
}

// 5. 使用 select 等待套接字变为可写 (连接完成或失败)
FD_ZERO(&write_fds);
FD_SET(sock, &write_fds);
timeout.tv_sec = TIMEOUT_SEC;
timeout.tv_usec = 0;

printf("Waiting up to %d seconds for connection to complete...\n", TIMEOUT_SEC);
int select_result = select(sock + 1, NULL, &write_fds, NULL, &timeout);

if (select_result < 0) {
perror("select failed");
close(sock);
exit(EXIT_FAILURE);
} else if (select_result == 0) {
printf("Connection timed out after %d seconds.\n", TIMEOUT_SEC);
close(sock);
exit(EXIT_FAILURE);
} else {
// select 返回 > 0, 表示至少有一个 fd 就绪
// 我们只监视了 sock 的可写事件
if (FD_ISSET(sock, &write_fds)) {
// 套接字可写,连接过程完成(成功或失败)
// 需要通过 getsockopt 检查 SO_ERROR 来确定最终结果
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
perror("getsockopt failed");
close(sock);
exit(EXIT_FAILURE);
}

if (error != 0) {
// error 变量包含了 connect 失败时的 errno 值
errno = error;
perror("connect failed asynchronously");
close(sock);
exit(EXIT_FAILURE);
} else {
printf("Asynchronous connect succeeded!\n");
}
}
}

// 6. 连接成功,可以进行通信了
printf("Ready to send/receive data on the connected socket.\n");

// ... 这里可以进行 read/write 操作 ...

close(sock);
printf("Socket closed.\n");
return 0;
}

代码解释:

创建 TCP 套接字。

使用 fcntl 将套接字设置为非阻塞模式 (O_NONBLOCK)。

配置服务器地址。

调用 connect。因为套接字是非阻塞的:

  • 如果连接能立即建立,connect 返回 0(罕见)。

  • 如果连接不能立即建立(通常是这种情况),connect 返回 -1,并将 errno 设置为 EINPROGRESS。这表明连接正在后台进行。

关键: 使用 select 来等待连接完成。

  • 监视套接字的可写 (write_fds) 事件。对于非阻塞 connect,当连接尝试完成(无论成功还是失败)时,套接字会变为可写。

  • 设置一个超时时间,避免无限期等待。

select 返回后,检查是超时还是套接字就绪。

如果套接字就绪,调用 getsockopt(sock, SOL_SOCKET, SO_ERROR, …) 来获取连接的最终状态。

  • 如果 SO_ERROR 的值为 0,表示连接成功。

  • 如果 SO_ERROR 的值非 0,该值就是连接失败时的错误码,将其赋给 errno 并打印错误信息。

连接成功后,套接字就可以像平常一样用于 read/write 了。

重要提示与注意事项:

TCP 三次握手: 对于 TCP 套接字,connect 的核心作用是启动并等待三次握手完成。

UDP 的特殊性: 对于 UDP,connect 不涉及网络交互,仅在内核中设置默认地址。

阻塞 vs 非阻塞: 理解阻塞和非阻塞 connect 的行为差异对于编写高性能或响应式网络程序至关重要。

错误处理: connect 失败的错误码 (errno) 提供了丰富的信息,如 ECONNREFUSED (端口未监听), ETIMEDOUT (超时), ENETUNREACH (网络不可达) 等。

客户端通常不 bind: 客户端程序通常不需要调用 bind 来绑定本地地址,操作系统会自动分配一个临时端口。

getpeername: 连接建立后,可以使用 getpeername 来确认连接的对端地址。

总结:

connect 是 TCP 客户端发起网络连接的关键函数,它对于 UDP 客户端则提供了一种简化地址管理的方法。掌握其在阻塞和非阻塞模式下的行为对于进行有效的网络编程非常重要。

copy_file_range系统调用示例

copy_file_range系统调用示例

探索Linux系统编程精髓,学习使用copy_file_range函数实现高效文件复制技巧。立即访问,掌握关键API用法!这次我们介绍Linux 系统编程中的重要函数 copy_file_range;

1. 函数介绍

copy_file_range 是一个相对较新的 Linux 系统调用(内核版本 >= 4.5),专门用于在两个文件描述符之间高效地复制数据。

你可以把它想象成一个优化版的 “文件剪切板” 功能:

你不需要先 read 把数据从源文件拿到用户空间的缓冲区。

也不需要再 write 把数据从用户空间缓冲区放到目标文件。

而是直接告诉内核:“嘿,内核,帮我把数据从文件 A 的这里,复制到文件 B 的那里。”

内核会尽可能地在内核空间内部完成这个操作,利用各种优化手段(如copy-on-write (COW)、 reflink、缓冲区到缓冲区的直接传输等),避免了在用户空间和内核空间之间来回复制数据(即避免了用户态和内核态的上下文切换以及数据拷贝**的开销),从而极大地提高了文件复制的效率。

这在复制大文件、备份、文件系统内部移动/复制(如果文件系统支持)等场景下尤其有用。

2. 函数原型

1
2
3
4
5
6
7
8
#define _GNU_SOURCE // 必须定义以使用 copy_file_range
#include <unistd.h> // ssize_t
#include <fcntl.h> // 定义了相关的标志 (如果需要)

ssize_t copy_file_range(int fd_in, off_t *off_in,
int fd_out, off_t *off_out,
size_t len, unsigned int flags);

3. 功能

  • 高效复制: 在内核内部将数据从一个文件描述符 fd_in 复制到另一个文件描述符 fd_out。

  • 指定范围: 可以指定源文件的起始偏移量 off_in、目标文件的起始偏移量 off_out 以及要复制的字节数 len。

  • 灵活偏移: 通过 off_in 和 off_out 指针,可以控制是使用文件的当前偏移量还是指定绝对偏移量。

  • 潜在优化: 内核可能会利用文件系统特性(如 reflink)来实现零拷贝或写时复制,使得复制操作极其快速。

4. 参数

  • int fd_in: 源文件的文件描述符。这个文件描述符必须是可读的。

off_t *off_in: 指向一个 off_t 类型变量的指针,该变量指定在源文件中开始复制的偏移量。

  • 如果 off_in 是 NULL: 复制从源文件的当前偏移量(由 lseek(fd_in, 0, SEEK_CUR) 决定)开始。复制操作会更新源文件的当前偏移量(增加已复制的字节数)。

  • 如果 off_in 非 NULL: 复制从 *off_in 指定的绝对偏移量开始。复制操作不会更新源文件的当前偏移量,但会更新 *off_in 的值(增加已复制的字节数)。

int fd_out: 目标文件的文件描述符。这个文件描述符必须是可写的。

off_t *off_out: 指向一个 off_t 类型变量的指针,该变量指定在目标文件中开始写入的偏移量。

  • 如果 off_out 是 NULL: 数据写入到目标文件的当前偏移量。复制操作会更新目标文件的当前偏移量。

  • 如果 off_out 非 NULL: 数据写入到 *off_out 指定的绝对偏移量。复制操作不会更新目标文件的当前偏移量,但会更新 *off_out 的值。

size_t len: 请求复制的最大字节数。

unsigned int flags: 控制复制行为的标志。在 Linux 中,目前这个参数必须设置为 0。保留供将来扩展。

5. 返回值

  • 成功时: 返回实际复制的字节数(一个非负值)。这个数可能小于请求的 len(例如,在读取时遇到文件末尾)。

  • 失败时: 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 EBADF 文件描述符无效或权限不足,EINVAL 参数无效,EXDEV fd_in 和 fd_out 不在同一个文件系统挂载点上且文件系统不支持跨挂载点复制,ENOMEM 内存不足等)。

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

  • sendfile: 用于在文件描述符之间(通常是文件到套接字)高效传输数据,是 copy_file_range 的前身和灵感来源之一。sendfile 通常不支持两个普通文件之间的复制(在旧内核上)。

  • splice: 用于在两个可 pipe 的文件描述符之间移动数据,也是一种零拷贝技术。

  • 传统的 read/write 循环: 最基础的文件复制方法,效率较低,因为涉及多次用户态/内核态切换和数据拷贝。

  • mmap + memcpy: 另一种零拷贝思路,但使用起来更复杂,且不一定比 copy_file_range 更快。

7. 示例代码

示例 1:基本使用 copy_file_range 复制文件

这个例子演示了如何使用 copy_file_range 将一个文件的内容复制到另一个文件。

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

#define BUFFER_SIZE 1024

int main(int argc, char *argv&#91;]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <source_file> <destination_file>\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

const char *src_filename = argv&#91;1];
const char *dst_filename = argv&#91;2];
int src_fd, dst_fd;
struct stat src_stat;
off_t offset_in, offset_out;
ssize_t bytes_copied, total_bytes_copied = 0;
size_t remaining;

// 1. 打开源文件 (只读)
src_fd = open(src_filename, O_RDONLY);
if (src_fd == -1) {
perror("Error opening source file");
exit(EXIT_FAILURE);
}

// 2. 获取源文件大小
if (fstat(src_fd, &src_stat) == -1) {
perror("Error getting source file stats");
close(src_fd);
exit(EXIT_FAILURE);
}

// 3. 创建/打开目标文件 (写入、创建、截断)
dst_fd = open(dst_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd == -1) {
perror("Error opening/creating destination file");
close(src_fd);
exit(EXIT_FAILURE);
}

printf("Copying '%s' to '%s' using copy_file_range()...\n", src_filename, dst_filename);
printf("Source file size: %ld bytes\n", (long)src_stat.st_size);

// 4. 使用 copy_file_range 进行复制
// 初始化偏移量为 0
offset_in = 0;
offset_out = 0;
remaining = src_stat.st_size;

while (remaining > 0) {
// 尝试复制剩余的所有字节,或者一个大块
// copy_file_range 可能不会一次复制完所有请求的字节
size_t to_copy = (remaining > 0x7ffff000) ? 0x7ffff000 : remaining; // 限制单次调用大小

bytes_copied = copy_file_range(src_fd, &offset_in, dst_fd, &offset_out, to_copy, 0);

if (bytes_copied == -1) {
perror("Error in copy_file_range");
// 尝试清理
close(src_fd);
close(dst_fd);
exit(EXIT_FAILURE);
}

if (bytes_copied == 0) {
// 可能已经到达源文件末尾
fprintf(stderr, "Warning: copy_file_range returned 0 before copying all data.\n");
break;
}

total_bytes_copied += bytes_copied;
remaining -= bytes_copied;
printf(" Copied %zd bytes (total: %zd)\n", bytes_copied, total_bytes_copied);
}

printf("Copy completed. Total bytes copied: %zd\n", total_bytes_copied);

// 5. 关闭文件描述符
if (close(src_fd) == -1) {
perror("Error closing source file");
}
if (close(dst_fd) == -1) {
perror("Error closing destination file");
}

return 0;
}

如何测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建一个大一点的测试文件
dd if=/dev/urandom of=large_source_file.txt bs=1M count=10 # 创建 10MB 随机数据文件
# 或者简单点
echo "This is the content of the source file." > small_source_file.txt

# 编译并运行
gcc -o copy_file_range_basic copy_file_range_basic.c
./copy_file_range_basic small_source_file.txt copied_file.txt

# 检查结果
cat copied_file.txt
ls -l small_source_file.txt copied_file.txt

代码解释:

检查命令行参数。

以只读模式打开源文件 src_fd。

使用 fstat 获取源文件的大小 src_stat.st_size。

以写入、创建、截断模式打开(或创建)目标文件 dst_fd。

关键步骤: 进入 while 循环进行复制。

  • 初始化 offset_in 和 offset_out 为 0。

  • remaining 变量跟踪还剩多少字节需要复制。

在循环中,调用 copy_file_range(src_fd, &offset_in, dst_fd, &offset_out, to_copy, 0)。

  • src_fd, dst_fd: 源和目标文件描述符。

  • &offset_in, &offset_out: 传递偏移量的指针。这使得 copy_file_range 在复制后自动更新这两个变量,指向下一次复制的起始位置。

  • to_copy: 本次尝试复制的字节数(做了大小限制)。

  • 0: flags 参数,必须为 0。

检查返回值 bytes_copied。

如果成功(> 0),则更新 total_bytes_copied 和 remaining。

如果返回 0,可能表示源文件已到末尾。

如果返回 -1,则处理错误。

循环直到 remaining 为 0 或出错。

打印总复制字节数。

关闭文件描述符。

示例 2:对比 copy_file_range 与传统 read/write 循环

这个例子通过复制同一个大文件,对比 copy_file_range 和传统的 read/write 循环在性能上的差异。

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

#define BUFFER_SIZE (1024 * 1024) // 1MB buffer

// 使用 read/write 循环复制文件
ssize_t copy_with_read_write(int src_fd, int dst_fd) {
char *buffer = malloc(BUFFER_SIZE);
if (!buffer) {
perror("malloc buffer");
return -1;
}

ssize_t total = 0;
ssize_t nread, nwritten;

while ((nread = read(src_fd, buffer, BUFFER_SIZE)) > 0) {
char *buf_ptr = buffer;
ssize_t nleft = nread;

while (nleft > 0) {
nwritten = write(dst_fd, buf_ptr, nleft);
if (nwritten <= 0) {
if (nwritten == -1 && errno == EINTR) {
continue; // Interrupted, retry
}
perror("write");
free(buffer);
return -1;
}
nleft -= nwritten;
buf_ptr += nwritten;
}
total += nread;
}

if (nread == -1) {
perror("read");
free(buffer);
return -1;
}

free(buffer);
return total;
}

// 使用 copy_file_range 复制文件
ssize_t copy_with_copy_file_range(int src_fd, int dst_fd, size_t file_size) {
off_t offset_in = 0;
off_t offset_out = 0;
size_t remaining = file_size;
ssize_t bytes_copied, total = 0;

while (remaining > 0) {
size_t to_copy = (remaining > 0x7ffff000) ? 0x7ffff000 : remaining;
bytes_copied = copy_file_range(src_fd, &offset_in, dst_fd, &offset_out, to_copy, 0);
if (bytes_copied == -1) {
perror("copy_file_range");
return -1;
}
if (bytes_copied == 0) {
break;
}
total += bytes_copied;
remaining -= bytes_copied;
}
return total;
}

int main(int argc, char *argv&#91;]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <source_file>\n", argv&#91;0]);
exit(EXIT_FAILURE);
}

const char *src_filename = argv&#91;1];
int src_fd;
struct stat src_stat;
clock_t start, end;
double cpu_time_used;

// 打开源文件
src_fd = open(src_filename, O_RDONLY);
if (src_fd == -1) {
perror("open source file");
exit(EXIT_FAILURE);
}

if (fstat(src_fd, &src_stat) == -1) {
perror("fstat source file");
close(src_fd);
exit(EXIT_FAILURE);
}

printf("Source file: %s\n", src_filename);
printf("File size: %ld bytes (%.2f MB)\n", (long)src_stat.st_size, (double)src_stat.st_size / (1024*1024));

// --- 测试 1: copy_file_range ---
printf("\n--- Testing copy_file_range ---\n");
char dst_filename1&#91;] = "copy_file_range_dst.tmp";
int dst_fd1 = open(dst_filename1, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd1 == -1) {
perror("open destination file 1");
close(src_fd);
exit(EXIT_FAILURE);
}

// 重置源文件偏移量
if (lseek(src_fd, 0, SEEK_SET) == -1) {
perror("lseek src_fd");
close(src_fd);
close(dst_fd1);
exit(EXIT_FAILURE);
}

start = clock();
ssize_t copied1 = copy_with_copy_file_range(src_fd, dst_fd1, src_stat.st_size);
end = clock();

if (copied1 == -1) {
fprintf(stderr, "copy_file_range failed.\n");
} else {
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf(" Bytes copied: %zd\n", copied1);
printf(" Time taken: %f seconds\n", cpu_time_used);
}

close(dst_fd1);

// --- 测试 2: read/write loop ---
printf("\n--- Testing read/write loop ---\n");
char dst_filename2&#91;] = "read_write_dst.tmp";
int dst_fd2 = open(dst_filename2, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (dst_fd2 == -1) {
perror("open destination file 2");
close(src_fd);
// Cleanup
unlink(dst_filename1);
exit(EXIT_FAILURE);
}

// 重置源文件偏移量
if (lseek(src_fd, 0, SEEK_SET) == -1) {
perror("lseek src_fd");
close(src_fd);
close(dst_fd2);
unlink(dst_filename1);
exit(EXIT_FAILURE);
}

start = clock();
ssize_t copied2 = copy_with_read_write(src_fd, dst_fd2);
end = clock();

if (copied2 == -1) {
fprintf(stderr, "read/write loop failed.\n");
} else {
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf(" Bytes copied: %zd\n", copied2);
printf(" Time taken: %f seconds\n", cpu_time_used);
}

close(dst_fd2);
close(src_fd);

// --- 清理 ---
unlink(dst_filename1);
unlink(dst_filename2);

printf("\nPerformance comparison completed.\n");
if (copied1 != -1 && copied2 != -1) {
printf("copy_file_range is expected to be faster, especially for large files on same filesystem.\n");
}
return 0;
}

如何测试:

1
2
3
4
5
6
7
# 创建一个较大的测试文件
dd if=/dev/zero of=test_large_file.txt bs=1M count=100 # 100MB 文件

# 编译并运行
gcc -o copy_file_range_vs_read_write copy_file_range_vs_read_write.c
./copy_file_range_vs_read_write test_large_file.txt

代码解释:

定义了两个函数:copy_with_read_write 和 copy_with_copy_file_range,分别实现两种复制方法。

copy_with_read_write:

  • 分配一个 1MB 的缓冲区。

  • 使用 while 循环 read 数据到缓冲区。

  • 内层 while 循环确保 write 将整个缓冲区的内容都写入目标文件(处理 write 可能部分写入的情况)。

  • 累计复制的总字节数。

copy_with_copy_file_range:

  • 使用 off_t 变量 offset_in 和 offset_out 来跟踪源和目标的偏移量。

  • 使用 while 循环调用 copy_file_range,直到复制完所有数据。

main 函数:

  • 获取源文件大小。

  • 依次测试两种方法。

  • 使用 clock() 来测量 CPU 时间(注意:clock 测量的是 CPU 时间,不是墙上时间,但对于比较相对性能还是有用的)。

  • 打印结果并清理临时文件。

重要提示与注意事项:

内核版本: 需要 Linux 内核 4.5 或更高版本。

glibc 版本: 需要 glibc 2.27 或更高版本才能直接使用 copy_file_range 函数。旧版本可能需要使用 syscall。

性能优势: copy_file_range 的主要优势在于其潜在的内核内部优化。如果源文件和目标文件在同一个支持 reflink 的文件系统(如 Btrfs, XFS, OCFS2)上,copy_file_range 可能会瞬间创建一个写时复制(COW)的副本,速度极快。即使不支持 reflink,它也通常比 read/write 循环更高效,因为它减少了用户态和内核态之间的数据拷贝。

flags 参数: 目前必须为 0。未来可能会添加新标志。

跨文件系统: copy_file_range 可能不支持在不同挂载点的文件系统之间复制(返回 EXDEV 错误),尽管某些组合可能支持。

偏移量指针: 理解 off_in 和 off_out 指针的行为(NULL vs 非 NULL)非常重要。使用指针允许在不修改文件自身偏移量的情况下进行复制,非常适合多线程环境或需要精确控制偏移的场景。

返回值: 像许多 I/O 函数一样,copy_file_range 可能不会一次完成所有请求的字节复制,需要循环处理。

错误处理: 始终检查返回值和 errno。EBADF, EINVAL, EXDEV, ENOMEM 是可能遇到的错误。

总结:

copy_file_range 是一个强大且高效的系统调用,用于在文件描述符之间复制数据。它通过将复制操作完全下放到内核来避免用户空间的开销,并可能利用底层文件系统的高级特性(如 reflink)来实现极致的性能。对于需要在 Linux 系统上进行高性能文件复制的应用程序来说,copy_file_range 是一个值得优先考虑的选择。

copy_file_range 系统调用示例, copy_file_range 函数使用方法, linux copy_file_range 详解, copy_file_range 系统编程教程, 如何使用 copy_file_range 函数, copy_file_range 在 Linux 中的应用, Linux 系统调用 copy_file_range, copy_file_range 示例代码, Linux 内核 copy_file_range 说明, copy_file_range 实战案例

https://www.calcguide.tech/2025/08/06/copy-file-range系统调用/

sync_file_range系统调用及示例

C语言高级编程技巧与最佳实践-完整版

C语言高级编程技巧与最佳实践 - 完整版

目录

宏定义与预处理技巧

内存管理高级技巧

函数指针与回调机制

数据结构设计

并发与多线程

错误处理与异常机制

性能优化技巧

调试与测试技巧

跨平台编程

安全编程实践

综合演示示例

宏定义与预处理技巧

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
/**
* 平台和编译器检测
* 用于条件编译和跨平台兼容性
*/
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM_WINDOWS
#elif defined(__linux__)
#define PLATFORM_LINUX
#elif defined(__APPLE__)
#define PLATFORM_MACOS
#endif

// 编译器检测
#if defined(__GNUC__)
#define COMPILER_GCC
#elif defined(_MSC_VER)
#define COMPILER_MSVC
#endif

// 版本检测
#if __STDC_VERSION__ >= 201112L
#define C11_SUPPORTED
#endif

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
/**
* 高级宏定义集合
* 提供类型安全和便捷的宏工具
*/

// 字符串化和连接
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define CONCAT(a, b) a##b

// 获取数组长度
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)&#91;0]))

// 容器of宏(从成员指针获取容器指针)
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })

// 最大最小值
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))

// 交换变量(不使用临时变量)
#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while(0)

// 编译时断言
#define STATIC_ASSERT(condition, message) \
typedef char static_assertion_##message&#91;(condition) ? 1 : -1]

// 可变参数宏
#define DEBUG_PRINT(fmt, ...) \
fprintf(stderr, "&#91;DEBUG] %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

3. 现代C语言特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* C11现代特性使用
* 利用新标准提高代码质量
*/

// 泛型选择
#define generic_max(a, b) _Generic((a), \
int: max_int, \
float: max_float, \
double: max_double \
)(a, b)

// 静态断言(C11)
_Static_assert(sizeof(int) >= 4, "int must be at least 4 bytes");

// 线程局部存储(C11)
_Thread_local int thread_var;

内存管理高级技巧

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
/**
* 内存池实现
* 提供高效的内存分配和回收机制
*/
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. 智能指针模拟

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
/**
* 智能指针模拟系统
* 实现引用计数自动内存管理
*/

#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. 内存对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 内存对齐工具
* 确保数据结构在内存中的正确对齐
*/

// C11对齐
_Alignas(16) char aligned_buffer&#91;256];

// 手动对齐
#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;
}

函数指针与回调机制

1. 面向对象风格编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 虚函数表模拟
* 实现C语言中的面向对象编程
*/

// 虚函数表模拟
typedef struct {
void (*destroy)(void *self);
void (*print)(void *self);
int (*compare)(void *self, void *other);
} vtable_t;

typedef struct {
vtable_t *vtable;
// 具体数据
} object_t;

// 多态调用
#define CALL_METHOD(obj, method, ...) \
((obj)->vtable->method((obj), ##__VA_ARGS__))

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
/**
* 状态机实现
* 提供灵活的状态管理机制
*/

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. 插件系统设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 插件系统设计
* 支持动态加载和扩展功能
*/

typedef struct {
const char *name;
int version;
int (*init)(void);
void (*cleanup)(void);
void* (*create_instance)(void);
} plugin_interface_t;

// 动态加载插件
#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

数据结构设计

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
/**
* 双向链表实现
* 提供高效的插入和删除操作
*/

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;

// 双向链表操作
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. 哈希表实现

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
/**
* 哈希表实现
* 提供快速的键值对存储和检索
*/

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. 环形缓冲区

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
/**
* 环形缓冲区实现
* 适用于生产者-消费者模式
*/

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;
}

并发与多线程

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
/**
* 线程安全计数器
* 提供原子操作和条件等待
*/

#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. 读写锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 读写锁实现
* 支持多读单写的并发控制
*/

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. 无锁编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 无锁编程工具
* 使用原子操作实现高性能并发
*/

#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);
}

错误处理与异常机制

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
/**
* 错误码系统
* 提供结构化的错误处理机制
*/

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)

// 带上下文的错误处理
typedef struct {
error_code_t code;
const char *message;
const char *file;
int line;
} error_context_t;

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
/**
* 异常模拟机制
* 使用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. 资源管理RAII

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* RAII资源管理
* 确保资源的自动释放
*/

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);
}
}

性能优化技巧

1. 缓存友好的数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 缓存友好的数据结构
* 优化内存布局提高缓存命中率
*/

// 结构体打包优化
struct __attribute__((packed)) packed_struct {
char a;
int b;
short c;
};

// 缓存行对齐
#define CACHE_LINE_SIZE 64
struct __attribute__((aligned(CACHE_LINE_SIZE))) cache_aligned_struct {
int data&#91;16];
};

2. 分支预测优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 分支预测优化
* 使用编译器提示提高执行效率
*/

// 静态分支预测
#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. 内联汇编优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 内联汇编优化
* 直接使用CPU指令提高性能
*/

// 获取时间戳计数器
static inline uint64_t rdtsc(void) {
uint32_t lo, hi;
__asm__ __volatile__("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}

// 内存屏障
#define MEMORY_BARRIER() __asm__ __volatile__("" ::: "memory")

4. SIMD优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* SIMD优化
* 利用向量指令并行处理数据
*/

#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);
}
// 处理剩余元素
for (; i < n; i++) {
result&#91;i] = a&#91;i] + b&#91;i];
}
}
#endif

调试与测试技巧

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
/**
* 调试工具宏
* 提供便捷的调试和性能分析功能
*/

#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

// 性能计时
#define TIME_IT(code, result_var) \
do { \
clock_t start = clock(); \
code; \
result_var = ((double)(clock() - start)) / CLOCKS_PER_SEC; \
} while(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
/**
* 单元测试框架
* 提供结构化的测试支持
*/

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. 内存泄漏检测

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
/**
* 内存泄漏检测
* 跟踪内存分配和释放
*/

#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

跨平台编程

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
/**
* 平台抽象层
* 提供统一的跨平台接口
*/

// 线程抽象
#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. 文件路径处理

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
/**
* 文件路径处理
* 提供跨平台的路径操作
*/

#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. 字节序处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 字节序处理
* 确保数据在网络传输中的正确性
*/

// 网络字节序转换
#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)

安全编程实践

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
/**
* 缓冲区溢出防护
* 提供安全的字符串操作函数
*/

// 安全字符串操作
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;
}

// 格式化字符串安全检查
#define SAFE_PRINTF(buffer, size, format, ...) \
do { \
int __result = snprintf(buffer, size, format, ##__VA_ARGS__); \
if (__result < 0 || (size_t)__result >= size) { \
/* 处理溢出 */ \
buffer&#91;size - 1] = '\0'; \
} \
} while(0)

2. 输入验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 输入验证
* 防止恶意输入导致的安全问题
*/

// 整数溢出检查
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; // 溢出
}
*result = a + b;
return 0;
}

// 指针验证
#define VALIDATE_PTR(ptr) \
do { \
if (!(ptr)) { \
return ERROR_INVALID_PARAM; \
} \
} while(0)

3. 安全随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 安全随机数生成
* 提供密码学安全的随机数
*/

#include <time.h>
#include <stdlib.h>

// 密码学安全随机数(需要平台支持)
#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
// 简单的伪随机数生成器
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

综合演示示例

事件驱动架构演示

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
/**
* 事件驱动架构 - 事件类型定义
* 用于构建灵活的事件处理系统
*/
typedef enum {
EVENT_NONE = 0,
EVENT_TIMER,
EVENT_NETWORK,
EVENT_USER,
EVENT_SYSTEM
} event_type_t;

/**
* 事件结构体
* 包含事件类型、时间戳和用户数据
*/
typedef struct {
event_type_t type;
uint64_t timestamp;
void *data;
size_t data_size;
} event_t;

/**
* 事件处理器函数指针类型
* @param event 事件指针
* @param context 用户上下文
* @return 处理结果
*/
typedef int (*event_handler_t)(event_t *event, void *context);

/**
* 事件监听器结构体
* 存储事件类型和对应的处理器
*/
typedef struct {
event_type_t type;
event_handler_t handler;
void *context;
int priority; // 处理优先级
} event_listener_t;

/**
* 事件循环结构体
* 管理事件队列和监听器
*/
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;

/**
* 创建事件循环
* @param queue_capacity 事件队列容量
* @return 事件循环指针
*/
event_loop_t* event_loop_create(size_t queue_capacity) {
event_loop_t *loop = calloc(1, sizeof(event_loop_t));
if (!loop) return NULL;

loop->queue_capacity = queue_capacity;
loop->event_queue = calloc(queue_capacity, sizeof(event_t));
if (!loop->event_queue) {
free(loop);
return NULL;
}

loop->listeners = calloc(16, sizeof(event_listener_t)); // 初始监听器容量
loop->listener_capacity = 16;

pthread_mutex_init(&loop->mutex, NULL);
pthread_cond_init(&loop->cond, NULL);

return loop;
}

/**
* 添加事件监听器
* @param loop 事件循环
* @param type 事件类型
* @param handler 事件处理器
* @param context 用户上下文
* @param priority 处理优先级
* @return 0成功,-1失败
*/
int event_loop_add_listener(event_loop_t *loop, event_type_t type,
event_handler_t handler, void *context, int priority) {
if (!loop || !handler) return -1;

pthread_mutex_lock(&loop->mutex);

// 扩展监听器数组
if (loop->listener_count >= loop->listener_capacity) {
size_t new_capacity = loop->listener_capacity * 2;
event_listener_t *new_listeners = realloc(loop->listeners,
new_capacity * sizeof(event_listener_t));
if (!new_listeners) {
pthread_mutex_unlock(&loop->mutex);
return -1;
}
loop->listeners = new_listeners;
loop->listener_capacity = new_capacity;
}

// 添加新监听器
event_listener_t *listener = &loop->listeners&#91;loop->listener_count++];
listener->type = type;
listener->handler = handler;
listener->context = context;
listener->priority = priority;

pthread_mutex_unlock(&loop->mutex);
return 0;
}

/**
* 发布事件
* @param loop 事件循环
* @param event 事件指针
* @return 0成功,-1失败
*/
int event_loop_post(event_loop_t *loop, event_t *event) {
if (!loop || !event) return -1;

pthread_mutex_lock(&loop->mutex);

// 检查队列是否已满
if ((loop->queue_tail + 1) % loop->queue_capacity == loop->queue_head) {
pthread_mutex_unlock(&loop->mutex);
return -1; // 队列已满
}

// 复制事件到队列
event_t *queue_event = &loop->event_queue&#91;loop->queue_tail];
queue_event->type = event->type;
queue_event->timestamp = event->timestamp;

if (event->data && event->data_size > 0) {
queue_event->data = malloc(event->data_size);
if (queue_event->data) {
memcpy(queue_event->data, event->data, event->data_size);
queue_event->data_size = event->data_size;
} else {
queue_event->data_size = 0;
}
} else {
queue_event->data = NULL;
queue_event->data_size = 0;
}

loop->queue_tail = (loop->queue_tail + 1) % loop->queue_capacity;

pthread_cond_signal(&loop->cond);
pthread_mutex_unlock(&loop->mutex);

return 0;
}

/**
* 事件循环主函数
* @param loop 事件循环
*/
void event_loop_run(event_loop_t *loop) {
if (!loop) return;

loop->running = 1;

while (loop->running) {
pthread_mutex_lock(&loop->mutex);

// 等待事件
while (loop->queue_head == loop->queue_tail && loop->running) {
pthread_cond_wait(&loop->cond, &loop->mutex);
}

if (!loop->running) {
pthread_mutex_unlock(&loop->mutex);
break;
}

// 获取事件
event_t event = loop->event_queue&#91;loop->queue_head];
loop->queue_head = (loop->queue_head + 1) % loop->queue_capacity;

pthread_mutex_unlock(&loop->mutex);

// 处理事件
for (size_t i = 0; i < loop->listener_count; i++) {
if (loop->listeners&#91;i].type == event.type ||
loop->listeners&#91;i].type == EVENT_NONE) { // EVENT_NONE监听所有事件
loop->listeners&#91;i].handler(&event, loop->listeners&#91;i].context);
}
}

// 清理事件数据
if (event.data) {
free(event.data);
}
}
}

/**
* 停止事件循环
* @param loop 事件循环
*/
void event_loop_stop(event_loop_t *loop) {
if (!loop) return;

pthread_mutex_lock(&loop->mutex);
loop->running = 0;
pthread_cond_signal(&loop->cond);
pthread_mutex_unlock(&loop->mutex);
}

/**
* 销毁事件循环
* @param loop 事件循环
*/
void event_loop_destroy(event_loop_t *loop) {
if (!loop) return;

event_loop_stop(loop);

if (loop->listeners) {
free(loop->listeners);
}

// 清理事件队列中剩余的事件
while (loop->queue_head != loop->queue_tail) {
event_t *event = &loop->event_queue&#91;loop->queue_head];
if (event->data) {
free(event->data);
}
loop->queue_head = (loop->queue_head + 1) % loop->queue_capacity;
}

if (loop->event_queue) {
free(loop->event_queue);
}

pthread_mutex_destroy(&loop->mutex);
pthread_cond_destroy(&loop->cond);

free(loop);
}

无锁队列实现

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
/**
* 无锁队列实现
* 使用CAS操作实现线程安全的无锁队列
*/

#include <stdatomic.h>

/**
* 队列节点结构
*/
typedef struct queue_node {
void *data;
_Atomic(struct queue_node*) next;
} queue_node_t;

/**
* 无锁队列结构
*/
typedef struct {
_Atomic(queue_node_t*) head;
_Atomic(queue_node_t*) tail;
atomic_size_t size;
} lockfree_queue_t;

/**
* 创建无锁队列节点
* @param data 节点数据
* @return 节点指针
*/
static queue_node_t* create_queue_node(void *data) {
queue_node_t *node = malloc(sizeof(queue_node_t));
if (node) {
node->data = data;
atomic_init(&node->next, NULL);
}
return node;
}

/**
* 创建无锁队列
* @return 队列指针
*/
lockfree_queue_t* lockfree_queue_create() {
lockfree_queue_t *queue = malloc(sizeof(lockfree_queue_t));
if (!queue) return NULL;

// 创建哨兵节点
queue_node_t *dummy = create_queue_node(NULL);
if (!dummy) {
free(queue);
return NULL;
}

atomic_init(&queue->head, dummy);
atomic_init(&queue->tail, dummy);
atomic_init(&queue->size, 0);

return queue;
}

/**
* 入队操作
* @param queue 队列
* @param data 数据
* @return 0成功,-1失败
*/
int lockfree_queue_enqueue(lockfree_queue_t *queue, void *data) {
if (!queue) return -1;

queue_node_t *node = create_queue_node(data);
if (!node) return -1;

queue_node_t *prev_tail = NULL;
queue_node_t *prev_tail_next = NULL;

while (1) {
prev_tail = atomic_load(&queue->tail);
prev_tail_next = atomic_load(&prev_tail->next);

// 检查tail是否一致
if (prev_tail == atomic_load(&queue->tail)) {
if (prev_tail_next == NULL) {
// tail是最后一个节点,尝试链接新节点
if (atomic_compare_exchange_weak(&prev_tail->next, &prev_tail_next, node)) {
break; // 成功
}
} else {
// tail不是最后一个节点,尝试推进tail
atomic_compare_exchange_weak(&queue->tail, &prev_tail, prev_tail_next);
}
}
}

// 推进tail
atomic_compare_exchange_weak(&queue->tail, &prev_tail, node);
atomic_fetch_add(&queue->size, 1);

return 0;
}

/**
* 出队操作
* @param queue 队列
* @param data 输出参数:出队数据
* @return 0成功,-1队列为空
*/
int lockfree_queue_dequeue(lockfree_queue_t *queue, void **data) {
if (!queue || !data) return -1;

queue_node_t *head = NULL;
queue_node_t *tail = NULL;
queue_node_t *next = NULL;

while (1) {
head = atomic_load(&queue->head);
tail = atomic_load(&queue->tail);
next = atomic_load(&head->next);

// 检查head是否一致
if (head == atomic_load(&queue->head)) {
if (head == tail) {
// 队列为空或只有一个哨兵节点
if (next == NULL) {
*data = NULL;
return -1; // 队列为空
}
// 队列正在变化,推进tail
atomic_compare_exchange_weak(&queue->tail, &tail, next);
} else {
// 读取数据
*data = next->data;
// 尝试推进head
if (atomic_compare_exchange_weak(&queue->head, &head, next)) {
atomic_fetch_sub(&queue->size, 1);
break;
}
}
}
}

free(head); // 释放旧的head节点
return 0;
}

/**
* 获取队列大小
* @param queue 队列
* @return 队列大小
*/
size_t lockfree_queue_size(lockfree_queue_t *queue) {
return atomic_load(&queue->size);
}

/**
* 销毁无锁队列
* @param queue 队列
*/
void lockfree_queue_destroy(lockfree_queue_t *queue) {
if (!queue) return;

// 清空队列
void *data;
while (lockfree_queue_dequeue(queue, &data) == 0) {
// 数据由调用者负责释放
}

// 释放哨兵节点
queue_node_t *head = atomic_load(&queue->head);
if (head) {
free(head);
}

free(queue);
}

缓存友好的数据结构

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
/**
* 缓存友好的数据结构实现
* 优化内存布局以提高缓存命中率
*/

/**
* 缓存行大小定义
*/
#define CACHE_LINE_SIZE 64

/**
* 缓存对齐宏
*/
#define CACHE_ALIGNED __attribute__((aligned(CACHE_LINE_SIZE)))

/**
* SoA (Structure of Arrays) 向量结构
* 将相关数据分离存储以提高缓存效率
*/
typedef struct {
float *x; // X坐标数组
float *y; // Y坐标数组
float *z; // Z坐标数组
int *id; // ID数组
size_t capacity; // 容量
size_t size; // 当前大小
char padding&#91;CACHE_LINE_SIZE - sizeof(size_t)*2 - sizeof(char*)*4]; // 填充到缓存行边界
} soa_vector_t;

/**
* AoS (Array of Structures) 向量结构
* 传统结构体数组方式
*/
typedef struct {
float x, y, z;
int id;
} aos_point_t;

typedef struct {
aos_point_t *points;
size_t capacity;
size_t size;
char padding&#91;CACHE_LINE_SIZE - sizeof(size_t)*2 - sizeof(void*)]; // 填充
} aos_vector_t;

/**
* 创建SoA向量
* @param initial_capacity 初始容量
* @return SoA向量指针
*/
soa_vector_t* soa_vector_create(size_t initial_capacity) {
soa_vector_t *vec = calloc(1, sizeof(soa_vector_t));
if (!vec) return NULL;

vec->capacity = initial_capacity;
vec->x = malloc(sizeof(float) * initial_capacity);
vec->y = malloc(sizeof(float) * initial_capacity);
vec->z = malloc(sizeof(float) * initial_capacity);
vec->id = malloc(sizeof(int) * initial_capacity);

if (!vec->x || !vec->y || !vec->z || !vec->id) {
soa_vector_destroy(vec);
return NULL;
}

return vec;
}

/**
* 创建AoS向量
* @param initial_capacity 初始容量
* @return AoS向量指针
*/
aos_vector_t* aos_vector_create(size_t initial_capacity) {
aos_vector_t *vec = calloc(1, sizeof(aos_vector_t));
if (!vec) return NULL;

vec->capacity = initial_capacity;
vec->points = malloc(sizeof(aos_point_t) * initial_capacity);
if (!vec->points) {
free(vec);
return NULL;
}

return vec;
}

/**
* SoA向量添加元素
* @param vec SoA向量
* @param x X坐标
* @param y Y坐标
* @param z Z坐标
* @param id ID
* @return 0成功,-1失败
*/
int soa_vector_push(soa_vector_t *vec, float x, float y, float z, int id) {
if (!vec) return -1;

// 检查是否需要扩容
if (vec->size >= vec->capacity) {
size_t new_capacity = vec->capacity * 2;

float *new_x = realloc(vec->x, sizeof(float) * new_capacity);
float *new_y = realloc(vec->y, sizeof(float) * new_capacity);
float *new_z = realloc(vec->z, sizeof(float) * new_capacity);
int *new_id = realloc(vec->id, sizeof(int) * new_capacity);

if (!new_x || !new_y || !new_z || !new_id) {
return -1;
}

vec->x = new_x;
vec->y = new_y;
vec->z = new_z;
vec->id = new_id;
vec->capacity = new_capacity;
}

size_t index = vec->size++;
vec->x&#91;index] = x;
vec->y&#91;index] = y;
vec->z&#91;index] = z;
vec->id&#91;index] = id;

return 0;
}

/**
* AoS向量添加元素
* @param vec AoS向量
* @param x X坐标
* @param y Y坐标
* @param z Z坐标
* @param id ID
* @return 0成功,-1失败
*/
int aos_vector_push(aos_vector_t *vec, float x, float y, float z, int id) {
if (!vec) return -1;

// 检查是否需要扩容
if (vec->size >= vec->capacity) {
size_t new_capacity = vec->capacity * 2;
aos_point_t *new_points = realloc(vec->points, sizeof(aos_point_t) * new_capacity);
if (!new_points) return -1;

vec->points = new_points;
vec->capacity = new_capacity;
}

aos_point_t *point = &vec->points&#91;vec->size++];
point->x = x;
point->y = y;
point->z = z;
point->id = id;

return 0;
}

/**
* SoA向量批量处理(缓存友好)
* @param vec SoA向量
* @param processor 处理函数
* @param context 用户上下文
*/
void soa_vector_process(soa_vector_t *vec,
void (*processor)(float x, float y, float z, int id, void *context),
void *context) {
if (!vec || !processor) return;

// 分别处理每个数组,提高缓存命中率
for (size_t i = 0; i < vec->size; i++) {
processor(vec->x&#91;i], vec->y&#91;i], vec->z&#91;i], vec->id&#91;i], context);
}
}

/**
* AoS向量批量处理
* @param vec AoS向量
* @param processor 处理函数
* @param context 用户上下文
*/
void aos_vector_process(aos_vector_t *vec,
void (*processor)(float x, float y, float z, int id, void *context),
void *context) {
if (!vec || !processor) return;

// 处理结构体数组
for (size_t i = 0; i < vec->size; i++) {
aos_point_t *point = &vec->points&#91;i];
processor(point->x, point->y, point->z, point->id, context);
}
}

/**
* 销毁SoA向量
* @param vec SoA向量
*/
void soa_vector_destroy(soa_vector_t *vec) {
if (!vec) return;

if (vec->x) free(vec->x);
if (vec->y) free(vec->y);
if (vec->z) free(vec->z);
if (vec->id) free(vec->id);
free(vec);
}

/**
* 销毁AoS向量
* @param vec AoS向量
*/
void aos_vector_destroy(aos_vector_t *vec) {
if (!vec) return;

if (vec->points) free(vec->points);
free(vec);
}

/**
* 性能测试结构
*/
typedef struct {
double soa_time;
double aos_time;
size_t elements_processed;
} performance_result_t;

安全字符串操作库

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
/**
* 安全字符串操作库
* 提供防止缓冲区溢出的安全字符串函数
*/

#include <string.h>
#include <stdio.h>
#include <stdarg.h>

/**
* 安全字符串结构
* 包含长度信息以防止溢出
*/
typedef struct {
char *data;
size_t length;
size_t capacity;
int is_secure; // 是否启用安全检查
} secure_string_t;

/**
* 创建安全字符串
* @param initial_capacity 初始容量
* @param enable_security 是否启用安全检查
* @return 安全字符串指针
*/
secure_string_t* secure_string_create(size_t initial_capacity, int enable_security) {
secure_string_t *str = calloc(1, sizeof(secure_string_t));
if (!str) return NULL;

str->data = malloc(initial_capacity + 1); // +1 for null terminator
if (!str->data) {
free(str);
return NULL;
}

str->data&#91;0] = '\0';
str->capacity = initial_capacity;
str->is_secure = enable_security;

return str;
}

/**
* 从C字符串创建安全字符串
* @param c_str C字符串
* @param enable_security 是否启用安全检查
* @return 安全字符串指针
*/
secure_string_t* secure_string_from_cstr(const char *c_str, int enable_security) {
if (!c_str) return NULL;

size_t len = strlen(c_str);
secure_string_t *str = secure_string_create(len, enable_security);
if (str) {
strncpy(str->data, c_str, str->capacity);
str->data&#91;str->capacity] = '\0';
str->length = strlen(str->data);
}

return str;
}

/**
* 安全字符串追加
* @param str 目标字符串
* @param append_str 要追加的字符串
* @return 0成功,-1失败
*/
int secure_string_append(secure_string_t *str, const char *append_str) {
if (!str || !append_str) return -1;

size_t append_len = strlen(append_str);
size_t new_length = str->length + append_len;

// 检查是否需要扩容
if (new_length >= str->capacity) {
if (str->is_secure) {
// 安全模式:拒绝超出容量的操作
return -1;
} else {
// 非安全模式:自动扩容
size_t new_capacity = (new_length + 1) * 2;
char *new_data = realloc(str->data, new_capacity + 1);
if (!new_data) return -1;

str->data = new_data;
str->capacity = new_capacity;
}
}

// 执行追加
strncat(str->data, append_str, str->capacity - str->length);
str->length = strlen(str->data);

return 0;
}

/**
* 安全格式化字符串
* @param str 目标字符串
* @param format 格式字符串
* @param ... 可变参数
* @return 写入的字符数,-1失败
*/
int secure_string_printf(secure_string_t *str, const char *format, ...) {
if (!str || !format) return -1;

va_list args;
va_start(args, format);

// 首先计算需要的空间
va_list args_copy;
va_copy(args_copy, args);
int needed = vsnprintf(NULL, 0, format, args_copy);
va_end(args_copy);

if (needed < 0) {
va_end(args);
return -1;
}

// 检查容量
if ((size_t)needed >= str->capacity - str->length) {
if (str->is_secure) {
va_end(args);
return -1; // 容量不足
} else {
// 自动扩容
size_t new_capacity = str->length + needed + 1;
char *new_data = realloc(str->data, new_capacity + 1);
if (!new_data) {
va_end(args);
return -1;
}

str->data = new_data;
str->capacity = new_capacity;
}
}

// 执行格式化
int written = vsnprintf(str->data + str->length,
str->capacity - str->length,
format, args);

if (written >= 0) {
str->length += written;
}

va_end(args);
return written;
}

/**
* 安全字符串比较
* @param str1 字符串1
* @param str2 字符串2
* @return 比较结果
*/
int secure_string_compare(const secure_string_t *str1, const secure_string_t *str2) {
if (!str1 && !str2) return 0;
if (!str1) return -1;
if (!str2) return 1;

return strcmp(str1->data, str2->data);
}

/**
* 获取C字符串
* @param str 安全字符串
* @return C字符串指针
*/
const char* secure_string_cstr(const secure_string_t *str) {
return str ? str->data : NULL;
}

/**
* 获取字符串长度
* @param str 安全字符串
* @return 字符串长度
*/
size_t secure_string_length(const secure_string_t *str) {
return str ? str->length : 0;
}

/**
* 清空字符串
* @param str 安全字符串
*/
void secure_string_clear(secure_string_t *str) {
if (str && str->data) {
str->data&#91;0] = '\0';
str->length = 0;
}
}

/**
* 销毁安全字符串
* @param str 安全字符串
*/
void secure_string_destroy(secure_string_t *str) {
if (!str) return;

if (str->data) {
// 安全清除内存
memset(str->data, 0, str->capacity);
free(str->data);
}

free(str);
}

/**
* 安全字符串池
* 管理多个安全字符串以提高性能
*/
typedef struct {
secure_string_t **strings;
size_t count;
size_t capacity;
pthread_mutex_t mutex;
} string_pool_t;

/**
* 创建字符串池
* @param initial_capacity 初始容量
* @return 字符串池指针
*/
string_pool_t* string_pool_create(size_t initial_capacity) {
string_pool_t *pool = calloc(1, sizeof(string_pool_t));
if (!pool) return NULL;

pool->strings = calloc(initial_capacity, sizeof(secure_string_t*));
if (!pool->strings) {
free(pool);
return NULL;
}

pool->capacity = initial_capacity;
pthread_mutex_init(&pool->mutex, NULL);

return pool;
}

综合演示函数

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
/**
* 事件驱动架构演示示例
*/
void demo_event_driven_architecture() {
printf("=== 事件驱动架构演示 ===\n");

// 创建事件循环
event_loop_t *loop = event_loop_create(100);
if (!loop) {
printf("Failed to create event loop\n");
return;
}

// 添加事件监听器
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);

// 启动事件循环线程
pthread_t loop_thread;
pthread_create(&loop_thread, NULL, (void*(*)(void*))event_loop_run, loop);

// 发布一些测试事件
for (int i = 0; i < 5; i++) {
event_t event = {0};
event.timestamp = time(NULL);

// 发布不同类型的事件
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);
}

// 清理事件数据
sleep(2);

// 停止并销毁事件循环
event_loop_stop(loop);
pthread_join(loop_thread, NULL);
event_loop_destroy(loop);

printf("=== 演示完成 ===\n\n");
}

/**
* 无锁队列演示示例
*/
void demo_lockfree_queue() {
printf("=== 无锁队列演示 ===\n");

// 初始化
queue = lockfree_queue_create();
atomic_init(&items_produced, 0);
atomic_init(&items_consumed, 0);

if (!queue) {
printf("Failed to create queue\n");
return;
}

// 创建生产者和消费者线程
pthread_t producers&#91;NUM_PRODUCERS];
pthread_t consumers&#91;NUM_CONSUMERS];
int producer_ids&#91;NUM_PRODUCERS];
int consumer_ids&#91;NUM_CONSUMERS];

// 启动生产者线程
for (int i = 0; i < NUM_PRODUCERS; i++) {
producer_ids&#91;i] = i;
pthread_create(&producers&#91;i], NULL, producer_thread, &producer_ids&#91;i]);
}

// 启动消费者线程
for (int i = 0; i < NUM_CONSUMERS; i++) {
consumer_ids&#91;i] = i;
pthread_create(&consumers&#91;i], NULL, consumer_thread, &consumer_ids&#91;i]);
}

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

for (int i = 0; i < NUM_CONSUMERS; i++) {
pthread_join(consumers&#91;i], NULL);
}

// 显示结果
printf("Total produced: %d\n", atomic_load(&items_produced));
printf("Total consumed: %d\n", atomic_load(&items_consumed));
printf("Queue size: %zu\n", lockfree_queue_size(queue));

// 清理
lockfree_queue_destroy(queue);

printf("=== 演示完成 ===\n\n");
}

/**
* 缓存友好数据结构演示示例
*/
void demo_cache_friendly_structures() {
printf("=== 缓存友好数据结构演示 ===\n");

// 测试不同规模的数据
size_t test_sizes&#91;] = {1000, 10000, 100000, 1000000};
int num_tests = sizeof(test_sizes) / sizeof(test_sizes&#91;0]);

printf("%-10s %-12s %-12s %-10s\n", "Elements", "SoA Time(s)", "AoS Time(s)", "Speedup");
printf("------------------------------------------------\n");

for (int i = 0; i < num_tests; i++) {
performance_result_t result = test_performance(test_sizes&#91;i]);

double speedup = result.aos_time / result.soa_time;
printf("%-10zu %-12.6f %-12.6f %-10.2fx\n",
result.elements_processed,
result.soa_time,
result.aos_time,
speedup);
}

printf("=== 演示完成 ===\n\n");
}

/**
* 安全字符串操作演示示例
*/
void demo_secure_strings() {
printf("=== 安全字符串操作演示 ===\n");

// 创建安全字符串(启用安全检查)
secure_string_t *str1 = secure_string_create(20, 1); // 安全模式
secure_string_t *str2 = secure_string_from_cstr("Hello", 1);

if (!str1 || !str2) {
printf("Failed to create secure strings\n");
return;
}

printf("Initial strings:\n");
printf("str1: '%s' (length: %zu)\n", secure_string_cstr(str1), secure_string_length(str1));
printf("str2: '%s' (length: %zu)\n", secure_string_cstr(str2), secure_string_length(str2));

// 安全追加
if (secure_string_append(str2, " World!") == 0) {
printf("After append: '%s'\n", secure_string_cstr(str2));
} else {
printf("Append failed (security check)\n");
}

// 安全格式化
if (secure_string_printf(str1, "Number: %d, String: %s", 42, "test") >= 0) {
printf("Formatted string: '%s'\n", secure_string_cstr(str1));
} else {
printf("Format failed (security check)\n");
}

// 尝试超出容量的操作(在安全模式下会失败)
printf("\nTesting security checks:\n");
if (secure_string_append(str1, "This is a very long string that exceeds capacity") == -1) {
printf("Security check prevented buffer overflow!\n");
}

// 字符串比较
secure_string_t *str3 = secure_string_from_cstr("Hello World!", 1);
printf("Comparison result: %d\n", secure_string_compare(str2, str3));

// 清理
secure_string_destroy(str1);
secure_string_destroy(str2);
secure_string_destroy(str3);

printf("=== 演示完成 ===\n\n");
}

// 综合演示函数
void run_all_demos() {
printf("C语言高级编程技巧演示\n");
printf("=====================\n\n");

// 运行所有演示
demo_event_driven_architecture();
demo_lockfree_queue();
demo_cache_friendly_structures();
demo_secure_strings();

printf("所有演示完成!\n");
}

附录:最佳实践总结

编码规范

命名约定:使用清晰的命名,避免缩写

注释风格:使用Doxygen风格注释

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

内存管理:遵循RAII原则

线程安全:明确标识线程安全函数

性能优化原则

先测量后优化:使用性能分析工具

算法优先:选择合适的数据结构和算法

避免过早优化:保持代码可读性

缓存友好:考虑数据局部性

编译器优化:合理使用编译器优化选项

安全编码原则

输入验证:永远不要信任外部输入

边界检查:防止缓冲区溢出

最小权限:使用最小必要权限

安全函数:使用安全的字符串函数

代码审查:定期进行安全代码审查

这份完整的C语言高级编程技巧指南涵盖了从基础宏定义到复杂并发编程的所有重要方面,提供了丰富的代码示例和最佳实践,帮助开发者编写高质量、高性能、安全的C代码。

C语言高级编程技巧, C语言最佳实践教程, C语言内存管理技巧, 函数指针与回调函数详解, C语言预处理指令使用方法, C语言高级编程指南, C语言宏定义优化技巧, C语言编程进阶教程, C语言高效编码技巧, C语言开发最佳实践

​​How Programmers Can Choose Side Hustles Smartly (Practical Guide)​(หัวข้อ:​​ ​​โปรแกรมเมอร์เลือกอาชีพเสริมอย่างไรให้มีประสิทธิภาพ​)

​​หัวข้อ:​​ ​​โปรแกรมเมอร์เลือกอาชีพเสริมอย่างไรให้มีประสิทธิภาพ​​เผยแพร่ 5 พฤษภาคม 2025 โดย Maiyaba Dad

​​(Thai Version)​

โปรแกรมเมอร์ที่ต้องการสร้างรายได้เสริม ควรเลือกโครงการที่มี ​​ต้นทุนส่วนเพิ่มต่ำ​​ (ต้นทุนแทบไม่เพิ่มเมื่อผู้ใช้เพิ่มขึ้น) เพื่อสร้าง “​​รายได้แบบพาสซีฟ​​” อย่างยั่งยืน ต่อไปนี้คือตัวอย่างที่ปฏิบัติได้จริง:

​​พัฒนา SaaS ขนาดเล็ก​​

  • จุดเด่น: พัฒนาหนึ่งครั้ง → สร้างการสมัครสมาชิกรายเดือน → ต้นทุนเซิร์ฟเวอร์ปรับตามผู้ใช้

ตัวอย่าง:

  • เครื่องมือตรวจสอบ SEO: สร้างรายงานอัตโนมัติจาก URL (Python + AWS Lambda)

  • เครื่องมือโพสต์โซเชียลมีเดีย: บันทึกเนื้อหาล่วงหน้า → โพสต์พร้อมกันหลายแพลตฟอร์ม (ใช้ Twitter/Facebook API)

  • เครื่องมือคำนวณภาษีสำหรับฟรีแลนซ์

ควบคุมต้นทุน: ใช้ Vercel/AWS Lambda

​​การขายคอร์สออนไลน์ & สินค้าความรู้​​

  • จุดเด่น: สร้างเนื้อหาครั้งเดียว → ขายได้ไม่จำกัด

ตัวอย่าง:

  • คอร์ส Udemy/คอร์ส MOOC ชื่อ “React ระดับสูง”

  • อีบุ๊ก PDF แนวปฏิบัติ TypeScript ผ่าน Gumroad

  • สมัครสมาชิกจดหมายข่าว Substack เช่น “Architecture Design Weekly”

เครื่องมือ: OBS (อัดวิดีโอ), Canva (สไลด์), Thinkific (เว็บขายคอร์ส)

​​การหารายได้จากโอเพนซอร์ส​​

  • กลยุทธ์: เวอร์ชันฟรี + เวอร์ชันพรีเมียม (มีฟีเจอร์พิเศษ)

ตัวอย่าง:

  • ขายปลั๊กอิน WordPress (เช่น ตารางข้อมูลระดับองค์กร)

  • รับเงินสนับสนุนผ่าน GitHub Sponsors

เคล็ดลับ: เลือกโซลูชันยอดนิยม (เช่น ตารางข้อมูล/ไลบรารีกราฟ)

​​บริการ API​​

  • โมเดลรายได้: คิดค่าบริการตามจำนวน Request

ตัวอย่าง:

  • API แปลงรูปเป็นข้อความ (ใช้ Tesseract, $0.001/ครั้ง)

  • API วิเคราะห์ความรู้สึกในข้อความ

  • API รวบรวมข้อมูลหุ้นแบบเรียลไทม์

ช่องทางขาย: APILayer, RapidAPI

​​ขายเทมเพลต & อีโพรดักต์​​

  • จุดเด่น: ไม่มีค่าจัดส่ง → ขายอัตโนมัติ

ตัวอย่าง:

  • เทมเพลตเว็บ Next.js ขายใน Envato Market

  • UI Kit ข้อมูลใน Figma

  • ปลั๊กอิน Chrome นับจำนวนโค้ด GitHub

(… ต่อด้วยรูปแบบเดียวกับต้นฉบับตามหัวข้อที่เหลือ …)

​​สูตรสำเร็จ:​​รายได้พาสซีฟ = ต้นทุนส่วนเพิ่มต่ำ + ขยายขนาดได้ + ส่งมอบแบบอัตโนมัติ

​​คำแนะนำสำหรับโปรแกรมเมอร์ไทย:​​

  • ใช้ Notion/Google Sheets เพื่อวางแผน

  • ชำระเงินผ่าน PromptPay → เชื่อมต่อระบบอัตโนมัติด้วย Zapier

  • ระวัง GDPR/PDPA ในโครงการ SaaS

​​印度英语版本 (Indian English)​​

​​Title:​​ ​​How Programmers Can Choose Side Hustles Smartly (Practical Guide)​​Posted on 5 May 2025 by Maiyaba Dad

For Indian programmers, side projects with ​​low marginal cost​​ (costs nearly flat when users scale) are key for creating ​​”hand-free income”​​. Here are field-tested methods:

​​Build Lightweight SaaS Tools​​

  • Why work? Single development → Recurring subscription → Auto-scaling servers

Desi Examples:

  • SEO Checker: Generate site report with Python + AWS Lambda

  • Social Poster: Schedule posts for Instagram/Facebook via APIs

  • GST Calculator for Freelancers

Cost Tip: Use JioCloud/AWS Lambda

​​Online Courses & Digital Products​​

  • Scale Factor: Create once → sell infinite times

Bharat-Friendly Cases:

  • Record “React Advanced” course → Sell on Udemy/Unacademy

  • eBook “TypeScript Pro Tips” via Gumroad/PayTM link

  • Tech Newsletter subscription via Substack

Tool Kit: OBS + Canva + Teachable

​​Monetize Open Source Projects​​

  • Model: Free Community Edition + Paid Enterprise Features

India Examples:

  • Premium React table library (₹10k/license)

  • Donations via UPI on GitHub Sponsors

  • Paid plugins for ERPNext

Mantra: Solve common dev pains (forms/charts)

​​API-as-a-Service​​

  • Pay-per-Use: Charge ₹0.10/API call

Desi Use Cases:

  • Aadhaar OCR API (Tesseract-based)

  • Hindi Sentiment Analysis API

  • Stock Market Data API

Hosting: Deploy on APILayer

​​Sell Digital Templates​​
Zero Delivery Cost:

  • Next.js E-commerce Boilerplate (₹2,499)

  • Figma Admin Dashboard UI Kit

  • Chrome Extension counting GitHub commits

Bharat Channels: Gumroad + Instamojo

(… remaining sections follow same localization pattern …)

​​Gaon Connection Formula:​​Hand-Free Income = Marginal Cost Kam + Scalable Model + Delivery Automation

​​India-Specific Tips:​​

  • Start with your core stack (eg. JavaScript devs → Chrome extensions)

  • Validate ideas on LinkedIn/WhatsApp dev groups

  • Use Razorpay/PayU for payment gateway

  • File GST as sole proprietor (₹1,500/year compliance cost)

​​Jugaad Pro Tip:​​Convert Telegram channel → paid ₹299/month community using Discord bots + UPI auto-collect!

https://www.calcguide.tech/2025/08/06/how-programmers-can-choose-side-hustles-smartly-practical-guide/

KEYWORDS:How Programmers Can Choose Side Hustles Smartly, Programmer Side Hustle Ideas, How to Start a Side Business as a Developer, Best Side Jobs for Programmers, Programmer Side Income Strategies, How to Maximize Earnings with a Side Hustle, Top Side Hustles for Tech Professionals, Programmer Side Hustle Guide 2024, How to Balance Work and Side Hustle, Effective Side Hustle Tips for Developers