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
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/syscall.h> #include <linux/kcmp.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <getopt.h> #include <pwd.h> #include <grp.h>
// kcmp 系统调用包装函数 int kcmp_wrapper(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2); }
// 资源类型名称 const char* get_resource_type_name(int type) { switch (type) { case KCMP_FILE: return "FILE"; case KCMP_VM: return "VM"; case KCMP_FILES: return "FILES"; case KCMP_FS: return "FS"; case KCMP_SIGHAND: return "SIGHAND"; case KCMP_IO: return "IO"; case KCMP_SYSVSEM: return "SYSVSEM"; default: return "UNKNOWN"; } }
// 显示详细的 kcmp 结果 void show_detailed_result(const char* description, int result, int show_details) { printf("%-25s: ", description); switch (result) { case 0: printf("相同 ✓"); if (show_details) printf(" (共享同一内核资源)"); break; case 1: printf("不同 ✗"); if (show_details) printf(" (使用不同内核资源)"); break; case 2: printf("无法访问"); if (show_details) printf(" (进程不存在或权限不足)"); break; default: printf("错误 (%d)", result); if (show_details) printf(" (%s)", strerror(errno)); break; } printf("\n"); }
// 分析两个进程的关系 void analyze_process_relationship(pid_t pid1, pid_t pid2) { printf("=== 进程关系分析 ===\n"); printf("进程1 PID: %d\n", pid1); printf("进程2 PID: %d\n\n", pid2); // 基本信息检查 if (pid1 == pid2) { printf("注意: 比较的是同一进程\n\n"); } // 逐个比较资源类型 int resource_types[] = { KCMP_FILES, KCMP_VM, KCMP_FS, KCMP_SIGHAND, KCMP_IO, KCMP_SYSVSEM }; int num_types = sizeof(resource_types) / sizeof(resource_types[0]); printf("资源共享分析:\n"); printf("%-25s %s\n", "资源类型", "状态"); printf("%-25s %s\n", "--------", "----"); int shared_count = 0; for (int i = 0; i < num_types; i++) { int result = kcmp_wrapper(pid1, pid2, resource_types[i], 0, 0); show_detailed_result(get_resource_type_name(resource_types[i]), result, 0); if (result == 0) { shared_count++; } } printf("\n共享资源统计: %d/%d 类型共享\n", shared_count, num_types); // 关系判断 printf("\n关系判断:\n"); if (shared_count == num_types) { printf("✓ 进程很可能有父子关系或克隆关系\n"); } else if (shared_count >= 3) { printf("○ 进程可能在相同环境中运行\n"); } else if (shared_count > 0) { printf("⚠ 进程部分资源共享\n"); } else { printf("✗ 进程完全独立\n"); } // 容器检测提示 if (shared_count < 3) { printf("\n容器环境检测:\n"); printf(" 如果进程在不同容器中,大多数资源应该显示为'不同'\n"); } }
// 比较特定文件描述符 void compare_file_descriptors(pid_t pid1, pid_t pid2, int fd1, int fd2) { printf("\n=== 文件描述符比较 ===\n"); int result = kcmp_wrapper(pid1, pid2, KCMP_FILE, fd1, fd2); printf("进程 %d fd%d vs 进程 %d fd%d: ", pid1, fd1, pid2, fd2); switch (result) { case 0: printf("指向同一文件 ✓\n"); break; case 1: printf("指向不同文件 ✗\n"); break; case 2: printf("无法访问进程\n"); break; default: printf("比较失败 (%s)\n", strerror(errno)); break; } }
// 显示帮助信息 void show_help(const char *program_name) { printf("用法: %s [选项]\n", program_name); printf("\n选项:\n"); printf(" -1, --pid1=PID 第一个进程 ID (默认: 当前进程)\n"); printf(" -2, --pid2=PID 第二个进程 ID (默认: 父进程)\n"); printf(" -f, --fd=FD1:FD2 比较特定文件描述符\n"); printf(" -t, --type=TYPE 比较特定资源类型\n"); printf(" -a, --all 显示详细分析\n"); printf(" -h, --help 显示此帮助信息\n"); printf("\n资源类型:\n"); printf(" file - 文件描述符\n"); printf(" vm - 虚拟内存\n"); printf(" files - 文件描述符表\n"); printf(" fs - 文件系统信息\n"); printf(" sighand - 信号处理\n"); printf(" io - I/O 信息\n"); printf(" sysvsem - System V 信号量\n"); printf("\n示例:\n"); printf(" %s -1 1234 -2 5678 # 比较进程 1234 和 5678\n"); printf(" %s -f 3:4 # 比较当前进程的 fd3 和 fd4\n"); printf(" %s -t vm # 比较虚拟内存\n"); }
int main(int argc, char *argv[]) { pid_t pid1 = 0; // 0 表示当前进程 pid_t pid2 = getppid(); // 默认比较当前进程和父进程 int fd1 = -1, fd2 = -1; int resource_type = -1; int show_all = 0; int specific_fd = 0; // 解析命令行参数 static struct option long_options[] = { {"pid1", required_argument, 0, '1'}, {"pid2", required_argument, 0, '2'}, {"fd", required_argument, 0, 'f'}, {"type", required_argument, 0, 't'}, {"all", no_argument, 0, 'a'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; int c; while (1) { int option_index = 0; c = getopt_long(argc, argv, "1:2:f:t:ah", long_options, &option_index); if (c == -1) break; switch (c) { case '1': pid1 = atoi(optarg); break; case '2': pid2 = atoi(optarg); break; case 'f': if (sscanf(optarg, "%d:%d", &fd1, &fd2) == 2) { specific_fd = 1; } else { fprintf(stderr, "错误: 文件描述符格式应为 FD1:FD2\n"); return 1; } break; case 't': if (strcmp(optarg, "file") == 0) resource_type = KCMP_FILE; else if (strcmp(optarg, "vm") == 0) resource_type = KCMP_VM; else if (strcmp(optarg, "files") == 0) resource_type = KCMP_FILES; else if (strcmp(optarg, "fs") == 0) resource_type = KCMP_FS; else if (strcmp(optarg, "sighand") == 0) resource_type = KCMP_SIGHAND; else if (strcmp(optarg, "io") == 0) resource_type = KCMP_IO; else if (strcmp(optarg, "sysvsem") == 0) resource_type = KCMP_SYSVSEM; else { fprintf(stderr, "错误: 未知的资源类型 '%s'\n", optarg); return 1; } break; case 'a': show_all = 1; break; case 'h': show_help(argv[0]); return 0; case '?': return 1; } } printf("=== kcmp 进程资源比较工具 ===\n\n"); // 如果指定了特定文件描述符比较 if (specific_fd) { compare_file_descriptors(pid1, pid2, fd1, fd2); return 0; } // 如果指定了特定资源类型 if (resource_type != -1) { int result = kcmp_wrapper(pid1, pid2, resource_type, 0, 0); printf("进程 %d 和 %d 的 %s 资源比较: ", pid1 ? pid1 : getpid(), pid2 ? pid2 : getppid(), get_resource_type_name(resource_type)); switch (result) { case 0: printf("相同\n"); break; case 1: printf("不同\n"); break; case 2: printf("无法访问\n"); break; default: printf("错误 (%s)\n", strerror(errno)); break; } return 0; } // 执行完整的进程关系分析 analyze_process_relationship(pid1 ? pid1 : getpid(), pid2 ? pid2 : getppid()); // 显示系统信息 printf("\n=== 系统信息 ===\n"); printf("当前进程: %d\n", getpid()); printf("父进程: %d\n", getppid()); printf("用户 ID: %d\n", getuid()); struct passwd *pwd = getpwuid(getuid()); if (pwd) { printf("用户名: %s\n", pwd->pw_name); } // 使用建议 printf("\n=== kcmp 使用建议 ===\n"); printf("典型应用场景:\n"); printf("1. 容器技术: 检测进程是否在同一容器中\n"); printf("2. 调试工具: 分析进程间资源共享情况\n"); printf("3. 安全审计: 验证进程隔离效果\n"); printf("4. 系统监控: 跟踪进程关系变化\n"); printf("\n注意事项:\n"); printf("1. 需要适当权限才能比较其他进程\n"); printf("2. 结果可能受 SELinux 等安全模块影响\n"); printf("3. 某些资源类型可能不适用于所有内核版本\n"); return 0; }
|