MENU

LinuxにC言語でリングバッファの使い方

  • URLをコピーしました!

※本ページはアフィリエイト広告を利用しています

今回は、マルチスレッドプログラミングにおける重要な概念、リングバッファについて解説します。

リングバッファは、データの一時的な格納場所として機能し、特にプロデューサーとコンシューマーという2つの異なるプロセスまたはスレッド間でデータをやり取りする際に有用です。

この記事では、まずリングバッファの基本的な概念とその特性について説明し、その後、C言語を用いてプロデューサーとコンシューマーのスレッド間でリングバッファを使ってデータ通信を行うデモプログラムを作成します。

目次

リングバッファとは

リングバッファ(またはサーキュラーバッファ)は、データ構造の一種で、特に組み込みシステムやリアルタイムシステムでよく使用されます。リングバッファは、固定サイズの配列として実装され、その配列は論理的にリングまたは円として機能します。これは、配列の最後の要素の次には配列の最初の要素が来るという意味です。

リングバッファの特徴

リングバッファは、一般的に2つのポインタ(読み取りポインタと書き込みポインタ)を持ちます。データは書き込みポインタの位置に追加され、読み取りポインタの位置から読み取られます。データが書き込まれると、書き込みポインタが進み、データが読み取られると、読み取りポインタが進みます。これらのポインタが配列の末尾に到達すると、配列の先頭に戻ります。

リングバッファの特徴
  1. 固定サイズ: リングバッファは固定サイズの配列として実装されます。これにより、メモリの使用量を制御することができます。
  2. 循環構造: リングバッファは、配列の最後の要素の次に配列の最初の要素が来るという循環構造を持っています。これにより、バッファが常に最新のデータセットを保持することができます。
  3. 読み取りと書き込みのポインタ: リングバッファは、一般的に2つのポインタ(読み取りポインタと書き込みポインタ)を持ちます。データは書き込みポインタの位置に追加され、読み取りポインタの位置から読み取られます。
  4. データのオーバーフロー防止: 書き込みポインタが読み取りポインタを追い越すと、古いデータが新しいデータで上書きされます。これにより、データのオーバーフローを防ぐことができます。
  5. マルチスレッド環境での使用: プロデューサとコンシューマが同時にアクセスできるため、マルチスレッド環境でのデータ共有に適しています。
  6. 広範なアプリケーション: リングバッファは、音声やビデオのストリーミング、ネットワーク通信、データロギングなど、さまざまなアプリケーションで使用されます。

リングバッファのメリット

リングバッファの主な利点は、データのオーバーフローを防ぐことができる点です。書き込みポインタが読み取りポインタを追い越すと、古いデータが新しいデータで上書きされます。これにより、バッファが常に最新のデータセットを保持することができます。

また、リングバッファは、プロデューサとコンシューマが同時にアクセスできるため、マルチスレッド環境でのデータ共有にも適しています。プロデューサはデータをバッファに書き込み、コンシューマはバッファからデータを読み取ります。これにより、プロデューサとコンシューマが同時にバッファを使用できるため、パフォーマンスが向上します。

作成したCコード

コード解説

このプログラムは、2つのスレッド(プロデューサーとコンシューマー)を作成し、それらがリングバッファを介してデータをやり取りするデモンストレーションです。

プロデューサーとコンシューマーは同時にリングバッファにアクセスでき、ミューテックスと条件変数を使用してスレッド間の同期を行います。

  1. 初期化: メイン関数では、リングバッファの初期化を行います。書き込み位置と読み取り位置を0に設定し、ミューテックスと条件変数を初期化します。
  2. スレッドの作成: 次に、プロデューサーとコンシューマーの2つのスレッドを作成します。これらのスレッドは、それぞれproducer関数とconsumer関数を実行します。
  3. プロデューサー: プロデューサーのスレッドは、リングバッファにデータ(この場合は0から99までの整数)を書き込みます。バッファが満杯の場合(つまり、次の書き込み位置が現在の読み取り位置と一致する場合)、プロデューサーは書き込み可能になるまで待ちます。データが書き込まれると、プロデューサーは読み取り可能状態をシグナルします。
  4. コンシューマー: コンシューマーのスレッドは、リングバッファからデータを読み取ります。バッファが空の場合(つまり、現在の書き込み位置と読み取り位置が一致する場合)、コンシューマーは読み取り可能になるまで待ちます。データが読み取られると、コンシューマーは書き込み可能状態をシグナルします。
  5. 終了処理: メイン関数では、2つのスレッドが終了するのを待ち、その後でミューテックスと条件変数を破棄します。

作成した全体のソースコード

作成した全体のソースコードは、以下の通りです。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUFFER_SIZE 10  // バッファのサイズを定義

// リングバッファの構造体を定義
typedef struct {
    int buffer[BUFFER_SIZE];  // バッファ本体
    size_t writePos;  // 書き込み位置
    size_t readPos;   // 読み取り位置
    pthread_mutex_t mutex;  // ミューテックス
    pthread_cond_t canRead;  // 読み取り可能状態を示す条件変数
    pthread_cond_t canWrite; // 書き込み可能状態を示す条件変数
} RingBuffer;

// プロデューサーのスレッド関数
void* producer(void* arg) {
    RingBuffer* ringBuffer = (RingBuffer*)arg;
    int i;
    for (i = 0; i < 100; ++i) {
        pthread_mutex_lock(&ringBuffer->mutex);  // ミューテックスをロック
        if ((ringBuffer->writePos + 1) % BUFFER_SIZE == ringBuffer->readPos) {
            // バッファが満杯の場合、書き込み可能になるまで待つ
            pthread_cond_wait(&ringBuffer->canWrite, &ringBuffer->mutex);
        }
        // データをバッファに書き込む
        ringBuffer->buffer[ringBuffer->writePos] = i;
        ringBuffer->writePos = (ringBuffer->writePos + 1) % BUFFER_SIZE;
        pthread_cond_signal(&ringBuffer->canRead);  // 読み取り可能状態をシグナル
        pthread_mutex_unlock(&ringBuffer->mutex);  // ミューテックスをアンロック
    }
    return NULL;
}

// コンシューマーのスレッド関数
void* consumer(void* arg) {
    RingBuffer* ringBuffer = (RingBuffer*)arg;
    int i;
    for (i = 0; i < 100; ++i) {
        pthread_mutex_lock(&ringBuffer->mutex);  // ミューテックスをロック
        if (ringBuffer->writePos == ringBuffer->readPos) {
            // バッファが空の場合、読み取り可能になるまで待つ
            pthread_cond_wait(&ringBuffer->canRead, &ringBuffer->mutex);
        }
        // データをバッファから読み取る
        int item = ringBuffer->buffer[ringBuffer->readPos];
        ringBuffer->readPos = (ringBuffer->readPos + 1) % BUFFER_SIZE;
        pthread_cond_signal(&ringBuffer->canWrite);  // 書き込み可能状態をシグナル
        pthread_mutex_unlock(&ringBuffer->mutex);  // ミューテックスをアンロック
        printf("Consumer read: %d\n", item);  // 読み取ったデータを表示
    }
    return NULL;
}

int main() {
    RingBuffer ringBuffer;
    ringBuffer.writePos = 0;  // 書き込み位置を初期化
    ringBuffer.readPos = 0;   // 読み取り位置を初期化
    pthread_mutex_init(&ringBuffer.mutex, NULL);  // ミューテックスを初期化
    pthread_cond_init(&ringBuffer.canRead, NULL);  // 読み取り可能状態を示す条件変数を初期化
    pthread_cond_init(&ringBuffer.canWrite, NULL); // 書き込み可能状態を示す条件変数を初期化

    pthread_t producerThread, consumerThread;
    // プロデューサーとコンシューマーのスレッドを作成
    pthread_create(&producerThread, NULL, producer, &ringBuffer);
    pthread_create(&consumerThread, NULL, consumer, &ringBuffer);

    // スレッドが終了するのを待つ
    pthread_join(producerThread, NULL);
    pthread_join(consumerThread, NULL);

    // ミューテックスと条件変数を破棄
    pthread_mutex_destroy(&ringBuffer.mutex);
    pthread_cond_destroy(&ringBuffer.canRead);
    pthread_cond_destroy(&ringBuffer.canWrite);

    return 0;
}

実行結果

実行環境の構築

WSLを使ったLinuxでの実行環境の構築方法を以下の記事で解説しています。

コンパイル環境構築

Linux環境でC言語のコンパイラとして使用するgccの導入方法を以下の記事で解説しています。(PCなどRaspberry Pi以外の環境でも使用できます)

以下のコマンドでコンパイルを実行します。

sudo gcc ringbuffer.c -o ringbuffer

実行結果

先ほどのコンパイルで生成された実行ファイルを、ターミナルから実行します。

./ringbuffer

プログラムが実行されると、プロデューサースレッドからリングバッファに書き込まれたデータ、をコンシューマスレッドで読みだして画面に表示することができました。

組込みソフトウェアの知識を仕事に活かすには

趣味でマイコンのプログラムを書いてるから、仕事でも活かしたいなあ。
でも自信が無い…

そんな時は、一度スクールで体系的に基礎を学ぶといいよ。

趣味のプログラミングスキルを活かしたいけど、「実務レベルで通用するか不安」という方には、プログラミングスクールを受講してみるのがおすすめです。

プログラミングスクールのカリキュラムは、企業の研修などでも利用されており、実践的で即戦力となるスキルを習得することが可能です。

Winスクールでは、Web系や機械学習、データサイエンスなどに加え、プログラミングスクールでは珍しいハードウェアを扱うC言語組み込みシステムやハードウェアとクラウドを連携させたIoTシステムの講座が非常に充実しています。

授業内容も受講者に対して組み込み用マイコンボードRaspberry Piを用意し、実機を使いながら学べる本格的なカリキュラムが用意されています。

また、受講者のスケジュールやロケーションに応じて、対面授業オンライン授業を選択、もしくは組み合わせて受講できるため、プログラミング初心者の方でも安心です。

家電自動車業界などへの転職を目指したい方に非常におすすめです。

また近年、クラウドソーシングのプラットフォームにも、マイコンボードを使った組み込みソフトウェアの開発案件も多く募集されており、副業でマイコンのソフトウェア開発を行う方も増えています。

クラウドワークスなどで「Raspberry Pi」と検索すると、開発解説記事作成などの案件が出てくるよ。

スクールで基礎をしっかり押さえておけば、安心して案件に応募できるね。

 プログラミング言語Python, C, C#, Java, JavaScript(React.js), Ruby, PHP, 他
 授業形式対面&オンラインが選択可能
 学習期間90分×40回(60時間)(C言語&組込みシステム開発(マイコンボード)コースの例)
 費用353,100円~(C言語&組込みシステム開発(マイコンボード)コース例)
 補助金、給付金一部の講座が教育訓練給付制度対象
 転職支援個別の就職カウンセリング、無料の就職支援セミナー
 実績・信頼性年間17,000人以上の受講者、年間1,484社の企業研修実績

\ Winスクール公式サイトを見てみる! /

まとめ

以上、リングバッファとその実装について解説しました。

リングバッファは、プロデューサーとコンシューマーという2つの異なるプロセスまたはスレッド間でデータをやり取りする際に非常に有用なデータ構造です。

今回作成したC言語のデモプログラムを通じて、リングバッファの動作原理とその実装方法を理解することができたかと思います。この知識は、組み込みシステムやマルチスレッドアプリケーションの開発において、非常に役立ちますので、ぜひ活用してみてください。。

よかったらシェアしてね!
  • URLをコピーしました!
目次