线程间的消息队列
该消息队列区别于linux posix标准的消息队列。该应用程序适合于线程间通信,可传递任何消息类型,借鉴了内核的kfifo消息队列,完全运行在用户区。
使用简单,方便。
该代码于git_hub上找到。
https://github.com/LnxPrgr3/message_queue
示例代码:
message_queue.c
/*
* Copyright (c) 2012 Jeremy Pepper
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of message_queue nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "message_queue.h"
#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
union padding {
char chardata;
short shortdata;
int intdata;
long longdata;
float floatdata;
double doubledata;
void *pointerdata;
};
static inline int pad_size(int size) {
return size % sizeof(union padding) ?
(size + (sizeof(union padding) - (size % sizeof(union padding)))) :
size;
}
static inline uint32_t round_to_pow2(uint32_t x) {
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x++;
return x;
}
static inline int max(int x, int y) {
return x > y ? x : y;
}
int message_queue_init(struct message_queue *queue, int message_size, int max_depth) {
int i;
char sem_name[128];
queue->message_size = pad_size(message_size);
queue->max_depth = round_to_pow2(max_depth);
queue->memory = malloc(queue->message_size * queue->max_depth);
if(!queue->memory)
goto error;
queue->freelist = malloc(sizeof(void *) * queue->max_depth);
if(!queue->freelist)
goto error_after_memory;
for(i=0;i<queue->max_depth;++i) {
queue->freelist[i] = queue->memory + (queue->message_size * i);
}
snprintf(sem_name, 128, "%d_%p", getpid(), &queue->allocator);
sem_name[127] = '\0';
queue->allocator.sem = &queue->allocator.unnamed_sem;
while (-1 == sem_init(&(queue->allocator.unnamed_sem), 0, 0)) {
if (errno == EINTR) {
continue;
} else {
do {
queue->allocator.sem = sem_open(sem_name, O_CREAT | O_EXCL, 0600, 0);
} while(queue->allocator.sem == SEM_FAILED && errno == EINTR);
if(queue->allocator.sem == SEM_FAILED)
goto error_after_freelist;
sem_unlink(sem_name);
break;
}
}
queue->allocator.blocked_readers = 0;
queue->allocator.free_blocks = queue->max_depth;
queue->allocator.allocpos = 0;
queue->allocator.freepos = 0;
queue->queue_data = malloc(sizeof(void *) * queue->max_depth);
if(!queue->queue_data)
goto error_after_alloc_sem;
for(i=0;i<queue->max_depth;++i) {
queue->queue_data[i] = NULL;
}
queue->queue.blocked_readers = 0;
snprintf(sem_name, 128, "%d_%p", getpid(), queue);
sem_name[127] = '\0';
queue->queue.sem = &queue->queue.unnamed_sem;
while (-1 == sem_init(&(queue->queue.unnamed_sem), 0, 0)) {
if (errno == EINTR) {
continue;
} else {
do {
queue->queue.sem = sem_open(sem_name, O_CREAT | O_EXCL, 0600, 0);
} while(queue->queue.sem == SEM_FAILED && errno == EINTR);
if(queue->queue.sem == SEM_FAILED)
goto error_after_queue;
sem_unlink(sem_name);
break;
}
}
queue->queue.entries = 0;
queue->queue.readpos = 0;
queue->queue.writepos = 0;
return 0;
error_after_queue:
free(queue->queue_data);
error_after_alloc_sem:
if(queue->allocator.sem == &queue->allocator.unnamed_sem) {
sem_destroy(queue->allocator.sem);
} else {
sem_close(queue->allocator.sem);
}
error_after_freelist:
free(queue->freelist);
error_after_memory:
free(queue->memory);
error:
return -1;
}
void *message_queue_message_alloc(struct message_queue *queue) {
if(__sync_fetch_and_add(&queue->allocator.free_blocks, -1) > 0) {
unsigned int pos = __sync_fetch_and_add(&queue->allocator.allocpos, 1) % queue->max_depth;
void *rv = queue->freelist[pos];
while(!rv) {
usleep(10); __sync_synchronize();
rv = queue->freelist[pos];
}
queue->freelist[pos] = NULL;
return rv;
}
__sync_fetch_and_add(&queue->allocator.free_blocks, 1);
return NULL;
}
void *message_queue_message_alloc_blocking(struct message_queue *queue) {
void *rv = message_queue_message_alloc(queue);
while(!rv) {
__sync_fetch_and_add(&queue->allocator.blocked_readers, 1);
rv = message_queue_message_alloc(queue);
if(rv) {
__sync_fetch_and_add(&queue->allocator.blocked_readers, -1);
return rv;
}
while(sem_wait(queue->allocator.sem) && errno == EINTR);
rv = message_queue_message_alloc(queue);
}
return rv;
}
void message_queue_message_free(struct message_queue *queue, void *message) {
unsigned int pos = __sync_fetch_and_add(&queue->allocator.freepos, 1) % queue->max_depth;
void *cur = queue->freelist[pos];
while(cur) {
usleep(10); __sync_synchronize();
cur = queue->freelist[pos];
}
queue->freelist[pos] = message;
__sync_fetch_and_add(&queue->allocator.free_blocks, 1);
if(queue->allocator.blocked_readers) {
__sync_fetch_and_add(&queue->allocator.blocked_readers, -1);
sem_post(queue->allocator.sem);
}
}
void message_queue_write(struct message_queue *queue, void *message) {
unsigned int pos = __sync_fetch_and_add(&queue->queue.writepos, 1) % queue->max_depth;
void *cur = queue->queue_data[pos];
while(cur) {
usleep(10); __sync_synchronize();
cur = queue->queue_data[pos];
}
queue->queue_data[pos] = message;
__sync_fetch_and_add(&queue->queue.entries, 1);
if(queue->queue.blocked_readers) {
__sync_fetch_and_add(&queue->queue.blocked_readers, -1);
sem_post(queue->queue.sem);
}
}
void *message_queue_tryread(struct message_queue *queue) {
if(__sync_fetch_and_add(&queue->queue.entries, -1) > 0) {
unsigned int pos = __sync_fetch_and_add(&queue->queue.readpos, 1) % queue->max_depth;
void *rv = queue->queue_data[pos];
while(!rv) {
usleep(10); __sync_synchronize();
rv = queue->queue_data[pos];
}
queue->queue_data[pos] = NULL;
return rv;
}
__sync_fetch_and_add(&queue->queue.entries, 1);
return NULL;
}
void *message_queue_read(struct message_queue *queue) {
void *rv = message_queue_tryread(queue);
while(!rv) {
__sync_fetch_and_add(&queue->queue.blocked_readers, 1);
rv = message_queue_tryread(queue);
if(rv) {
__sync_fetch_and_add(&queue->queue.blocked_readers, -1);
return rv;
}
while(sem_wait(queue->queue.sem) && errno == EINTR);
rv = message_queue_tryread(queue);
}
return rv;
}
void message_queue_destroy(struct message_queue *queue) {
if(queue->queue.sem == &queue->queue.unnamed_sem) {
sem_destroy(queue->queue.sem);
} else {
sem_close(queue->queue.sem);
}
free(queue->queue_data);
if(queue->allocator.sem == &queue->queue.unnamed_sem) {
sem_destroy(queue->allocator.sem);
} else {
sem_close(queue->allocator.sem);
}
free(queue->freelist);
free(queue->memory);
}
message_queue.h
/*
* Copyright (c) 2012 Jeremy Pepper
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of message_queue nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MESSAGE_QUEUE_H
#define MESSAGE_QUEUE_H
#ifndef CACHE_LINE_SIZE
#define CACHE_LINE_SIZE 64
#endif
#include <semaphore.h>
/**
* \brief Message queue structure
*
* This structure is passed to all message_queue API calls
*/
struct message_queue {
unsigned int message_size;
unsigned int max_depth;
void *memory;
void **freelist;
void **queue_data;
struct {
sem_t unnamed_sem;
sem_t *sem;
unsigned int blocked_readers;
int free_blocks;
unsigned int allocpos __attribute__((aligned(CACHE_LINE_SIZE)));
unsigned int freepos __attribute__((aligned(CACHE_LINE_SIZE)));
} allocator __attribute__((aligned(CACHE_LINE_SIZE)));
struct {
sem_t unnamed_sem;
sem_t *sem;
unsigned int blocked_readers;
int entries;
unsigned int readpos __attribute__((aligned(CACHE_LINE_SIZE)));
unsigned int writepos __attribute__((aligned(CACHE_LINE_SIZE)));
} queue __attribute__((aligned(CACHE_LINE_SIZE)));
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Initialize a message queue structure
*
* This function must be called before any other message_queue API calls on a
* message queue structure.
*
* \param queue pointer to the message queue structure to initialize
* \param message_size size in bytes of the largest message that will be sent
* on this queue
* \param max_depth the maximum number of message to allow in the queue at
* once. This will be rounded to the next highest power of two.
*
* \return 0 if successful, or nonzero if an error occured
*/
int message_queue_init(struct message_queue *queue, int message_size, int max_depth);
/**
* \brief Allocate a new message
*
* This allocates message_size bytes to be used with this queue. Messages
* passed to the queue MUST be allocated with this function or with
* message_queue_message_alloc_blocking.
*
* \param queue pointer to the message queue to which the message will be
* written
* \return pointer to the allocated message, or NULL if no memory is available
*/
void *message_queue_message_alloc(struct message_queue *queue);
/**
* \brief Allocate a new message
*
* This allocates message_size bytes to be used with this queue. Messages
* passed to the queue MUST be allocated with this function or with
* message_queue_message_alloc. This function blocks until memory is
* available.
*
* \param queue pointer to the message queue to which the message will be
* written
* \return pointer to the allocated message
*/
void *message_queue_message_alloc_blocking(struct message_queue *queue);
/**
* \brief Free a message
*
* This returns the message to the queue's freelist to be reused to satisfy
* future allocations. This function MUST be used to free messages--they
* cannot be passed to free().
*
* \param queue pointer to the message queue from which the message was
* allocated
* \param message pointer to the message to be freed
*/
void message_queue_message_free(struct message_queue *queue, void *message);
/**
* \brief Write a message to the queue
*
* Messages must have been allocated from the same queue by
* message_queue_message_alloc to be passed to this function.
*
* \param queue pointer to the queue to which to write
* \param message pointer to the message to write to the queue
*/
void message_queue_write(struct message_queue *queue, void *message);
/**
* \brief Read a message from the queue if one is available
*
* \param queue pointer to the queue from which to read
* \return pointer to the next message on the queue, or NULL if no messages
* are available.
*/
void *message_queue_tryread(struct message_queue *queue);
/**
* \brief Read a message from the queue
*
* This reads a message from the queue, blocking if necessary until one is
* available.
*
* \param queue pointer to the queue from which to read
* \return pointer to the next message on the queue
*/
void *message_queue_read(struct message_queue *queue);
/**
* \brief Destroy a message queue structure
*
* This frees any resources associated with the message queue.
*
* \param queue pointer to the message queue to destroy
*/
void message_queue_destroy(struct message_queue *queue);
#ifdef __cplusplus
}
#endif
#endif
测试代码:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/select.h>
#include <pthread.h>
#include <signal.h>
#include "message_queue.h"
typedef struct {
unsigned char message_type;
unsigned char buf[128];
} out_message_info, * p_out_message_info;
static struct message_queue my_queue;
void* thread1(void*);
void* thread2(void*);
/*
* when program is started, a single thread is created, called the initial thread or main thread.
* Additional threads are created by pthread_create.
* So we just need to create two thread in main().
*/
int main(int argc, char** argv)
{
printf("enter main\n");
pthread_t tid1, tid2;
int rc1=0, rc2=0;
/* The biggest message we'll send
* with this queue is 512 bytes, and
* the queue can only be 128
* messages deep */
message_queue_init(&my_queue, sizeof(out_message_info), 128);
rc1 = pthread_create(&tid1, NULL, thread1, NULL);
if(rc1 != 0)
printf("pthread2 create error! \n");
rc2 = pthread_create(&tid2, NULL, thread2, NULL);
if(rc2 != 0)
printf("pthread1 create error! \n");
while(1);
/*删除队列*/
// message_queue_destroy(&my_queue);
}
/*
* thread1() will be execute by thread1, after pthread_create()
* it will set g_Flag = 1;
*/
void* thread1(void* arg)
{
printf("enter thread1\n");
p_out_message_info p_write_message;
unsigned char i = 0;
while (1) {
p_write_message = message_queue_message_alloc_blocking(&my_queue);
printf("write_pthread: free_blocks=%d, allocpos=%d, freepos=%d\n", my_queue.allocator.free_blocks, my_queue.allocator.allocpos, my_queue.allocator.freepos);
p_write_message->buf[0] = i++;
p_write_message->buf[1] = i++;
printf("write_pthread: buf[0] = %d, buf[1] = %d\n", p_write_message->buf[0], p_write_message->buf[1]);
/* Construct the message here */
message_queue_write(&my_queue, p_write_message);
printf("write_pthread: entries=%d, writepos=%d\n\n", my_queue.queue.entries, my_queue.queue.writepos);
usleep(100000);
}
}
/*
* thread2() will be execute by thread2, after pthread_create()
* it will set g_Flag = 2;
*/
void* thread2(void* arg)
{
printf("enter thread2\n");
p_out_message_info p_read_message;
while (1) {
/* Blocks until a message is available */
p_read_message = message_queue_read(&my_queue);
printf("read_pthread: entries=%d, readpos=%d\n", my_queue.queue.entries, my_queue.queue.readpos);
printf("read_pthread: buf[0] = %d, buf[1] = %d\n\n", p_read_message->buf[0], p_read_message->buf[1]);
message_queue_message_free(&my_queue, p_read_message);
sleep(1);
}
}