1、前言
最近項目中用到一個環形緩沖區(ring buffer),代碼是由linux核心的kfifo改過來的。緩沖區在檔案系統中經常用到,通過緩沖區緩解cpu讀寫記憶體和讀寫磁盤的速度。例如一個程序a産生資料發給另外一個程序b,程序b需要對程序a傳的資料進行處理并寫入檔案,如果b沒有處理完,則a要延遲發送。為了保證程序a減少等待時間,可以在a和b之間采用一個緩沖區,a每次将資料存放在緩沖區中,b每次沖緩沖區中取。這是典型的生産者和消費者模型,緩沖區中資料滿足fifo特性,是以可以采用隊列進行實作。linux核心的kfifo正好是一個環形隊列,可以用來當作環形緩沖區。生産者與消費者使用緩沖區如下圖所示:
2、linux 核心kfifo
kfifo設計的非常巧妙,代碼很精簡,對于入隊和出對處理的出人意料。首先看一下kfifo的資料結構:
kfifo提供的方法有:
定義自旋鎖的目的為了防止多程序/線程并發使用kfifo。因為in和out在每次get和out時,發生改變。初始化和建立kfifo的源代碼如下:
在kfifo_init和kfifo_calloc中,kfifo->size的值總是在調用者傳進來的size參數的基礎上向2的幂擴充,這是核心一貫的做法。這樣的好處不言而喻--對kfifo->size取模運算可以轉化為與運算,如:kfifo->in % kfifo->size 可以轉化為 kfifo->in & (kfifo->size – 1)
kfifo的巧妙之處在于in和out定義為無符号類型,在put和get時,in和out都是增加,當達到最大值時,産生溢出,使得從0開始,進行循環使用。put和get代碼如下所示:
put和get在調用__put和__get過程都進行加鎖,防止并發。從代碼中可以看出put和get都調用兩次memcpy,這針對的是邊界條件。例如下圖:藍色表示空閑,紅色表示占用。
(1)空的kfifo,
(2)put一個buffer後
(3)get一個buffer後
(4)當此時put的buffer長度超出in到末尾長度時,則将剩下的移到頭部去
3、測試程式
仿照kfifo編寫一個ring_buffer,現有線程互斥量進行并發控制。設計的ring_buffer如下所示:
采用多線程模拟生産者和消費者編寫測試程式,如下所示:
測試結果如下所示: