關於循環緩沖區(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 並為其分配一個新的槽位。對於新的線程來說,檢查全局列表並且重用相同的槽位(如果以前的實例使用了它的話),這是非常重要的。因為垃圾收集器線程和寫入者線程可能同時嘗試修改全局列表,所以同樣也需要使用某種鎖定機制。
---------------------
作者:Shim_ZoMoe
來源:CSDN
原文:https://blog.csdn.net/sim_szm/article/details/17011545
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!