1

TL;DR 60 Hz で一定の​​デクリメントを維持しながら (正確ではありませんが、ほぼ正確です)、同時書き込みと読み取りを可能にするタイマーを C でエミュレートする必要があります。これは、Linux CHIP8 エミュレーターの一部になります。共有メモリとセマフォでスレッドベースのアプローチを使用すると、精度の問題が発生するだけでなく、メイン スレッドがタイマーを使用する方法に応じて競合状態が発生します。

そのようなタイマーを考案して実装する最良の方法はどれですか?


エミュレーションの世界に飛び込むために、モジュールごとに C で Linux CHIP8 インタープリターを作成しています。

実装が仕様で可能な限り正確であることを望みます。その点で、タイマーは私にとって最も難しいモジュールであることが証明されています。

遅延タイマーを例にとってみましょう。仕様では、これは「特別な」レジスタであり、最初は 0 に設定されています。値を設定してレジスタから取得する特定のオペコードがあります。

ゼロ以外の値がレジスタに入力されると、60 Hz の周波数で自動的にデクリメントを開始し、ゼロに達すると停止します。

その実装に関する私の考えは次のとおりです。

  1. を使用して約 60 Hz の周波数で自動的に減分を行う補助スレッドの使用nanosleep()fork()とりあえずスレ立てに使ってます。

  2. mmap()タイマー レジスタを割り当て、その値を格納するための共有メモリの使用。このアプローチにより、補助スレッドとメイン スレッドの両方がレジスタから読み書きできるようになります。

  3. 両方のスレッドのアクセスを同期するためのセマフォの使用。私はsem_open()それを作成し、共有リソースをロックおよびロック解除するためにそれぞれ使用sem_wait()します。sem_post()

次のコード スニペットは、概念を示しています。

void *p = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
/* Error checking here */

sem_t *mutex = sem_open("timersem", O_CREAT, O_RDWR, 1);
/* Error checking and unlinking */

int *val = (int *) p;
*val = 120; // 2-second delay

pid_t pid = fork();

if (pid == 0) {
    // Child process
    while (*val > 0) { // Possible race condition
        sem_wait(mutex); // Possible loss of frequency depending on main thread code
        --(*val); // Safe access
        sem_post(mutex);
        /* Here it goes the nanosleep() */
    }
} else if (pid > 0) {
    // Parent process
    if (*val == 10) { // Possible race condition
        sem_wait(mutex);
        *val = 50; // Safe access
        sem_post(mutex);
    }
}

このような実装で見られる潜在的な問題は、3 番目の点に依存しています。ゼロ以外の値に達した後にプログラムがたまたまタイマー レジスタを更新した場合、補助スレッドはメイン スレッドがリソースのロックを解除するのを待機してはなりません。そうしないと、60 Hz の遅延が満たされません。これは、両方のスレッドがレジスタを自由に更新および/または読み取ることができることを意味し (補助スレッドの場合は一定の書き込み)、明らかに競合状態が発生します。

私が何をしているのか、何を達成しようとしているのかを説明したら、私の質問は次のとおりです。

許容可能な固定周波数を維持しながら、同時書き込みと読み取りを可能にするタイマーを考案してエミュレートする最良の方法はどれですか?

4

1 に答える 1