有限系统调用下子进程向父进程传递大数值的实现方案
问题:子进程向父进程传递超出0-255范围的计算结果
我正在实现名为double.c的计算程序,仅允许使用fork、exec*系列、str*系列、ato*系列、printf/sprintf、round这些系统调用/函数。程序运行示例如下:
./double square 3 output:36 as square(double(3))
目前我使用exit(result)让子进程向父进程返回计算结果,但exit返回值范围仅为0-255,结果超过该范围时会出错(例如执行./double square 8时,结果256会被截断为0,导致返回值错误)。由于限制无法使用pipe、munmap等方法,请问如何在仅用指定函数的前提下传递更大数值?
我尝试的代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <math.h> #include <sys/wait.h> #include <stdlib.h> #define EPSILON 0.001 double simple_abs(double x) { return x > 0 ? x : -x; } double simple_sqrt(double x) { double previous = 0; double guess = x; while (simple_abs(guess - previous) > EPSILON) { previous = guess; guess = previous - (previous * previous - x) / (2 * previous); } return guess; } int lop(int status) { return (status >> 8) & 0xff; } int square(int n) { printf("number = %d and square = %d\n", n , n*n); return n * n; } int root(int n) { return (int) simple_sqrt((double) n); } int doubleVal(int n) { return n * 2; } int main(int argc, char *argv[]) { int value = atoi(argv[argc - 1]); int result = value; if (argc > 2){ result = doubleVal(result); for (int i = 1; i < argc - 1; i++) { int pid = fork(); if (pid == 0) { if (*argv[i] == 's' && *(argv[i]+1) == 'q' && *(argv[i]+2) == 'u' && *(argv[i]+3) == 'a' && *(argv[i]+4) == 'r' && *(argv[i]+5) == 'e' && *(argv[i]+6) == '\0'){ result = square(result); } else if (argv[i][0] == 'r' && argv[i][1] == 'o' && argv[i][2] == 'o' && argv[i][3] == 't' && argv[i][4] == '\0') { result = root(result); } else if (argv[i][0] == 'd' && argv[i][1] == 'o' && argv[i][2] == 'u' && argv[i][3] == 'b' && argv[i][4] == 'l' && argv[i][5] == 'e' && argv[i][6] == '\0') { result = doubleVal(result); } else { printf("Unknown operation: %s\n", argv[i]); exit(1); } printf("Child result: %d\n", result); //return result; exit(result); } else { int status; wait(&status); //result = (status >> 8) & 0xff; result = lop(status);#lop is working as WEXITSTATUS system api } } } else { result = doubleVal(result); } printf("Result: %d\n", result); return result; }
解决方案:通过
exec*传递结果作为命令行参数 放弃用exit传递数值的方案,转而利用exec*系列函数将计算结果以命令行参数的形式传递给新的进程实例,完全规避exit返回值的0-255限制。具体逻辑如下:
- 子进程完成当前计算后,将结果格式化为字符串,构造包含剩余操作和当前结果的新命令行参数数组。
- 子进程通过
execvp调用当前程序的副本,传入新的命令行参数,让新进程继续处理剩余操作。 - 父进程只需等待子进程执行完成,无需获取
exit状态,后续计算由exec后的进程接管,最终由最后一个进程输出结果。
修改后的代码示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <math.h> #include <string.h> #define EPSILON 0.001 double simple_abs(double x) { return x > 0 ? x : -x; } double simple_sqrt(double x) { double previous = 0; double guess = x; while (simple_abs(guess - previous) > EPSILON) { previous = guess; guess = previous - (previous * previous - x) / (2 * previous); } return guess; } int square(int n) { return n * n; } int root(int n) { return (int) simple_sqrt((double) n); } int doubleVal(int n) { return n * 2; } // 辅助函数:拼接操作链字符串,用于最终输出 void build_chain(char *dest, const char *op, const char *prev_chain) { if (prev_chain == NULL) { sprintf(dest, "%s(double(%%d))", op); } else { // 替换prev_chain中的%d为当前操作包裹的形式 char temp[256]; strcpy(temp, prev_chain); char *pos = strstr(temp, "%d"); if (pos != NULL) { *pos = '\0'; sprintf(dest, "%s%s(%%d)%s", temp, op, pos + 2); } else { sprintf(dest, "%s(%s)", op, prev_chain); } } } int main(int argc, char *argv[]) { // 检查是否有操作链参数(用于跟踪操作流程) char *chain = NULL; int value_idx = argc - 1; if (argc > 2 && strcmp(argv[1], "--chain") == 0) { chain = argv[2]; value_idx = argc - 1; argc -= 2; argv += 2; } // 无剩余操作,输出最终结果 if (argc == 2) { int value = atoi(argv[1]); int final_result = doubleVal(value); if (chain != NULL) { printf("output:%d as ", final_result); printf(chain, value); printf("\n"); } else { printf("output:%d as double(%d)\n", final_result, value); } return 0; } int value = atoi(argv[value_idx]); int current_result; char *current_op = argv[1]; // 先执行初始double操作,再处理当前操作 int doubled_val = doubleVal(value); if (strcmp(current_op, "square") == 0) { current_result = square(doubled_val); } else if (strcmp(current_op, "root") == 0) { current_result = root(doubled_val); } else if (strcmp(current_op, "double") == 0) { current_result = doubleVal(doubled_val); } else { printf("Unknown operation: %s\n", current_op); return 1; } // 构造新的操作链 char new_chain[256]; build_chain(new_chain, current_op, chain); // 构造新的命令行参数数组 int new_argc = argc + 1; // 增加--chain参数 char **new_argv = malloc(sizeof(char*) * (new_argc + 1)); if (!new_argv) { perror("malloc failed"); return 1; } new_argv[0] = argv[0]; new_argv[1] = "--chain"; new_argv[2] = new_chain; // 复制剩余操作 for (int i = 3; i < new_argc - 1; i++) { new_argv[i] = argv[i - 1]; } // 转换当前结果为字符串 char result_str[20]; sprintf(result_str, "%d", current_result); new_argv[new_argc - 1] = result_str; new_argv[new_argc] = NULL; pid_t pid = fork(); if (pid == 0) { // 子进程执行新的命令 execvp(new_argv[0], new_argv); // exec失败时的错误处理 perror("execvp failed"); exit(1); } else { // 父进程等待子进程完成 int status; wait(&status); free(new_argv); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { return 1; } return 0; } }
运行示例
./double square 3 output:36 as square(double(3)) ./double square 8 output:256 as square(double(8))
内容的提问来源于stack exchange,提问作者timp bill
相关产品推荐
相关产品推荐

