使用 C 和 evecvp 执行程序参数无法正确转发

Executing a program arguments with C and evecvp failing to forward correctly

我正在尝试在监督者和客户端分布式系统中使用 execvp 函数执行程序。客户端发送一个要执行的程序,参数为:

char buf[500];
int bytes_recieved = 0;
char array[1][26];
bytes_recieved = recv(clientfd, buf, 5000, 0);
buf[bytes_recieved] = '[=10=]';

char buf1[50];
int bytes_recieved1 = 0;
char *array1[4];

for (int i = 0; i < 3; i++){
   bytes_recieved1 = recv(clientfd, buf1, 50, 0);
   array1[i] = buf1;
   printf("%s = buffer\n", buf1);
} 
buf1[bytes_recieved] = '[=10=]';

if(bytes_recieved != -1){
   printTime();
   fprintf(stdout,"atempting to execute program: %s\n", buf);
   if(execvp(buf, array1) == -1) {
      return 1;
   }
}

我一直在试图弄清楚当我在程序中打印出参数数组时会发生什么,最后一个参数对所有参数都是相同的?比如我运行这个在客户端程序中要执行的:

./client 12345 home/user/test_program 1 2 3

简单 printf 的结果是:

3
3
3

当我在监督者中手动分配数组中的每个参数时:

array1[0] = "1";
array1[1] = "2";
array1[2] = "3";

并将其发送到正确打印的执行程序。

我还测试了从文件描述符接收到的缓冲区是否正确分配了数组中的变量:

 printf("%s = buffer\n", array1[i]);

在循环赋值中,returns:

    1 = buffer
    2 = buffer
    3 = buffer 

我做错了什么? 如果您需要更多信息,请告诉我。

这是一些骨架代码,基于您的代码片段。我无法测试它——你没有提供 MCVE (Minimal, Complete, Verifiable Example — 或 MRE 或 SO 现在使用的任何名称) 或一个 SSCCE (Short, Self-Contained, Correct Example).

char buf[500];
int bytes_received = recv(clientfd, buf, sizeof(buf)-1, 0);
if (bytes_received < 0)
    return 1;
buf[bytes_received] = '[=10=]';

char *array1[5] = { buf };

for (int i = 0; i < 3; i++)
{
    char buf1[50];
    int bytes_received1 = recv(clientfd, buf1, sizeof(buf1)-1, 0);
    if (bytes_received1 < 0)
        return 1;
    buf1[bytes_received1] = '[=10=]';
    array1[i + 1] = strdup(buf1);
    printf("argument = [%s]\n", buf1);
}

printTime();
printf("atempting to execute program: %s\n", buf);
for (int i = 0; array1[i] != NULL; i++)
    printf("argv[%d] = [%s]\n", i, array1[i]);
fflush(0);
execvp(array1[0], array1);
fprintf(stderr, "failed to execute '%s'\n", array1[0]);
return 1;

多项更改包括:

  • 使用 sizeof 确定数组大小。
  • 正在更改“receive”的拼写
  • 减 1 以允许 space 为终端空字节将消息转换为字符串。
  • 使 array1 足够大以容纳终端 NULL 指针并将第 0 个之后的元素初始化(间接)为 NULL。这个很重要; execvp() 的参数数组必须以 NULL 指针结尾。
  • Return 如果初始接收失败,以避免使用负数进行索引。
  • 使 buf1 数组成为循环的局部;同上 bytes_received1.
  • Return 如果后续接收失败,以避免使用负数进行索引。
  • 复制使用 strdup() 读取的字符串 — 这是一个关键变化。
  • 不尝试 null-terminate buf1 基于 buf 中收到的数据的位置。
  • 修改打印,在字符串周围放置方括号,以便更容易发现尾随 space 或换行符。
  • 正在打印 execvp() 的所有参数。
  • 正在讨论调试输出是否应该转到 stderr 而不是 stdout。我最终将它留在 stdout,但这不一定是最佳选择。
  • 将一个 fprintf(stdout, …) 更改为 printf(…) 以保持一致性。
  • 正在调用 fflush(0) 以将任何待处理的输出发送到其设备。随着调试输出转到 stdout,如果输出通过管道传输到另一个程序,数据将被完全缓冲,而不是行缓冲,并且除非您强制显示,否则不会出现。调用 fflush(stdout) 也是一种选择。当您调用 execvp().[=77 时,您可能不应该(不)打开 stdinstdoutstderr 以外的任何文件流=]
  • 你应该考虑在到达这里之前是否应该关闭其他流(文件描述符),也许使用 O_CLOEXECFC_CLOEXEC 选项来确保文件描述符在成功执行时关闭,所以执行的进程不会获得它不知道的活动文件描述符。
  • 懒得检查 execvp() 的 return 值。如果是returns,则失败;如果成功,则不会 return.
  • execvp()执行失败报错
  • 在失败的 execvp() 之后留下 return 1; 作为代码的一部分。使用 exit(EXIT_FAILURE); 或类似的(也许 _exit(EXIT_FAILURE) )通常会更好。如果没有调用该函数片段的函数的更大上下文,就不可能知道这里最好的是什么。
  • 请注意,如果 execvp() 失败并且 returns,而不是退出,您正在泄漏由 strdup() 分配的内存。在 return 1;.
  • 之前应该有一个循环 for (int i = 0; i < 3; i++) free(array1[i+1]); 来释放复制的内存
  • 代码不检查是否没有数据截断 — 它不知道 recv() 调用之一是否会读取比它读取的数据更多的数据,因为没有足够的 space 存储所有数据。您可能想要检查实际数据大小是否小于可用的 space 以确保没有截断。
  • 不清楚为什么程序名可以比参数大十倍。一般来说,参数可以比程序名称大,但是,因为您的示例数据具有 123 等参数,所以这不是问题。