- ベストアンサー
リングバッファって何ですか
こんばんわ。 素人な質問ですが教えてください。 『リングバッファ』とは何ですが? <言葉から連想されるイメージは以下のようなものです> 丸いバッファ? 領域サイズが固定されており、容量がいっぱいになったら、 書き込むアドレスを最初の位置に戻し、古い記憶から順に 上書きされていくもの…? 詳しく教えて頂きたく思います。 お願い致します。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
こんにちは。 > こんなイメージを持ったのですが、間違っていますか? イメージは正しい(間違ってはいない)と思います。 ※当方の認識不足の場合はすみません。 リングバッファは、通信処理などでも使用されます。 ※リングバッファを使用することで、送信処理と受信処理を別々に行うことが 可能となります。 ※また、データの取り逃し防止、処理の渋滞回避などのためにも有効です。 参考までに、リングバッファ使用のサンプルプログラムを作成してみました。 ※文字列を登録するリングバッファを使用したものです。 ※C言語によるベタな作り方です。 C++ならクラス化するとか、STLなどを使用して、もっとスマートな作り方が できると思います。 これで、リングバッファの動作が、ご理解いただければ良いのですが。。。 ※的外れな内容でしたらすみません。 ■サンプルの処理の流れ 1)リングバッファの初期化 ↓ 2)リングバッファに10個の文字列を登録 ・リングバッファの登録数いっぱいまで登録 ↓ 3)リングバッファより1個目のデータ(文字列)を取り出して表示出力 ↓ 4)リングバッファへ11個目の追加データ(文字列)を登録 ↓ 5)リングバッファより2個目のデータ(文字列)を取り出して表示出力 ↓ 6)リングバッファへ12個目の追加データ(文字列)を登録 ↓ 7)リングバッファより3個目のデータ(文字列)を取り出して表示出力 ↓ 8)リングバッファへ13個目の追加データ(文字列)を登録 ↓ 9)リングバッファより残りのデータ(文字列)全部を表示出力 なお、今回のサンプルでは、リングバッファへのアクセスは時系列で順番に 行っていますが、 マルチスレッドのような並列処理で、書き込み処理と、読み込み処理を、別々 に行うような場合は、リングバッファへの同時アクセスを回避するために、 排他制御(片方が使用している場合は、もう片方を使用不可とする)が必要な 場合があります。 ■サンプルプログラム 注)インデント等のため、全角スペースを入れています。 ※コピペする際は、半角スペースorタブに置換して下さい。 ========================= /* * tring10.c : リングバッファのテスト */ #include <stdio.h> #include <string.h> #define RING_SIZE 10 /*リングバッファのデータ登録数(上限値)*/ #define SBUF_SIZE 50 /*リングバッファ内の文字列バッファサイズ*/ #ifdef TRUE /*関数の戻り値の定義(TRUE)*/ #undef TRUE #endif #define TRUE 1 #ifdef FALSE /*関数の戻り値の定義(FALSE)*/ #undef FALSE #endif #define FALSE 0 /* リングバッファ構造体 */ typedef struct _T_RING { int nSetNum; /*登録データ数*/ int nWtPos; /*書き込み位置*/ int nRdPos; /*読み込み位置*/ char szBuf[RING_SIZE][SBUF_SIZE]; /*文字列バッファ*/ } T_RING; /* 関数プロトタイプ */ void InitRing( T_RING *t_ring ); int PutRing( T_RING *t_ring, char *szBuf ); int GetRing( T_RING *t_ring, char *szBuf ); /*== main ==*/ int main(void) { int icnt1; /*カウンタ変数(書き込み用)*/ int icnt2; /*カウンタ変数(読み込み用)*/ char szTemp[SBUF_SIZE]; /*汎用文字列*/ static T_RING t_ring; /*リングバッファ*/ /* 登録データ */ const char szPutData[][20] = { { "Data-01" }, /*01*/ { "Data-02" }, /*02*/ { "Data-03" }, /*03*/ { "Data-04" }, /*04*/ { "Data-05" }, /*05*/ { "Data-06" }, /*06*/ { "Data-07" }, /*07*/ { "Data-08" }, /*08*/ { "Data-09" }, /*09*/ { "Data-10" }, /*10*/ { "AddData-01" }, /*11*/ { "AddData-02" }, /*12*/ { "AddData-03" }, /*13*/ { "\0" } /*End*/ }; /*リングバッファの初期化*/ InitRing( &t_ring ); /*リングバッファへデータ登録*/ /*※最初に10個分(リングバッファの最大登録数分)*/ for(icnt1=0; icnt1<RING_SIZE && szPutData[icnt1][0]!='\0'; icnt1++) { PutRing( &t_ring, (char*)szPutData[icnt1] ); } icnt2 = 0; /*読み込み用カウンタをリセット*/ /*リングバッファより1個読み込んで出力*/ printf( "-- Get Ring (1) --\n" ); if( GetRing( &t_ring, (char*)szTemp ) ){ printf( "No.=%3d: %s\n", icnt2+1, szTemp ); icnt2++; } /*リングバッファへ追加データ登録(1個目)*/ if( szPutData[icnt1][0]!='\0' ){ PutRing( &t_ring, (char*)szPutData[icnt1] ); icnt1++; } /*リングバッファより1個読み込んで出力*/ printf( "-- Get Ring (2) --\n" ); if( GetRing( &t_ring, (char*)szTemp ) ){ printf( "No.=%3d: %s\n", icnt2+1, szTemp ); icnt2++; } /*リングバッファへ追加データ登録(2個目)*/ if( szPutData[icnt1][0]!='\0' ){ PutRing( &t_ring, (char*)szPutData[icnt1] ); icnt1++; } /*リングバッファより1個読み込んで出力*/ printf( "-- Get Ring (3) --\n" ); if( GetRing( &t_ring, (char*)szTemp ) ){ printf( "No.=%3d: %s\n", icnt2+1, szTemp ); icnt2++; } /*リングバッファへ追加データ登録(3個目)*/ if( szPutData[icnt1][0]!='\0' ){ PutRing( &t_ring, (char*)szPutData[icnt1] ); icnt1++; } /*リングバッファの残りのデータを出力*/ printf( "-- Get Ring (4) --\n" ); for(; GetRing( &t_ring, (char*)szTemp ); icnt2++) { printf( "No.=%3d: %s\n", icnt2+1, szTemp ); } return 0; } /*== リングバッファの初期化 ==*/ void InitRing( T_RING *t_ring ) { int i; /* リングバッファのメンバデータを初期化 */ t_ring->nSetNum = 0; t_ring->nWtPos = 0; t_ring->nRdPos = 0; /* リングバッファの文字列バッファをクリア */ for(i=0; i<RING_SIZE; i++){ memset( t_ring->szBuf[i], 0, (SBUF_SIZE * sizeof(char)) ); } } /*== リングバッファへのデータ登録 ==*/ int PutRing( T_RING *t_ring, char *szBuf ) { /* リングバッファがいっぱいのときは登録しないようにする場合は */ /* 下記を有効とする */ /* * if( t_ring->nSetNum == RING_SIZE ){ * return FALSE; * } */ /* リングバッファの書き込み位置のバッファに引数の文字列をセット */ strcpy( t_ring->szBuf[t_ring->nWtPos], szBuf ); /* リングバッファの書き込み位置を+1する */ t_ring->nWtPos = (t_ring->nWtPos + 1) % RING_SIZE; /* リングバッファのデータ登録数を+1する */ if( t_ring->nSetNum < RING_SIZE ){ /* データ登録数が上限未満のとき */ t_ring->nSetNum++; } else { /* データ登録数が上限に達したとき */ /* ※読み込み位置を書き込み位置と同じにする */ t_ring->nRdPos = t_ring->nWtPos; } return TRUE; } /*== リングバッファからデータ取得 ==*/ int GetRing( T_RING *t_ring, char *szBuf ) { /* リングバッファが空きならFALSEで戻る */ if( t_ring->nSetNum == 0 ){ return FALSE; } /* 引数の文字列にリングバッファの読み込み位置の文字列をセット */ strcpy( szBuf, t_ring->szBuf[t_ring->nRdPos] ); /* リングバッファの読み込み位置を+1する */ t_ring->nRdPos = (t_ring->nRdPos + 1) % RING_SIZE; /* リングバッファのデータ登録数を-1する */ /* ※データ登録数が>0のとき */ if( t_ring->nSetNum > 0 ) t_ring->nSetNum--; return TRUE; } ========================= ■サンプルの実行結果 -- Get Ring (1) -- No.= 1: Data-01 -- Get Ring (2) -- No.= 2: Data-02 -- Get Ring (3) -- No.= 3: Data-03 -- Get Ring (4) -- No.= 4: Data-04 No.= 5: Data-05 No.= 6: Data-06 No.= 7: Data-07 No.= 8: Data-08 No.= 9: Data-09 No.= 10: Data-10 No.= 11: AddData-01 No.= 12: AddData-02 No.= 13: AddData-03 以上です。参考になれば幸いです。
その他の回答 (1)
- beefisdead
- ベストアンサー率63% (92/145)
はい、そうです。 一定の容量を取っておいて、アドレスの最後と最初とが繋がっているかのように(プログラム上)扱うものをリングバッファと呼んでいます。 音楽の再生なんかではほぼ必ずリングバッファが使われます。一秒程度のデータをメモリに展開しておいて、それを二分割か四分割し、再生位置が切れ目に来るとイベントが起きてバッファに新しいデータを書き込みます。走る人の前の方に新しく道が出来ていくような具合です。 こういう風にすると、たとえば650MBの音楽CDを再生するときでも、バッファは1秒分だけ持てば良いことになります。
お礼
リングバッファが どういうものか理解することが できました。 回答ありがとうございました。
補足
素早い回答ありがとうございます。 私の持っていたイメージが当たっていたようですね。 音楽の再生で用いられるようですが、確認させて ください! (物分りが悪くて申し訳ありません) <今思っているイメージ> 要素数5個の配列をリングバッファに例えてみます。 ちなみに■が読み取られていない新しいデータ、 □がすでに読み取られた古いデータとします。 最初にバッファいっぱいまでデータを書き込んでおきます。(一秒程度) ■■■■■ 0, 1, 2, 3, 4 この状態で、左から順にCPUがデータを読み取っていく時、 0が読み取られ、CPUは次に1を読みに行きます。 □■■■■ この時に、0の位置に新しいデータを書き込んでおきます。 ■□■■■ CPUが1を読み取り、2を読みに行く時には、2に新しいデータを 書き込んでおく…。 こんなイメージを持ったのですが、間違っていますか?
お礼
ご丁寧な回答をありがとうございます。 リングバッファがどういうものか 分かりました。 サンプルプログラムまで作って頂いて、 本当にありがとうございました。