欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux中消息队列kfifo和信号量sem_t的用法

程序员文章站 2022-07-14 16:30:16
...

linux中消息队列kfifo和信号量sem_t的用法

使用kfifo和sem_t配合来实现消息队列:由sem来管理目前可以发送和接收的总的消息数,由kfifo来存储消息。具体实现起来就是定义信号量sem_t_send和sem_t_recv,sem_t_send设为max_num,sem_t_recv设为0。

消息发送前先判断sem_t_send是否为0,不为0就把sem_t_send减1,,然后将消息加入kfifo队列,同时将sem_t_recv加1代表队列有1条可接收的消息。

消息接收前先判断sem_t_recv是否为0,如果不为0,则sem_t_recv减1,然后将消息从kfifo队列中取出来,同时将sem_t_send加1代表发送队列又多了一条可以发送的消息。

这里涉及了典型的设计模式:生产者消费者模式,代码结构见下文

linux中消息队列kfifo和信号量sem_t的用法

首先我们可以定义队列结构体用来管理消息队列:

typedef struct _ST_SCLR_OS_QUEU
{
    struct kfifo stFifo;//存储消息
    struct semaphore stSemSend;//管理send消息
    struct semaphore stSemRecv;//管理recv消息
    spinlock_t stSpinLock;//消息自旋锁
    u16 u16MsgSize;
    u16 u16MaxNum;
    bool b8Valid;
} ST_SCLR_OS_QUEUE;

//创建队列

void *OS_SHL_QueueCreate( u16 u16MsgSize, u16 u16MaxNum, const c8 *pc8Name )
{
    ST_SCLR_OS_QUEUE *pstQ;
    //1 创建pstQ
    pstQ = (ST_SCLR_OS_QUEUE*)vk_kmalloc(sizeof(ST_SCLR_OS_QUEUE),GFP_KERNEL);
    //2 初始化fifo
    iret = kfifo_alloc(&( pstQ->stFifo ), u16MsgSize*u16MaxNum, GFP_KERNEL);
    //3 初始化sem和lock
    //semSend设置MaxNum即目前可发送的消息总数为MaxNum
    vk_sema_init( &( pstQ->stSemSend ), ( int )u16MaxNum );
    //semRecv设置为0即目前可接受的消息总数为0
    vk_sema_init( &( pstQ->stSemRecv ), 0 );
    vk_spin_lock_init( &( pstQ->stSpinLock ) );
}

//发送消息

bool OS_SHL_QueueSend(void *pQueue, void *pMsg, u16 u16MsgSize, u32 u32TimeoutInMS )
{
    pstQ = ( ST_SCLR_OS_QUEUE * )pQueue
    //1 获取semSend看看是否可以发送消息
    if ( u32TimeoutInMS == 0 )//立即获取,没有就返回失败
    {
        iret = vk_down_trylock( &( pstQ->stSemSend ) );
    }
    else if ( u32TimeoutInMS != OS_SHL_SEMAPHORE_TIMEOUT_NEVER )//等待几秒
    {
        iret = vk_down_timeout( &(pstQ->stSemSend), vk_msecs_to_jiffies(u32TimeoutInMS) );
    }
    else//阻塞线程直到获取到
    {
        iret = vk_down_interruptible( &(pstQ->stSemSend) );
    }
    //2 将消息放进队列
    iret = kfifo_in( &(pstQ->stFifo), pMsg, u16MsgSize, &(pstQ->stSpinLock) );
    //3 给semRecv加1 代表队列现在可以接受的消息数多了一个
    vk_up( &(pstQ->stSemRecv) );
}

//接收消息

bool OS_SHL_QueueRecv (void *pQueue, void *pMsg, u16 u16MsgSize, u32 u32TimeoutInMS )
{
    pstQ = ( ST_SCLR_OS_QUEUE * )pQueue
    //1 获取semRecv看看是否可以接收消息
    if ( u32TimeoutInMS == 0 )//立即获取,没有就返回失败
    {
        iret = vk_down_trylock( &(pstQ->stSemRecv) );
    }
    else if ( u32TimeoutInMS != OS_SHL_SEMAPHORE_TIMEOUT_NEVER ) //等待几秒
    {
        iret=vk_down_timeout( &(pstQ->stSemRecv),vk_msecs_to_jiffies(u32TimeoutInMS) );
    }
    else//阻塞线程直到获取到
    {
        iret = vk_down_interruptible( &(pstQ->stSemRecv) );
    }
    //2 将消息取出消息队列
    iret = kfifo_out ( &(pstQ->stFifo), pMsg, u16MsgSize, &(pstQ->stSpinLock) );
    //3 给semSent加1 代表队列现在可以发送给的消息数多了一个
    vk_up( &(pstQ->stSemSend) );
}