linux內核中的循環緩沖區

????? Linux內核中的循環緩沖區(circular buffer)為解決某些特殊情況下的競爭問題提供了一種免鎖的方法。這種特殊的情況就是當生產者和消費者都只有一個,而在其它情況下使用它也是必須要加鎖的。

循環緩沖區定義在include/linux/kfifo.h中,如下:

struct kfifo {
unsigned char *buffer; // buffer指向存放數據的緩沖區
unsigned int size;??????? // size是緩沖區的大小
unsigned int in;?????????? // in是寫指針下標
unsigned int out;??????? // out是讀指針下標
spinlock_t *lock;???????? // lock是加到struct kfifo上的自旋鎖
};?
????? (上面說的免鎖不是免這里的鎖,這個鎖是必須的),防止多個進程并發訪問此數據結構。當in==out時,說明緩沖區為空;當(in-out)==size時,說明緩沖區已滿。
?????? 為kfifo提供的接口可以分為兩類:
?????? 一類是滿足上述情況下使用的,以雙下劃線開頭,沒有加鎖的;
?????? 另一類是在不滿足的條件下,即需要額外加鎖的情況下使用的。
?????? 其實后一類只是在前一類的基礎上進行加鎖后的包裝(也有一處進行了小的改進),實現中所加的鎖是spin_lock_irqsave。

清空緩沖區的函數:
static inline void __kfifo_reset(struct kfifo *fifo);
static inline void kfifo_reset(struct kfifo *fifo);
這很簡單,直接把讀寫指針都置為0即可。

向緩沖區里放入數據的接口是:
static inline unsigned int kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
unsigned int __kfifo_put(struct kfifo *fifo, unsigned char *buffer, unsigned int len);

后者是在kernel/kfifo.c中定義的。這個接口是經過精心構造的,可以小心地避免一些邊界情況。我們有必要一起來看一下它的具體實現。

 1: /**
 2: * __kfifo_put - puts some data into the FIFO, no locking version
 3: * @fifo: the fifo to be used.
 4: * @buffer: the data to be added.
 5: * @len: the length of the data to be added.
 6: *
 7: * This function copies at most @len bytes from the @buffer into
 8: * the FIFO depending on the free space, and returns the number of
 9: * bytes copied.
 10: *
 11: * Note that with only one concurrent reader and one concurrent
 12: * writer, you don't need extra locking to use these functions.
 13: */
 14: unsigned int __kfifo_put(struct kfifo *fifo,
 15:  const unsigned char *buffer, unsigned int len)
 16: {
 17: unsigned int l;
 18: ?
 19: len = min(len, fifo->size - fifo->in + fifo->out);
 20: ?
 21:  /*
 22: * Ensure that we sample the fifo->out index -before- we
 23: * start putting bytes into the kfifo.
 24: */
 25: ?
 26: smp_mb();
 27: ?
 28:  /* first put the data starting from fifo->in to buffer end */
 29: l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
 30: memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
 31: ?
 32:  /* then put the rest (if any) at the beginning of the buffer */
 33: memcpy(fifo->buffer, buffer + l, len - l);
 34: ?
 35:  /*
 36: * Ensure that we add the bytes to the kfifo -before-
 37: * we update the fifo->in index.
 38: */
 39: ?
 40: smp_wmb();
 41: ?
 42: fifo->in += len;
 43: ?
 44:  return len;
 45: }
 46: EXPORT_SYMBOL(__kfifo_put);
 1: /**
 2: * kfifo_put - puts some data into the FIFO
 3: * @fifo: the fifo to be used.
 4: * @buffer: the data to be added.
 5: * @len: the length of the data to be added.
 6: *
 7: * This function copies at most @len bytes from the @buffer into
 8: * the FIFO depending on the free space, and returns the number of
 9: * bytes copied.
 10: */
 11: static inline unsigned int kfifo_put(struct kfifo *fifo,
 12:  const unsigned char *buffer, unsigned int len)
 13: {
 14: unsigned long      
 15: unsigned int ret;
 16: ?
 17: spin_lock_irqsave(fifo->lock, flags);
 18: ?
 19: ret = __kfifo_put(fifo, buffer, len);
 20: ?
 21: spin_unlock_irqrestore(fifo->lock, flags);
 22: ?
 23:  return ret;
 24: }


len = min(len, fifo->size - fifo->in + fifo->out);
????? 在 len (fifo->size - fifo->in + fifo->out) 之間取一個較小的值賦給len。注意,當 (fifo->in == fifo->out+fifo->size) 時,表示緩沖區已滿,此時得到的較小值一定是0,后面實際寫入的字節數也全為0。
????? 另一種邊界情況是當 len 很大時(因為len是無符號的,負數對它來說也是一個很大的正數),這一句也能保證len取到一個較小的值,因為????fifo->in 總是大于等于 fifo->out ,所以后面的那個表達式 l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 的值不會超過fifo->size的大小。
????? smp_mb();? smp_wmb(); 是加內存屏障,這里不是我們討論的范圍,你可以忽略它。
?? ?? l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));??? 是把上一步決定的要寫入的字節數len “切開”,這里又使用了一個技巧。注意:實際分配給 fifo->buffer 的字節數 fifo->size,必須是2的冪,否則這里就會出錯。既然 fifo->size 是2的冪,那么 (fifo->size-1) 也就是一個后面幾位全為1的數,也就能保證(fifo->in & (fifo->size - 1)) 總為不超過 (fifo->size - 1) 的那一部分,和 (fifo->in)% (fifo->size - 1) 的效果一樣。
????? 這樣后面的代碼就不難理解了,它先向? fifo->in? 到緩沖區末端這一塊寫數據,如果還沒寫完,在從緩沖區頭開始寫入剩下的,從而實現了循環緩沖。最后,把寫指針后移 len 個字節,并返回len。
?????? 從上面可以看出,fifo->in的值可以從0變化到超過fifo->size的數值,fifo->out也如此,但它們的差不會超過fifo->size。

從kfifo向外讀數據的函數是:
static inline unsigned int kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
unsigned int __kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);

 1: ?
 2: /**
 3: * __kfifo_get - gets some data from the FIFO, no locking version
 4: * @fifo: the fifo to be used.
 5: * @buffer: where the data must be copied.
 6: * @len: the size of the destination buffer.
 7: *
 8: * This function copies at most @len bytes from the FIFO into the
 9: * @buffer and returns the number of copied bytes.
 10: *
 11: * Note that with only one concurrent reader and one concurrent
 12: * writer, you don't need extra locking to use these functions.
 13: */
 14: unsigned int __kfifo_get(struct kfifo *fifo,
 15: unsigned char *buffer, unsigned int len)
 16: {
 17: unsigned int l;
 18: ?
 19: len = min(len, fifo->in - fifo->out);
 20: ?
 21:  /*
 22: * Ensure that we sample the fifo->in index -before- we
 23: * start removing bytes from the kfifo.
 24: */
 25: ?
 26: smp_rmb();
 27: ?
 28:  /* first get the data from fifo->out until the end of the buffer */
 29: l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
 30: memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
 31: ?
 32:  /* then get the rest (if any) from the beginning of the buffer */
 33: memcpy(buffer + l, fifo->buffer, len - l);
 34: ?
 35:  /*
 36: * Ensure that we remove the bytes from the kfifo -before-
 37: * we update the fifo->out index.
 38: */
 39: ?
 40: smp_mb();
 41: ?
 42: fifo->out += len;
 43: ?
 44:  return len;
 45: }
 46: EXPORT_SYMBOL(__kfifo_get);
 1: ?
 2: /**
 3: * kfifo_get - gets some data from the FIFO
 4: * @fifo: the fifo to be used.
 5: * @buffer: where the data must be copied.
 6: * @len: the size of the destination buffer.
 7: *
 8: * This function copies at most @len bytes from the FIFO into the
 9: * @buffer and returns the number of copied bytes.
 10: */
 11: static inline unsigned int kfifo_get(struct kfifo *fifo,
 12: unsigned char *buffer, unsigned int len)
 13: {
 14: unsigned long flags;
 15: unsigned int ret;
 16: ?
 17: spin_lock_irqsave(fifo->lock, flags);
 18: ?
 19: ret = __kfifo_get(fifo, buffer, len);
 20: ?
 21:  /*
 22: * optimization: if the FIFO is empty, set the indices to 0
 23: * so we don't wrap the next time
 24: */
 25:  if (fifo->in == fifo->out)
 26: fifo->in = fifo->out = 0;
 27: ?
 28: spin_unlock_irqrestore(fifo->lock, flags);
 29: ?
 30:  return ret;
 31: }

和上面的__kfifo_put類似,不難分析。

static inline unsigned int __kfifo_len(struct kfifo *fifo);
static inline unsigned int kfifo_len(struct kfifo *fifo);

 1: ?
 2: /**
 3: * __kfifo_len - returns the number of bytes available in the FIFO, no locking version
 4: * @fifo: the fifo to be used.
 5: */
 6: static inline unsigned int __kfifo_len(struct kfifo *fifo)
 7: {
 8:  return fifo->in - fifo->out;
 9: }
 10: ?
 11: /**
 12: * kfifo_len - returns the number of bytes available in the FIFO
 13: * @fifo: the fifo to be used.
 14: */
 15: static inline unsigned int kfifo_len(struct kfifo *fifo)
 16: {
 17: unsigned long flags;
 18: unsigned int ret;
 19: ?
 20: spin_lock_irqsave(fifo->lock, flags);
 21: ?
 22: ret = __kfifo_len(fifo);
 23: ?
 24: spin_unlock_irqrestore(fifo->lock, flags);
 25: ?
 26:  return ret;
 27: }

這兩個函數返回緩沖區中實際的字節數,只要用fifo->in減去fifo->out即可。

kernel/kfifo.c中還提供了初始化kfifo,分配和釋放kfifo的接口:

struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, gfp_t gfp_mask, spinlock_t *lock);
struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock);

void kfifo_free(struct kfifo *fifo);

再一次強調,調用kfifo_init必須保證size是2的冪,而kfifo_alloc不必,它內部會把size向上圓到2的冪。kfifo_alloc和kfifo_free搭配使用,因為這兩個函數會為fifo->buffer分配/釋放內存空間。而kfifo_init只會接受一個已分配好空間的fifo->buffer,不能和kfifo->free搭配,用kfifo_init分配的kfifo只能用kfree釋放。

循環緩沖區在驅動程序中使用較多,尤其是網絡適配器。但這種免鎖的方式在內核互斥中使用較少,取而代之的是另一種高級的互斥機制──RCU。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253740.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253740.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253740.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

js的規范寫法ES5(自己以后按照這樣寫)

1、引號的使用&#xff0c;單引號 優先&#xff08;如果不是引號嵌套&#xff0c;不要使用雙引號&#xff09; 正常情況&#xff1a;console.log(hello there) 雙引號轉碼&#xff1a; $("<div classbox>") 2、空格的使用問題&#xff1a;&#xff08…

刪除本地git的遠程分支和遠程刪除git服務器的分支

在項目中使用git管理代碼后&#xff0c;有些時候會創建很多不同名稱的分支&#xff0c;以此區分各個分支代碼功能。 而隨著代碼的合并&#xff0c;以前的分支就可能不再需要保存了&#xff0c;所以就要對沒有用的分支進行刪除&#xff0c;包括緊急回滾時從中抽取某一個版本記錄…

數字圖像處理——引導濾波

一、概述 引導濾波是由何愷明等人于2010年發表在ECCV的文章《Guided Image Filtering》中提出的&#xff0c;后續于2013年發表。引導過濾器根據局部線性模型原理&#xff0c;通過考慮引導圖像的內容來計算過濾輸出&#xff0c;引導圖像可以是輸入圖像本身或另一個不同的圖像。具…

Ubuntu 18.04換國內源

2019獨角獸企業重金招聘Python工程師標準>>> 參考文檔&#xff1a; https://blog.csdn.net/zhangjiahao14/article/details/80554616 https://blog.csdn.net/xiangxianghehe/article/details/80112149 1.復制源文件備份&#xff0c;以防萬一 我們要修改的文件是sour…

video4linux簡介

Video4linux&#xff08;簡稱V4L),是linux中關于視頻設備的內核驅動,現在已有Video4linux2&#xff0c;還未加入linux內核&#xff0c;使用需自己下載補丁。在Linux中&#xff0c;視頻設備是設備文件&#xff0c;可以像訪問普通文件一樣對其進行讀寫&#xff0c;攝像頭在/dev/v…

動態DPC算法學習

造成壞點的原因 感光元件芯片自身工藝技術瑕疵造成;光線采集存在缺陷;制造商產品差異;壞點分類 hot pixel: 固定保持較高的像素值,一般呈現為畫面高亮的點;dead pixel: 固定保持較低的像素值,一般在畫面中呈現為暗點;noise pixel:信號強度隨光照呈現的變化規律不符合正…

windows 郵槽mailslot 在服務程序內建立后客戶端無權限訪問(GetLastError() == 5)的問題...

郵槽創建在服務程序內&#xff0c;可以創建成功&#xff0c; 但外部客戶端連接時 m_hMailslot CreateFile("\\\\.\\mailslot\\zdpMailslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);GetLastError返回錯誤 5 &#xff0c;無權…

遞歸下降分析

對于給定的文法G[E] : E→ET|E-T|TT→T*F| T/F|FF→(E)|i 消除左遞歸后的文法是&#xff1a;E→TE E→TE|-TE|∑ T→FT T→*FT|/FT|∑ F→(E)|i 是否是LL(1)文法&#xff1f; select(E→TE)first(TE){(,i}select(E→TE)first(TE){}select(E→-TE)first(-TE){-}select(E→∑)fol…

SYS簡介

"sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.” --- documentation/filesystems/sysfs.txt 可以先把documentation/filesystems/…

數字后端——布圖規劃

布圖規劃&#xff08;floorplan&#xff09;與布局&#xff08;place&#xff09;在芯片設計中占據著重要的地位&#xff0c;它的合理與否直接關系到芯片的時序收斂、布線通暢、電源穩定以及良品率。所以在整個芯片設計中&#xff0c;從布圖規劃到完成布局一般需要占據整個物理…

利用SSH傳輸文件

在linux下一般用scp這個命令來通過ssh傳輸文件。 1、從服務器上下載文件scp usernameservername:/path/filename /var/www/local_dir&#xff08;本地目錄&#xff09; 2、上傳本地文件到服務器scp /path/filename usernameservername:/path 例如scp /var/www/test.php root19…

App WebView實例化

a&#xff0c;高級設置里的環境變量 jdk的配置 b&#xff0c;下載Google的sdk&#xff0c;里面直接包含eclipse 1&#xff0c;新建一個項目 2&#xff0c;起個名字 3&#xff0c;設么走不做&#xff0c;next 4&#xff0c;只操作選擇顯示的三種方式 5&#xff0c;next什么都不做…

[動態代理三部曲:下] - 從動態代理,看Retrofit的源碼實現

前言 關于動態代理的系列文章&#xff0c;到此便進入了最后的“一出好戲”。前倆篇內容分別展開了&#xff1a;從源碼上&#xff0c;了解JDK實現動態代理的原理&#xff1b;以及從動態代理切入&#xff0c;學會看class文件結構的含義。 如果還沒有看過這倆篇文章的小伙伴&#…

Ti的DM368系列芯片的所有PDF資料匯總

http://www.ti.com/sc/docs/psheets/man_dsp.htm

劉浩(專業打劫三十年)20155307的預備作業02:

我的技能&#xff1f;比大多數人好&#xff1f;經驗是什么&#xff1f;與老師的經驗的共同之處&#xff1f; 我的技能之一就是單詞翻譯王——其實看了婁老師的學習經驗之后便有些自慚形穢了&#xff0c;我目前的單詞量是7300,扇貝上測的&#xff0c;而且測試時是嚴格的“不會就…

數字后端——電源規劃

電源規劃是給整個芯片的供電設計出一個均勻的網絡&#xff0c;它是芯片物理設計中非常關鍵的一部分。電源規劃在芯片布圖規劃后或在布圖規劃過程中交叉完成,它貫穿于整個設計中&#xff0c;需要在芯片設計的不同階段對電源的供電網絡進行分析并根據要求進行修改。&#xff0c;主…

逆向project實戰--Acid burn

0x00 序言 這是第二次破解 crackme 小程序&#xff0c;感覺明顯比第一次熟練。破解過程非常順利&#xff0c;差點兒是分分鐘就能夠找到正確的 serial&#xff0c;可是我們的目標是破解計算過程。以下將具體介紹。 0x01 初次執行 剛開始拿到 crackme 先執行程序。看看有哪些明顯…

PyCharm使用技巧(六):Regullar Expressions的使用

2019獨角獸企業重金招聘Python工程師標準>>> PyCharm v2018.2最新版本下載 使用正則表達式查找和替換文件中的文本 示例代碼 使用正則表達式查找和替換字符串 假設您想用擴展標記<title> </title>替換元素&#xff08;title&#xff09;中的屬性&#x…

內核中_init,_exit中的作用

__init&#xff0c; __initdata等屬性標志&#xff0c;是要把這種屬性的代碼放入目標文件的.init.text節&#xff0c;數據放入.init.data節──這一過程是通過編譯內核時為相關目標平臺提供了xxx.lds鏈接腳本來指導ld完成的。 對編譯成module的代碼和數據來說&#xff0c;當模…

jQuery筆記總結

來源于&#xff1a;http://blog.poetries.top/2016/10/20/review-jQuery/ http://www.jianshu.com/p/f8e3936b34c9 首先&#xff0c;來了解一下jQuery學習的整體思路 第一節 jQuery初步認知 jQuery概述 JQuery概念 javascript概念 基于Js語言的API和語法組織邏輯&#xff0c;通…