參考資料:《Linux核心中的迴圈緩衝區》作者:西郵 王聰 嚴重感謝文章作者! 但是(可能是源碼版本問題)有些結論並不正確: “而kfifo_init只會接受一個已指派好空間的fifo->buffer,不能和kfifo_free搭配,用kfifo_init分配的kfifo只能用kfree釋放。” 閱讀源碼可以得出這樣的結論:kfifo_init和kfifo_alloc分配的kfifo都能用kfree釋放。已經用實驗證實。原文連結地址: http://www.kerneltravel.net/jiaoliu/kern-kfifo.html在學習到第十章 中斷處理 時,其中的中斷驅動的I/O需要使用緩衝區,我覺得與其自己實現一個緩衝區,不如利用核心已經寫好的fifo。核心裡有一個通用的迴圈緩衝區的實現在 <linux/kfifo.h>。
使用的資料結構如下:
struct kfifo {unsigned char *buffer; /* 使用的緩衝區頭指標 */unsigned int size; /* 緩衝區總大小 */unsigned int in; /* 已寫入緩衝區的資料總量,當前緩衝區寫指標的位移量:(in % size) */unsigned int out; /* 已讀出緩衝區的資料總量,當前緩衝區讀指標的位移量:(out % size) */ spinlock_t *lock; /* 為避免競態的自旋鎖 */};/*當in==out時,緩衝區為空白;當(in-out)==size時,緩衝區已滿*/kfifo提供的迴圈緩衝的部分函數分為2類:(1)以雙底線開頭,沒有使用自旋鎖函數;(2)沒有雙底線開頭,需要額外加鎖的情況下使用的函數。其實第二類只是在第一類的基礎上進行加鎖後,實際的代碼如下:
unsigned long flags; spin_lock_irqsave(fifo->lock, flags);/*第一類函數*/ spin_unlock_irqrestore(fifo->lock, flags);
以下我按使用的順序介紹每個函數的使用,部分函數源碼在kernel/kfifo.c中定義,這些介面是經過精心構造的,可以小心地避免一些邊界情況,原理其實很簡單,建議去看源碼弄清楚實現的原理,可以學到一些編程技巧。(0)聲明迴圈緩衝資料結構指標
struct kfifo *tekkamanfifo;
(1)初始化迴圈緩衝結構體
struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size, gfp_t gfp_mask, spinlock_t *lock); /*調用kfifo_init必須保證size是2的整數次冪,而且buffer只接受一個已指派好空間的指標。也就是說之前要使用kmalloc分配好空間,將返回的指標傳遞到buffer*/ struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask, spinlock_t *lock); /*調用kfifo_alloc不必保證size是2的冪,它內部會把size向上調整到2的整數次冪。空間分配的內部實現使用kmalloc。函數內部調用kfifo_init/
buffer:之前要使用kmalloc分配好的空間指標;size:迴圈緩衝空間大小;gfp_mask:和kmalloc使用的分配標誌(flags)一樣。(參閱Linux裝置驅動程式學習(8)-分配記憶體)lock:是事先聲明並初始化好的自旋鎖結構體指標;傳回值 為初始化好的迴圈緩衝資料結構指標 。(2) 向緩衝區裡寫入資料
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);
fifo:要寫入資料的緩衝區結構體指標;buffer:要寫入的資料指標,指向核心空間。如需要使用者空間資料,之前要用copy_from_user複製資料到核心空間;len:要寫入的資料大小;傳回值 為寫入緩衝區的資料位元組數。(3)從緩衝區裡讀出資料
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);
參數定義和kfifo_put類似。傳回值 為從緩衝區讀出的資料位元組數。(4)得到緩衝區已有的資料位元組數
unsigned int kfifo_len(struct kfifo *fifo); unsigned int __kfifo_len(struct kfifo *fifo);
fifo:要操作的緩衝區結構體指標;函數返回緩衝區實際已有的資料位元組數,內部實現十分簡單,就是in - out;傳回值 為緩衝區已有的資料位元組數。(5)清空緩衝區
void __kfifo_reset(struct kfifo *fifo); void kfifo_reset(struct kfifo *fifo);
內部實現十分簡單,就是in = out = 0。(6)使用結束,釋放緩衝區。
void kfifo_free(struct kfifo *fifo);
所有的kfifo提供的迴圈緩衝的函數就是這些。在理解內部實現原理的基礎上才能更好的使用它,所以
再次建議閱讀源碼,因為源碼很簡單,但是很精巧。
ARM9開發板實驗
實驗模組源碼:scull-kfifo
測試程式源碼:scull-kfifo-test
實驗現象:
[Tekkaman2440@SBC2440V4]#cd /lib/modules/ [Tekkaman2440@SBC2440V4]#insmod scull_kfifo.ko [Tekkaman2440@SBC2440V4]#cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 14 sound 81 video4linux 89 i2c 90 mtd 116 alsa 128 ptm 136 pts 153 spi 180 usb 189 usb_device 204 s3c2410_serial 252 scull_kfifo 253 usb_endpoint 254 rtc Block devices: 1 ramdisk 256 rfd 7 loop 31 mtdblock 93 nftl 96 inftl 179 mmc [Tekkaman2440@SBC2440V4]#mknod -m 666 /dev/scull_kfifo c 252 0 [Tekkaman2440@SBC2440V4]#echo 1234567890 > /dev/scull_kfifo "sh" did write 11 bytes [Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test scull_kfifo: the module can not lseek! please input the command :1 scull_kfifo: ioctl SCULL_KFIFO_SIZE len=11 please input the command :2 scull_kfifo: SCULL_KFIFO_RESET code=0 please input the command :1 scull_kfifo: ioctl SCULL_KFIFO_SIZE len=0 please input the command :q [Tekkaman2440@SBC2440V4]#echo 123456789012345678901234567890 > /dev/scull_kfifo "sh" did write 31 bytes [Tekkaman2440@SBC2440V4]#echo 123456789012345678901234567890 > /dev/scull_kfifo "sh" did write 31 bytes [Tekkaman2440@SBC2440V4]#echo 1234567890 > /dev/scull_kfifo"sh" did write 2 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes "sh" did write 0 bytes printk: 204310 messages suppressed. "sh" did write 0 bytes 1234567890 [Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test scull_kfifo: the module can not lseek! please input the command :1 scull_kfifo: ioctl SCULL_KFIFO_SIZE len=64 please input the command :q [Tekkaman2440@SBC2440V4]#cat /dev/scull_kfifo printk: 1493677 messages suppressed. "cat" did read 64 bytes 1234"cat" reading: going to sleep 56789012345678901234567890 123456789012345678901234567890 12 [Tekkaman2440@SBC2440V4]#/tmp/scull_kfifo_test scull_kfifo: the module can not lseek! please input the command :2 scull_kfifo: SCULL_KFIFO_RESET code=0 please input the command :q [Tekkaman2440@SBC2440V4]#rmmod scull_kfifo [Tekkaman2440@SBC2440V4]#lsmod Module Size Used by Not tainted [Tekkaman2440@SBC2440V4]#