1. 程式人生 > >linux中訊息佇列kfifo和訊號量sem_t的用法

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代表傳送佇列又多了一條可以傳送的訊息。

這裡涉及了典型的設計模式:生產者消費者模式,程式碼結構見下文

這裡寫圖片描述

首先我們可以定義佇列結構體用來管理訊息佇列:

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) );
}