linux內核環形緩沖區【轉】


轉自:https://blog.csdn.net/eydwyz/article/details/56671023

循環緩沖區在一些競爭問題上提供了一種免鎖的機制,免鎖的前提是,生產者和消費
 都只有一個的情況下,否則也要加鎖。下面就內核中提取出來,而經過修改后的fifo進
 行簡要的分析。
 
 先看其只要數據結構:
 struct my_fifo {
 unsignedchar *buffer;/* the buffer holding the data*/
 unsignedint size;/* the size of the allocated buffer*/
unsignedint in;/* data is added at offset (in % size)*/
 unsignedint out;/* data is extracted from off. (out % size)*/

}

也不用多說,一看就明白。size, in, out 都設成無符號型的,因為都不存在負值的情型。

 /*
 form kernel/kfifo.c
 */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <fifo.h>
 
 #define min(a,b) ((a) < (b) ? (a):(b))
 /*

my_fifo_init

 */

 struct my_fifo *my_fifo_init(unsignedchar *buffer,unsigned int size)
 {
 struct my_fifo *fifo;
 
 fifo = malloc(sizeof(struct my_fifo));
 if (!fifo)
 returnNULL;

 fifo->buffer = buffer;
 fifo->size = size;
 fifo->in = fifo->out = 0;

 return fifo;
 }
這個初始化fifo結構的函數一般也不會在應用層里進行調用,而是被下面的fifo_alloc
 調用。依我的觀點來看,這兩個函數合成一個函數會更加的清晰,但是這一情況只針對

buffer是系統開辟的空間,如果buffer的空間是由其它的函數來提供,就只能用上面的這個函數。

 

/*
 my_fifo_alloc
*/
 struct my_fifo *my_fifo_alloc(unsignedint size)
 {
 unsignedchar *buffer;
 struct my_fifo *ret;
 /*
 * round up to the next power of 2, since our 'let the indices
 * wrap' tachnique works only in this case.
 */
 
 buffer = malloc(size);
 if (!buffer)
 returnNULL;
 
 ret = my_fifo_init(buffer, size);
 
 if (ret ==NULL)
 free(buffer);
 
 return ret;
 }
 /*
 * my_fifo_free
 */
 void my_fifo_free(struct my_fifo *fifo)
 {
 free(fifo->buffer);
 free(fifo);
 }
 
 這兩個函數也不作過多的分析,都很清晰。


 /*
 my_fifo_put()
 */
 unsignedint my_fifo_put(struct my_fifo *fifo,
 unsignedchar *buffer, unsigned int len)
 {
 unsignedint l;
 
 len = min(len, fifo->size - fifo->in + fifo->out);/*可能是緩沖區的空閑長度或者要寫長度*/
 
 /* first put the data starting from fifo->in to buffer end*/
 l = min(len, fifo->size - (fifo->in & (fifo->size -1)));
 memcpy(fifo->buffer + (fifo->in & (fifo->size -1)), buffer, l);
 
 /* then put the rest (if any) at the beginning of the buffer*/
 memcpy(fifo->buffer, buffer + l, len - l);
 
 fifo->in += len;
 
 return len;
 }

 
 /*
 my_fifo_get
 */
 unsignedint my_fifo_get(struct my_fifo *fifo,
 unsignedchar *buffer, unsigned int len)
 {
 unsignedint l;
 
 len = min(len, fifo->in - fifo->out); /*可讀數據*/
 
 /* first get the data from fifo->out until the end of the buffer*/
 l = min(len, fifo->size - (fifo->out & (fifo->size -1)));
 memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size -1)), l);
 
 /* then get the rest (if any) from the beginning of the buffer*/
 memcpy(buffer + l, fifo->buffer, len - l);
 
 fifo->out += len;
 
 return len;
 }
 這兩個讀寫結構才是循環緩沖區的重點。在fifo結構中,size是緩沖區的大小,是由用
 戶自己定義的,但是在這個設計當中要求它的大小必須是2的冪次。
 當in==out時,表明緩沖區為空的,當(in-out)==size 時,說明緩沖區已滿。
 
 我們看下具體實現,在86行處如果size-in+out ==0,也即獲得的len值會0,而沒有數
 據寫入到緩沖區中。所以在設計緩沖區的大小的時候要恰當,讀出的速度要比定入的速
 度要快,否則緩沖區滿了會使數據丟失,可以通過成功寫入的反回值來做判斷嘗試再次
 寫入.
 另一種情況則是緩沖區有足夠的空間給要寫入的數據,但是試想一下,如果空閑的空間
 在緩沖的首尾兩次,這又是如何實現呢?這部分代碼實現得非常巧妙。
 我們看fifo->in &(fifo->size-1) 這個表達式是什么意思呢?我們知道size是2的冪次
 項,那么它減1即表示其值的二進制所有位都為1,與in相與的最終結果是in%size,比
 size要小,所以看in及out的值都是不斷地增加,但再相與操作后,它們即是以size為
 周期的一個循環。89行就是比較要寫入的數據應該是多少,如果緩沖區后面的還有足夠
 的空間可寫,那么把全部的值寫到后面,否則寫滿后面,再寫到前面去93行。
 讀數據也可以作類似的分析,108行表示請求的數據要比緩沖區的數據要大時,只
 讀取緩沖區中可用的數據。
 
 staticinline void my_fifo_reset(struct my_fifo *fifo)
 {
 fifo->in = fifo->out = 0;
 }
 
 staticinline unsigned int my_fifo_len(struct my_fifo *fifo)

 return fifo->in - fifo->out;
 }
 在頭文件里還有緩沖區置位及返回緩沖區中數據大小兩個函數,很簡單,不必解釋


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM