线程间的消息队列

线程间的消息队列
该消息队列区别于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);
    } 
} 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Linux 中实现线程消息队列,我们可以使用 POSIX 消息队列(POSIX message queues)和 SysV 队列(System V Message Queue)。 1. POSIX 消息队列 POSIX 消息队列是 POSIX 线程提供的一个进程通信的方式。一个消息队列包含一个标识符、一个最大队列长度和一个消息队列的缓存区。它类似于管道和消息队列,但它与进程无关。 POSIX 消息队列提供了两个函数 mq_open() 和 mq_send() 来创建队列和发送消息。mq_open() 函数返回类型为 mqd_t(消息队列描述符),它是一个整数,用于后续操作中参考队列。mq_send() 函数用于向队列中发送消息,它需要传递指定的描述符以便发送消息。 以下是 POSIX 消息队列的实现示例: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <mqueue.h> #define QUEUE_NAME "/my_message_queue" #define QUEUE_PERMS ((mode_t) 0600) #define MAX_MSG_SIZE 2048 #define MAX_MSG_NUM 10 void die(char *s){ perror(s); exit(EXIT_FAILURE); } int main(int argc, char *argv[]){ mqd_t qd; struct mq_attr attr; attr.mq_flags = 0; //设置队列标志为 0 attr.mq_maxmsg = MAX_MSG_NUM; //设置队列中允许最大的消息数 attr.mq_msgsize = MAX_MSG_SIZE; //设置队列中允许最大的消息大小 /*打开或创建一个新的消息队列*/ qd = mq_open(QUEUE_NAME, O_RDWR | O_CREAT, QUEUE_PERMS, &attr); if(qd == -1) die("mq_open"); char *msg = "Hello, world."; size_t msg_len = strlen(msg); unsigned int priority = 1; /*将消息发送到队列中*/ if(mq_send(qd, msg, msg_len, priority) == -1) die("mq_send"); mq_close(qd); //关闭消息队列 return 0; } ``` 2. SysV 队列 SysV 队列是 System V IPC 提供的一种线程通信方式。它提供了 msgget()、msgctl()、msgrcv() 这三个函数来创建队列、控制队列和接收消息。 以下是 SysV 消息队列的实现示例: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define QUEUE_PERMS 0600 #define BUF_SIZE 256 struct message{ long mtype; char mtext[BUF_SIZE]; }; int main(int argc, char *argv[]){ key_t key; int qid; struct message msg; /*创建共享目录*/ if((key = ftok(".", 'a')) == -1){ perror("ftok"); exit(1); } /*创建消息队列*/ if((qid = msgget(key, QUEUE_PERMS | IPC_CREAT)) == -1){ perror("msgget"); exit(1); } /*向消息队列中发送消息*/ msg.mtype = 1; strncpy(msg.mtext, "Hello, world.", BUF_SIZE-1); if(msgsnd(qid, &msg, strlen(msg.mtext), IPC_NOWAIT) == -1){ perror("msgsnd"); exit(1); } /*释放消息队列*/ if(msgctl(qid, IPC_RMID, NULL) == -1){ perror("msgctl"); exit(1); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值