linux中訊息佇列kfifo和訊號量sem_t的用法
阿新 • • 發佈:2019-01-27
使用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) );
}