c - 访问另一个进程中的共享内存缓冲区

标签 c posix

我正在尝试使用互斥体和共享缓冲区解决生产者消费者问题,但在访问共享缓冲区结构中的值时遇到问题,特别是 char 数组。当我在一个终端中调用 Producer.c 文件并使用

打印值(输入是字母表的 txt 文件)时
printf("%c", newBuff->bytes[newBuff->rear]); 

字符确实显示正常,但是当我在 Consumer.c 中执行相同的操作时,但使用

printf("%c", newBuff->bytes[newBuff->front]);

值显示为空白。 newBuff->front 值为零,因此它应该打印字母 a。当我访问consumer.c中的结构中的其他值(例如front、count或rear)时,它们是正确的。共享内存创建和附件也可以正常工作,因此我认为问题是我没有在数组中正确存储 char 值,或者我试图错误地访问它们。在下面的代码中,我将 printf 放置在 Producer.c 的循环中,然后放置在 Consumer.c 的循环之外,因此我知道在消费者开始提取数据之前确实存在一个值。

消费者.c

typedef struct buffer{
    pthread_mutex_t lock;
    pthread_cond_t shout;
    int front;
    int rear;
    int count;
    int endOfFile;
    char bytes[1024];
} buffer;


int main(int argc, char const *argv[]) {
    int i=0;
    FILE *file = fopen(argv[1], "w");
    if (argc != 2){
        printf("You must enter in a file name\n");
    }
    int shmid, swapCount=0;
    char swapBytes[] = "";
    char path[] = "~";
    key_t key = ftok(path, 7);
    buffer* newBuff;
    if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
        newBuff = (buffer*) shmat(shmid, 0, 0);
        printf("successful creation\n");
        newBuff->front = 0;
        newBuff->count = 0;
        newBuff->endOfFile = 0;
        pthread_mutexattr_t attr;
        pthread_condattr_t condAttr;

        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&newBuff->lock, &attr);

        pthread_condattr_init(&condAttr);
        pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
        pthread_cond_init(&newBuff->shout, &condAttr);
    } //shared memory creation

    else if ((shmid = shmget(key, 0, 0)) != -1){
        printf("%d\n", shmid);
        printf("successful attachment\n" );
        newBuff = (buffer*) shmat(shmid, 0, 0);
        printf("%c\n", newBuff->count);
    }
    else{
        printf("oops\n");
        exit(0);
    }
    pthread_mutex_lock(&newBuff->lock);
    printf("%c\n", newBuff->bytes[newBuff->front]);
    while (newBuff->endOfFile != 1)
    {
        while (newBuff->count == 0){
            pthread_cond_signal(&newBuff->shout);
            pthread_cond_wait(&newBuff->shout, &newBuff->lock);
        }
        newBuff->front = ((newBuff->front + 1)%SIZE);
        newBuff->count--;
    }
    pthread_mutex_unlock(&newBuff->lock);
    shmdt(&newBuff);

    //pthread_mutexattr_destroy(&attr);
    //pthread_condattr_destroy(&condAttr);*/

    return 0;
}

Producer.c

typedef struct buffer{
    pthread_mutex_t lock;
    pthread_cond_t shout;
    int front;
    int rear;
    int count;
    int endOfFile;
    char bytes[1024];
} buffer;


int main(int argc, char const *argv[]) {
    FILE *file = fopen(argv[1], "r");
    if (argc != 2){
        printf("You must enter in a file dumbass\n");
    }
    int shmid;
    char path[] = "~";
    key_t key = ftok(path, 7);
    buffer* newBuff;
    printf("dfasdfasdf\n");
    if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
        newBuff = (buffer*) shmat(shmid, 0, 0);
        printf("successful creation\n");


        newBuff->front = 0;
        newBuff->count = 0;
        newBuff->endOfFile=0;
        pthread_mutexattr_t attr;
        pthread_condattr_t condAttr;

        pthread_mutexattr_init(&attr);
        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&newBuff->lock, &attr);

        pthread_condattr_init(&condAttr);
        pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
        pthread_cond_init(&newBuff->shout, &condAttr);
    } //shared memory creation

    else if ((shmid = shmget(key, 0, 0)) != -1){
        printf("successful attachment\n" );
        newBuff = (buffer*) shmat(shmid, 0, 0);
    }
    else{
        printf("oops\n");
        exit(0);
    }
    printf("%d\n", shmid);
    pthread_mutex_lock(&newBuff->lock);
    while (fscanf(file, "%c", &newBuff->bytes[newBuff->rear]) != EOF) //read in file
    {
        printf("%c\n", newBuff->bytes[newBuff->rear]);
        while (newBuff->count >= SIZE){ //buffer is full
            //("%c\n", newBuff->bytes[newBuff->rear]);
            pthread_cond_signal(&newBuff->shout);
            pthread_cond_wait(&newBuff->shout, &newBuff->lock);
        }
        //printf("%c\n", newBuff->bytes[newBuff->rear]);
        newBuff->rear = ((newBuff->front + 1)%SIZE);
        newBuff->count++;
    }
    newBuff->endOfFile = 1;
    pthread_cond_signal(&newBuff->shout);
    pthread_mutex_unlock(&newBuff->lock);

    shmdt(&newBuff);

    //pthread_mutexattr_destroy(&attr);
    //pthread_condattr_destroy(&condAttr);

    return 0;
}

最佳答案

您的代码存在一些困难,其中一些已在注释中解决:

  • ftok() 需要传递给它的路径来指定现有文件,但您传递的路径不需要。

  • 您请求的共享内存比实际需要的要少:仅是缓冲区内容的大小,而不是整个struct buffer的大小。因为实际分配的共享内存量将四舍五入为页面大小的倍数,所以这可能最终没问题,但您应该通过请求实际需要的量来确保它没问题.

  • System V 共享内存段具有内核持久性,因此一旦创建,它们将继续存在,直到它们被显式删除或系统重新启动。你永远不会删除你的。仅当您首次创建它时才初始化其内容。因此,除非您在运行之间手动删除它,否则您将在第二次和后续运行中使用旧数据(例如设置了文件结束指示符)。我建议让消费者安排移除时间。

  • 消费者仅从缓冲区打印一个字节的数据,并且在验证是否有任何内容要读取之前这样做。

  • 向缓冲区添加一个字节后,生产者不会更新可用字节计数,直到向消费者发出信号之后。充其量这是一种浪费,因为消费者直到下次(如果有)唤醒时才会看到计数的变化。

  • 生产者根据当前的front值错误地更新了缓冲区的rear索引,而不是基于当前值。因此,数据不会被写入缓冲区数组中的正确位置。

  • 一旦生产者设置了 endOfFile 标志,消费者就会忽略所有剩余未读字节,只保留一个字节。

  • 如果生产者在完成时将 count 保留为零,消费者将死锁。

我发现您的程序的修改版本成功地解决了所有这些问题,并通过共享内存准确地传输数据。

更新:

另外,

  • 消费者和/或生产者初始化互斥锁和条件变量的方式本身并不安全。无论哪个进程都可能在第一个进程完成初始化之前尝试第二次(或第三次,或...)访问这些对象。更一般地,一旦连接了共享内存段,写入它就不存在固有的内存屏障。为了解决这些问题,SysV 共享内存的天然伴侣就是 SysV 信号量。

关于c - 访问另一个进程中的共享内存缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48671885/

相关文章:

python - 蓝牙与 Python 和 Arduino (HC-06)

c - 指针定义中的双星

c - POSIX 系统清除未完全解锁的文件锁的顺序是什么?

c - 写(): Bad file descriptor

c - 头文件中找不到符号

c - 标准c库是否提供链表等数据结构?

c - 在c中执行时正则表达式不起作用

c - 如何从 NSURL 获取结构文件?

c - Solaris 中的 getopt 隐式声明?

C 私有(private)变量 Get 和 Set 方法