【发布时间】: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