You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

共享内存传递char数组参数异常:execv传参错误原因及修复方案

问题原因分析

你的问题核心出在错误地将字符串指针(char*)直接赋值给单个char类型的共享内存元素,以及对共享内存传递字符串的逻辑理解有误:

  1. 指针赋值的致命错误:在子进程中,shmc[0] = argv[1]; 这行代码是把argv[1]这个指针(64位系统下是8字节的地址值)强行塞进1字节的char变量里,直接截断了指针的高位字节,只保留低8位数值,完全不是你想要的字符串内容。同理shmc[1] = argv[2];和shmc[2] = argv[3];也犯了同样的错误。
  2. 跨进程指针的无效性:就算你能完整存储指针,子进程的地址空间在fork()后是独立的(写时复制机制),子进程里的argv指针在父进程的地址空间中是无效的,父进程根本无法通过这个指针访问到子进程地址空间里的字符串。
  3. 参数传递的逻辑错误:父进程里的&shmp[1]是取共享内存中第2个char元素的地址,而这个元素里存的是argv[2]指针的低8位数值,当你把这个地址传给execv时,程序会把这个地址开始的内存当作字符串读取,自然会出现乱码或者不符合预期的结果(比如你看到的23和3)。
解决方法

正确的做法是将字符串的实际内容复制到共享内存中,而不是存储指针。具体实现步骤如下:

1. 计算共享内存所需大小

需要存储三个字符串,每个字符串都要包含终止符\0,所以总大小应该是三个字符串的长度之和加上3(每个字符串的终止符)。我们可以动态计算这个大小,避免浪费内存。

2. 修改子进程代码:复制字符串到共享内存

使用strcpy将argv[1]、argv[2]、argv[3]的内容依次复制到共享内存中,每个字符串以\0结尾,确保父进程能正确识别每个字符串的边界。

3. 修改父进程代码:从共享内存中提取字符串地址

父进程挂载共享内存后,依次找到每个字符串的起始地址(第一个字符串从shmp开始,第二个从第一个字符串的结束位置+1开始,以此类推),然后将这些地址传入execv。

修改后的完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    // 检查输入参数是否足够
    if (argc < 4) {
        fprintf(stderr, "Usage: %s str1 str2 str3\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int shmid;
    char *shm_addr;
    pid_t pid;

    // 计算需要的共享内存大小:三个字符串的长度+3个终止符
    size_t shm_size = strlen(argv[1]) + 1 + strlen(argv[2]) + 1 + strlen(argv[3]) + 1;
    shmid = shmget(IPC_PRIVATE, shm_size, 0666 | IPC_CREAT);
    if (shmid == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }

    pid = fork();
    if (pid == -1) {
        perror("fork failed");
        shmctl(shmid, IPC_RMID, NULL);
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程:挂载共享内存并复制字符串
        shm_addr = (char *)shmat(shmid, NULL, 0);
        if (shm_addr == (char *)-1) {
            perror("shmat failed in child");
            exit(EXIT_FAILURE);
        }

        // 依次复制三个字符串到共享内存
        char *current = shm_addr;
        strcpy(current, argv[1]);
        current += strlen(current) + 1;
        strcpy(current, argv[2]);
        current += strlen(current) + 1;
        strcpy(current, argv[3]);

        // 卸载共享内存
        if (shmdt(shm_addr) == -1) {
            perror("shmdt failed in child");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    } else {
        // 父进程:等待子进程完成,然后读取共享内存并调用execv
        wait(NULL);

        shm_addr = (char *)shmat(shmid, NULL, 0);
        if (shm_addr == (char *)-1) {
            perror("shmat failed in parent");
            shmctl(shmid, IPC_RMID, NULL);
            exit(EXIT_FAILURE);
        }

        // 提取三个字符串的地址
        char *str1 = shm_addr;
        char *str2 = str1 + strlen(str1) + 1;
        char *str3 = str2 + strlen(str2) + 1;

        // 准备execv的参数数组
        char *arg_vec[] = {"./test", str2, str3, NULL};
        execv("./test", arg_vec);

        // 如果execv执行到这里,说明出错了
        perror("execv failed");
        shmdt(shm_addr);
        shmctl(shmid, IPC_RMID, NULL);
        exit(EXIT_FAILURE);
    }
}

额外注意事项

  • 一定要添加错误检查:shmget、shmat、fork、execv等系统调用都可能失败,需要通过返回值判断并处理错误,避免隐藏问题。
  • 共享内存的清理:父进程中,execv成功后会替换当前进程的地址空间,所以shmdt和shmctl不会被执行,你可以考虑在./test程序中清理共享内存,或者使用信号、文件锁等机制来确保共享内存被正确释放。
  • 如果字符串长度不确定,也可以在共享内存的开头存储每个字符串的长度,这样父进程可以更方便地定位每个字符串的位置。

内容的提问来源于stack exchange,提问作者user13851309

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:57:46