關於循環緩沖區(Ring Buffer)的概念,其實來自於Linux內核(Maybe),是為解決某些特殊情況下的競爭問題提供了一種免鎖的方法。這種特殊的情況就是當生產者和消費者都只有一個,而在其它情況下使用它也是必須要加鎖的。對應在Linux內核中有對它的定義:
struct kfifo { unsigned char *buffer; unsigned int size; unsigned int in; unsigned int out; spinlock_t *lock; };
當然關於對它有對應的操作函數,這里不再說了(不是今天的重點)。我們只要了解這種概念就好。
關於定義: 其中buffer指向存放數據的緩沖區,size是緩沖區的大小,in是寫指針下標,out是讀指針下標,lock是加到struct kfifo上的自旋鎖(上面說不加鎖不是這里的鎖),防止多個進程並發訪問此數據結構。當in==out時,說明緩沖區為空;當(in-out)==size時,說明緩沖區已滿。
注:我們保有對應的讀寫指針,當第一批數據(藍色)完成,第二批數據(紅色)會根據當前的寫指針位置繼續我們的數據操作,當達到最大的Buffer_Size時,會重新回到Buffer的開始端。
對此我給出一個簡單的模擬實現Class:
/* * ===================================================================================== * * Filename: ring_buffer_class.h * Version: 1.0 * Created: 2013年11月28日 13時08分04秒 * Revision: none * Compiler: clang * Author: sim szm, xianszm007@gmail.com * * ===================================================================================== */ #include <iostream> class ring_buffer { public: ring_buffer( void* buffer, unsigned int buffer_size ); void buffer_data( const void* data, unsigned int& len ); void get_Data( void* outData, unsigned int& len ); void skip_data( unsigned int& len ); inline unsigned int free_space(); inline unsigned int buffered_bytes(); private: void flush_state(); unsigned char *read_ptr, *write_ptr; unsigned char *end_pos; unsigned char *buffer; int max_read, max_write, buffer_data_; }; ring_buffer::ring_buffer( void* buffer, unsigned int buffer_size ) { buffer = (unsigned char*)buffer; end_pos = buffer + buffer_size; read_ptr = write_ptr = buffer; max_read = buffer_size; max_write = buffer_data_ = 0; flush_state(); } void ring_buffer::buffer_data( const void* data, unsigned int& len ) { if ( len > (unsigned int)max_read ) len = (unsigned int)max_read; memcpy( read_ptr, data, len ); read_ptr += len; buffer_data_ += len; flush_state(); } void ring_buffer::get_Data( void* outData, unsigned int& len ) { if ( len > (unsigned int)max_write ) len = (unsigned int)max_write; memcpy( outData, write_ptr, len ); write_ptr += len; buffer_data_ -= len; flush_state(); } void ring_buffer::skip_data( unsigned int& len ) { unsigned int requestedSkip = len; for ( int i=0; i<2; ++i ) { // 可能會覆蓋,做兩次 int skip = (int)len; if ( skip > max_write ) skip = max_write; write_ptr += skip; buffer_data_ -= skip; len -= skip; flush_state(); } len = requestedSkip - len; } inline unsigned int ring_buffer::free_space() { return (unsigned int)max_read; } inline unsigned int ring_buffer::buffered_bytes() { return (unsigned int)buffer_data_; } void ring_buffer::flush_state() { if (write_ptr == end_pos) write_ptr = buffer; if (read_ptr == end_pos) read_ptr = buffer; if (read_ptr == write_ptr) { if ( buffer_data_ > 0 ) { max_read = 0; max_write = end_pos - write_ptr; } else { max_read = end_pos - read_ptr; max_write = 0; } } else if ( read_ptr > write_ptr ) { max_read = end_pos - read_ptr; max_write = read_ptr - write_ptr; } else { max_read = write_ptr - read_ptr; max_write = end_pos - write_ptr; } }
我們更多要說的是Ring Buffer關於在我們在日志處理方面的一個應用,我們知道對於Program來說日志記錄提供了故障前應用程序狀態的詳細信息,在一段時間的運行過程中,會將不斷地產生大量的跟蹤數據,以及調試信息並持續地將其寫入到磁盤上的文本文件中。多億進行有效的日志記錄,需要使用大量的磁盤空間,並且在多線程環境中,所需的磁盤空間會成倍地增加。常規的日志處理來說存在一些問題,比如硬盤空間的可用性,以及在對一個文件寫入數據時磁盤 I/O 的速度較慢。持續地對磁盤進行寫入操作可能會極大地降低程序的性能,導致其運行速度緩慢。通常,可以通過使用日志輪換策略來解決空間問題,將日志保存在幾個文件中,當這些文件大小達到某個預定義的字節數時,對它們進行截斷和覆蓋。
所以要克服空間問題並實現磁盤 I/O 的最小化,某些程序可以將它們的跟蹤數據記錄在內存中,僅當請求時才轉儲這些數據。這個循環的、內存中的緩沖區稱為循環緩沖區。它可以將相關的數據保存在內存中,而不是每次都將其寫入到磁盤上的文件中。在需要的時候(比如當用戶請求將內存數據轉儲到文件中時、程序檢測到一個錯誤時,或者由於非法的操作或者接收到的信號而引起程序崩潰時)可以將內存中的數據轉儲到磁盤。循環緩沖區日志記錄由一個固定大小的內存緩沖區構成,進程使用這個內存緩沖區進行日志記錄。
當然現在我們面對的大多是多線程的協同工作,對於日志記錄來說,倘若采取傳統的加鎖機制訪問我們的存儲文件,這些線程將在獲得和釋放鎖上花費了大部分的時間,所以采取循環緩沖區會是一個不錯的辦法。通過使得每個線程將數據寫入到它自己的內存塊,就可以完全避免同步問題。當收到來自用戶的轉儲數據的請求時,每個線程獲得一個鎖,並將其轉儲到中心位置。或者分配一個很大的全局內存塊,並將其划分為較小的槽位,其中每個槽位都可由一個線程用來進行日志記錄。每個線程只能夠讀寫它自己的槽位,而不是整個緩沖區。當每個線程第一次嘗試寫入數據時,它會嘗試尋找一個空的內存槽位,並將其標記為忙碌。當線程獲得了一個特定的槽位時,可以將跟蹤槽位使用情況的位圖中相應的位設置為1,當該線程退出時,重新將這個位設置為 0。在這里需要同時需要維護當前使用的槽位編號的全局列表,以及正在使用它的線程的線程信息。
但是這里需要注意的是當一個線程已經死亡,卻沒有釋放相應的槽位,並在垃圾收集器釋放該槽位之前,再次使用了這個線程 ID 並為其分配一個新的槽位。對於新的線程來說,檢查全局列表並且重用相同的槽位(如果以前的實例使用了它的話),這是非常重要的。因為垃圾收集器線程和寫入者線程可能同時嘗試修改全局列表,所以同樣也需要使用某種鎖定機制。
————————————————
版權聲明:本文為CSDN博主「Shim_ZoMoe」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sim_szm/article/details/17011545