【问题标题】:Shared memory doesn't work for a struct but works for a char*共享内存不适用于结构,但适用于 char*
【发布时间】:2014-05-25 12:50:29
【问题描述】:

这个错误对我来说已经花费了太多时间,我根本无法找出问题所在。

使用我为 char * 创建的共享内存效果很好,但是当我尝试将它用于我创建的结构时却不行。

语言是 C。

目标

目标是创建一个将数据发送到第二个线程(由第一个线程创建)的线程。数据保存在一个名为 CircularQueue 的结构中,并且必须使用共享内存进行传递。

另外,第一个线程不能等待第二个线程,它可以在第二个线程收到作为参数发送的共享内存的名称时立即结束。

循环队列

就我个人而言,我认为问题不在这里,因为它运行良好(至少直到现在我已经开始使用共享内存)。

CircularQueue 是一种限制数据量的结构,它使用信号量和互斥体来做到这一点。如果队列已满,则信号量使生产者等待,如果队列为空,则消费者等待。它使用互斥体,因此一次只能写入 1 个。

问题

如果我将“CircularQueue *”更改为“char *”,将 sizeof(CircularQueue) 更改为 sizeof(char)*NUMBER_OF_CHARS 并在第一个线程上写入,我可以在第二个线程上读取它。 但是使用 CircularQueue 是不行的。

主文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/types.h>
#include "CircularQueue.h"

typedef struct {
    char *v;
    sem_t sem;
} sharedMemoryName;

void * readThread(void * arg)
{
    // This first 3 lines is to get the name of the shared memory from the argument
    sharedMemoryName shm_name = *(sharedMemoryName *)arg;
    sem_post(&((*(sharedMemoryName *)arg).sem));
    printf("shm_name_2:%s:\n",shm_name.v);

    // This 2 lines open the shared memory
    int shmfd = shm_open(shm_name.v,O_RDWR,0600);
    if (shmfd<0) { perror("READER failure in shm_open()"); exit(1); }

    // This 2 lines attach the region of the virtual memory to the pointer
    CircularQueue *q = (CircularQueue *) mmap(0,sizeof(CircularQueue),PROT_READ|PROT_WRITE,MAP_SHARED,shmfd,0); //attach this region to virtual memory
    if (q == MAP_FAILED) { perror("READER failure in mmap()"); exit(2); }

    queue_print(q);   // Prints all the data of the CircularQueue (For Debug purposes)

    // Closes the virtual memory
    if (munmap(q,sizeof(CircularQueue)) < 0) { perror("READER failure in munmap()"); exit(4); } //unmap shared memory region
    if (shm_unlink(shm_name.v) < 0) { perror("READER failure in shm_unlink()"); exit(6); }

    return NULL;
}

void * writeThread(void * arg)
{
    // This first 4 lines is just for the name of the shared memory
    sharedMemoryName shm_name;
    snprintf(shm_name.v, sizeof shm_name.v, "%s%d", "/shm", 100);
    printf("shm_name_1:%s:\n",shm_name.v);
    sem_init(&(shm_name.sem), 0, 0);

    // This 3 lines create the shared memory
    int shmfd = shm_open(shm_name.v,O_CREAT|O_RDWR,0600);
    if (shmfd<0) { perror("WRITER failure in shm_open()"); exit(1); }
    if (ftruncate(shmfd,sizeof(CircularQueue)) < 0) { perror("WRITER failure in ftruncate()"); exit(2); }

    // This 2 lines attach the region of the virtual memory to the pointer
    CircularQueue *q = (CircularQueue *) mmap(0,sizeof(CircularQueue),PROT_READ|PROT_WRITE,MAP_SHARED,shmfd,0); //attach this region to virtual memory
    if (q == MAP_FAILED) { perror("WRITER failure in mmap()"); exit(3); }

    queue_init(&q,4);   // Initializes the CircularQueue
    queue_put(q,1,2);   // Puts the value 1 on the CircularQueue
    queue_print(q);     // Prints all the data of the CircularQueue (For Debug purposes)

    // Creates the next thread
    pthread_t nt;
    pthread_create(&nt, NULL, readThread, &shm_name);

    // Waits for the second thread (Reader) to read the argument, so it can end
    sem_wait(&(shm_name.sem));
    return NULL;
}

int main() {
    setbuf(stdout, NULL); // This is to avoid buffering in printf, therefore it prints immediatly
    pthread_t nt;
    pthread_create(&nt, NULL, writeThread, NULL);
    pthread_exit(NULL);
}

循环队列文件

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
typedef unsigned long QueueElem;
typedef struct
{
    QueueElem *v;       // pointer to the queue buffer
    unsigned int capacity;  // queue capacity
    unsigned int first; // head of the queue
    unsigned int last;  // tail of the queue
    sem_t empty;        // semaphores and mutex for implementing the
    sem_t full;     // producer-consumer paradigm
    pthread_mutex_t mutex;
} CircularQueue;
void queue_init(CircularQueue **q, unsigned int capacity)
{
    *q = (CircularQueue *) malloc(sizeof(CircularQueue));
    sem_init(&((*q)->empty), 0, capacity);
    sem_init(&((*q)->full), 0, 0);
    pthread_mutex_init(&((*q)->mutex), NULL);
    (*q)->v = (QueueElem *) malloc(capacity * sizeof(QueueElem));
    (*q)->capacity = capacity;
    (*q)->first = 0;
    (*q)->last = -1;
}
void queue_print(CircularQueue *q)
{
    int fullValue;
    sem_getvalue(&(q->full),&fullValue);
    if (fullValue!=0) {
        int i, emptyValue;
        if (q->first <= q->last) {
            for (i=q->first;i <= q->last;i++)
                printf("|%d:%d",i,(int)q->v[i]);
        } else {
            for (i=q->first;i < q->capacity;i++)
                printf("|%d:%d",i,(int)q->v[i]);
            for (i=0;i <= q->last;i++)
                printf("|%d:%d",i,(int)q->v[i]);
        }

        sem_getvalue(&(q->empty),&emptyValue);
        if (emptyValue==0)
            printf(" [FULL]");
    }
    else {
        printf("[EMPTY]");
    }

    printf("\n");
}
void queue_put(CircularQueue *q, QueueElem value, int n)
{
    sem_wait(&(q->empty));
    pthread_mutex_lock(&(q->mutex));
    q->last++;
    if ((q->last) >= (q->capacity))
        q->last = 0;
    q->v[q->last] = value;
    sem_post(&(q->full));
        printf("[%d]",n); queue_print(q);   // FOR DEBUG
    pthread_mutex_unlock(&(q->mutex));
}
QueueElem queue_get(CircularQueue *q, int n)
{
    QueueElem elem;
    sem_wait(&(q->full));
    pthread_mutex_lock(&(q->mutex));
    elem = q->v[q->first];
    q->first++;
    if ((q->first) >= (q->capacity))
        q->first = 0;
    sem_post(&(q->empty));
        printf("[%d]",n); printf("(GET:%d)",(int)elem); // FOR DEBUG
        queue_print(q);                 // FOR DEBUG
    pthread_mutex_unlock(&(q->mutex));
    return elem;
}
void queue_destroy(CircularQueue *q)
{
    free(q->v);
    pthread_mutex_destroy(&(q->mutex));
    sem_destroy(&(q->full));
    sem_destroy(&(q->empty));
    free(q);
}

控制台输出(printf)

shm_name_1:/shm100:
[2]|0:1               // This is printed by the queue_put(), when the first thread puts "1" in the queue (The 2, which is the 3rd argument of the function, is just for debug, this way I can see who wrote)
|0:1                  // This is printed by the first thread using queue_print()
shm_name_2:/shm100:   // The shared memory name is successfully passed
[EMPTY]               // This is printed by the second thread using queue_print()

感谢您抽出宝贵时间。

【问题讨论】:

  • 您为什么要为此使用共享内存?线程都存在于同一个地址空间中,你只需要使用信号量来保证对数据线程的安全访问。
  • 根据手册:POSIX 共享内存对象实际上是一个句柄,不相关的进程可以使用该句柄来映射(2)共享内存的同一区域。 (即应该在进程之间使用共享内存,而不是线程)
  • Mgetz 和 Levente Kurusa,项目和这个确切的功能(传递 CircularQueue)需要共享内存。我居然在没有共享内存的情况下完成了项目,然后我看到了这个需求……

标签: c multithreading struct shared-memory


【解决方案1】:

queue_init()你正在做

*q = (CircularQueue *) malloc(sizeof(CircularQueue));

这会覆盖您在编写器线程中通过mmap() 检索到的共享内存地址。

所以q 指向新的malloced 内存而不是共享内存。

不要认为你应该 malloc() 排队。

另外,您应该为(*q)-&gt;v 使用共享内存,而不是分配为

 (*q)->v = (QueueElem *) malloc(capacity * sizeof(QueueElem));

如果你malloc()在多个进程中使用共享内存,(*q)-&gt;v在其他进程中无效,可能会崩溃。

【讨论】:

  • 我使用多线程,这是我为尝试解决问题而创建的简化版本。那我应该如何使用共享内存呢?如何定义 CircularQueue 数组的容量?
  • @Edu,您应该将capacity * sizeof(QueueElem) 添加到要创建的共享内存中,并为q-&gt;v 分配适当的地址来存储这些元素。正如其他人指出的那样,如果您使用线程,则不需要共享内存。
  • 我需要创建另一个共享内存还是可以使用相同的?我需要使用共享内存,因为项目和这个确切的功能(传递 CircularQueue)需要它。
猜你喜欢
  • 1970-01-01
  • 2015-05-22
  • 2012-07-31
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多