linux线程间通信支持阻塞的消息队列实现

消息队列

消息队列实现并不难。消息队列主要有两种实现方式:数组实现和链表实现。
此处采用链表实现。由于实现并不能难,重写浪费时间,直接参考别人的就好了。

Linux下消息队列实现

Linux下的同个进程的线程通信好像没有RTOS下支持阻塞的消息队列。想要支持阻塞的消息队列似乎就要自己实现了。
实现可阻塞的消息队列需要用到pthread中的条件变量(pthread_cond_t )互斥锁(pthread_mutex_t )

数据结构

#define LIN_MSG_MAX_LEN    4096      /* max 4K */

/* msgqueue return state */
#define LIN_MSGQ_EOK     0          /**< There is no error */                
#define LIN_MSGQ_ERROR   1           /**< A generic error happens */
#define LIN_MSGQ_EINVAL  2           /**< Invalid argument */
#define LIN_MSGQ_ENOMEM  3            /**< No memory */
#define LIN_MSGQ_TIMEOUT 4            /**< Timeout> */

struct lin_msgq_block;
typedef struct lin_msgq_block lin_mq_blk_t;

/* message block */
struct lin_msgq_block {
    lin_mq_blk_t *next;  
	int len;
    char *data;
};
  
struct lin_msgqueue;
typedef struct lin_msgqueue lin_mq_t;

struct lin_msgqueue
{
    lin_mq_blk_t *first;
    lin_mq_blk_t *last;
    int     num;
 
    pthread_mutex_t mutex;
    pthread_cond_t  not_empty;
};

消息队列的API接口

lin_mq_t *lin_mq_create(void); 
int lin_mq_destroy(lin_mq_t *mq);
int lin_mq_send_msg(lin_mq_t *mq, char *msg, int msg_len);
int lin_mq_get_msg(lin_mq_t *mq, char *buffer, int size);
int lin_mq_get_msg_timeout(lin_mq_t *mq, char *buffer, int size, int timeout);
int lin_mq_get_msg_num(lin_mq_t *mq);
int lin_mq_clear(lin_mq_t *mq);

消息队列API接口实现

#define SLOG_USE_CUSTOM   1

#if SLOG_USE_CUSTOM == 1
#define _PRINT   printf
#else
#define _PRINT 
#endif
 
#define __FILENAME__ (strrchr(__FILE__, '\\') ? (strrchr(__FILE__, '\\') + 1) : __FILE__)

#define SLOG_PRINT(X, args...) _PRINT("[%s]-%d:" X, __FILENAME__, __LINE__, ##args)

static unsigned long long timeout_ns = 0;

static void lin_mq_free_msg_blk(lin_mq_blk_t *elm)
{
    if (elm != NULL)
    {
        if (elm->data != NULL)
        {
            free(elm->data);
            elm->data = NULL;
        }
        free(elm);
        elm = NULL;
    }
}

static void put_msg_to_queue(lin_mq_t *mq, lin_mq_blk_t *elm)
{
    if (NULL == mq || NULL == elm)
    {
        SLOG_PRINT("mq or elm is NULL");
        return;
    }

    if (NULL != elm->next)
    {
        elm->next = NULL;
    }

    pthread_mutex_lock(&mq->mutex);
    /* message queue have no message block */
    if (mq->first == mq->last && 0 == mq->num)
    {
        mq->first = elm;
        mq->last = elm;
        mq->num++;
        /* There is a new message block, Wake up a blocked thread */
        pthread_cond_signal(&mq->not_empty);
    }
    else
    {
        /* Insert the new message block at the end of the queue */
        mq->last->next = elm;
        mq->last = elm;
        mq->num++;
    }

    pthread_mutex_unlock(&mq->mutex);
}

static int lin_mq_get_msg_blk(lin_mq_t *mq, lin_mq_blk_t **blk)
{
    if (NULL == mq)
    {
        SLOG_PRINT("mq or elm is NULL");
        return -LIN_MSGQ_EINVAL;
    }

    pthread_mutex_lock(&mq->mutex);
    /* message queue have no message block */
    while (0 == mq->num)
    {
        /* blocking threads and wait message block */
        pthread_cond_wait(&mq->not_empty, &mq->mutex);
    }
    /* get new message block from message queue */
    *blk = (mq->first);
    if (1 == mq->num)
    {
        mq->first = mq->last = NULL;
        mq->num = 0;
    }
    else
    {
        mq->first = mq->first->next;
        mq->num--;
    }

    pthread_mutex_unlock(&mq->mutex);

    return LIN_MSGQ_EOK;
}

int lin_mq_get_msg(lin_mq_t *mq, char *buffer, int size)
{
    int ret;
    lin_mq_blk_t *blk;
    ret = lin_mq_get_msg_blk(mq, &blk);  
    if (ret != LIN_MSGQ_EOK) return ret;

    if (blk->data != NULL)
    {
        size = size > blk->len ? blk->len : size;
        memmove(buffer, blk->data, size);
        lin_mq_free_msg_blk(blk);
    }
    return LIN_MSGQ_EOK;
}

static int lin_mq_get_msg_blk_timeout(lin_mq_t *mq, lin_mq_blk_t **blk, int timeout)
{
    struct timespec timenow;

    if (NULL == mq)
    {
        SLOG_PRINT("mq or elm is NULL");
        return -LIN_MSGQ_EINVAL;
    }

    pthread_mutex_lock(&mq->mutex);
    /* There is no message block */
    if (0 == mq->num)
    {
        clock_gettime(CLOCK_MONOTONIC, &timenow);
        timenow.tv_sec = timenow.tv_sec + timeout / 1000; /* add sec */
        timeout %= 1000;                                  /* get msec */

        timeout_ns = timenow.tv_nsec + timeout * 1000 * 1000;  /* get nsec */
        /* If more than 1 s */
        if (timeout_ns >= 1000 * 1000 * 1000) 
        {
            timenow.tv_sec++;
            timenow.tv_nsec = timeout_ns - 1000 * 1000 * 1000;
        }
        else
            timenow.tv_nsec = timeout_ns;

        /* blocking threads and wait timeout */
        pthread_cond_timedwait(&mq->not_empty, &mq->mutex, &timenow);
    }

    if (mq->num <= 0)
    {   
        pthread_mutex_unlock(&mq->mutex);
        return -LIN_MSGQ_TIMEOUT;
    }

    /* get the message block from the message queue head */
    *blk = mq->first;
    if (1 == mq->num)
    {
        mq->first = mq->last = NULL;
        mq->num = 0;
    }
    else
    {
        mq->first = mq->first->next;
        mq->num--;
    }

    pthread_mutex_unlock(&mq->mutex);

    return LIN_MSGQ_EOK;
}

int lin_mq_get_msg_timeout(lin_mq_t *mq, char *buffer, int size, int timeout)
{
    int ret;
    lin_mq_blk_t *blk = NULL;

    ret = lin_mq_get_msg_blk_timeout(mq, &blk, timeout);  
    if (ret != LIN_MSGQ_EOK) return ret;

    if (blk->data == NULL)
    {
        SLOG_PRINT("message block data is NULL\r\n");
        return -LIN_MSGQ_ENOMEM;
    }

    size = size > blk->len ? blk->len : size;
    memmove(buffer, blk->data, size);
    lin_mq_free_msg_blk(blk);
    return LIN_MSGQ_EOK;
}

int lin_mq_clear(lin_mq_t *mq)
{
    lin_mq_blk_t *blk = NULL;
    lin_mq_blk_t *blk_tmp = NULL;

    if (NULL == mq)
    {
        SLOG_PRINT("mq is NULL");
        return -LIN_MSGQ_EINVAL;
    }

    if (mq->num <= 0)
    {
        SLOG_PRINT("mq->num is 0\r\n");
        return -LIN_MSGQ_ERROR;
    }

    pthread_mutex_lock(&mq->mutex);
    /* Clears all message nodes in the buffer before the current message node */
    blk = mq->first;
    while (blk != NULL)
    {
        if (blk == mq->last)
        {
            mq->first = mq->last;
            if (mq->num != 1)
            {
                mq->num = 1;
            }
            break;
        }

        blk_tmp = blk->next;
        lin_mq_free_msg_blk(blk);
        mq->num--;
        blk = blk_tmp;
        mq->first = blk;
    }

    pthread_mutex_unlock(&mq->mutex);

    return LIN_MSGQ_EOK;
}

int lin_mq_send_msg(lin_mq_t *mq, char *msg, int msg_len)
{
    lin_mq_blk_t *blk = NULL;

    if (msg == NULL) 
    {
        SLOG_PRINT("msg is NULL!!!");
        return -LIN_MSGQ_EINVAL;
    }

    blk = (lin_mq_blk_t *)malloc(sizeof(lin_mq_blk_t));
    if (NULL == blk)
    {
        SLOG_PRINT("new msg element failed!!");
        return -LIN_MSGQ_ENOMEM;
    }
    /* Limit maximum length */
    if (msg_len > LIN_MSG_MAX_LEN) 
    {
        msg_len = LIN_MSG_MAX_LEN;
        SLOG_PRINT("msg %d!", LIN_MSG_MAX_LEN);
    }

    memset(blk, 0, sizeof(lin_mq_blk_t));
    blk->len = msg_len;
    /* Allocate memory based on the sent size */
    blk->data = (char *)malloc(msg_len); 
    if (blk->data == NULL)
    {
        SLOG_PRINT("new element->data failed!!");
        lin_mq_free_msg_blk(blk);
        return -LIN_MSGQ_ENOMEM;
    }

    memmove(blk->data, msg, msg_len);

    blk->next = NULL;

    put_msg_to_queue(mq, blk);

    return LIN_MSGQ_EOK;
}

int lin_mq_destroy(lin_mq_t *mq)
{
    lin_mq_blk_t *blk = NULL;

    if (NULL == mq)
    {
        return (-LIN_MSGQ_EINVAL);
    }

    if (mq->first != mq->last && mq->num > 0)
    {
        blk = lin_mq_clear(mq);
    }
    else
    {
        blk = mq->last;
    }

    if (NULL != blk)
    {
        lin_mq_free_msg_blk(blk);
        mq->first = mq->last = NULL;
        mq->num = 0;
    }

    pthread_mutex_destroy(&mq->mutex);
    pthread_cond_destroy(&mq->not_empty);
    free(mq);

    mq = NULL;
    return (LIN_MSGQ_EOK);
}

int lin_mq_get_msg_num(lin_mq_t *mq)
{
    if (NULL == mq)
        return (-LIN_MSGQ_EINVAL);

    return mq->num;
}


lin_mq_t *lin_mq_create(void)
{
    lin_mq_t *mq = NULL;
    pthread_condattr_t cattr;

    mq = (lin_mq_t *)malloc(sizeof(lin_mq_t));
    if (NULL == mq)
    {
        SLOG_PRINT("init msg buffer failed!!");
        return NULL;
    }

    memset(mq, 0, sizeof(lin_mq_t));
    mq->first = NULL;
    mq->last = NULL;
    mq->num = 0;

    pthread_mutex_init(&(mq->mutex), NULL);
#if 1
    pthread_condattr_init(&cattr);
    pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC);
    pthread_cond_init(&(mq->not_empty), &cattr);
#else
    pthread_cond_init(&(mq->not_empty), NULL);
#endif

    return mq;
}

测试

代码:https://gitee.com/guangjieMVP/lin_msgqueue.git

安装xmake
sudo add-apt-repository ppa:xmake-io/xmake
sudo apt update
sudo apt install xmake

查看是否安装成功:

xmake --version
编译
xmake
执行
./build/linux/x86_64/debug/linqueue_test
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲盖弥彰1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值