C中的CHIP8 - 如何正确处理延迟计时器?

时间:2016-07-12 21:51:17

标签: c linux multithreading emulation chip-8

TL; DR 我需要在C中模拟一个允许并发写入和读取的定时器,同时保持60 Hz的恒定递减(不完全,但大致准确)。它将成为Linux CHIP8仿真器的一部分。使用具有共享内存和信号量的基于线程的方法会引发一些准确性问题,以及竞争条件,具体取决于主线程如何使用计时器。

设计和实施这样一个计时器的最佳方法是什么?

我正在逐个模块地用C编写一个Linux CHIP8解释器,以便深入了解仿真世界。

我希望我的实现尽可能准确。在这个问题上,计时器已被证明是对我来说最困难的模块。

以延迟计时器为例。在规范中,它是一个特殊的"寄存器,初始设置为0.有特定的操作码设置值,并从寄存器中获取。

如果一个不同于零的值输入到寄存器中,它将自动开始递减,频率为60 Hz,一旦达到零就停止。

我对其实施的想法包括以下内容:

  1. 使用辅助线程,使用nanosleep()以近60 Hz的频率自动递减。我暂时使用fork()来创建线程。

  2. 通过mmap()使用共享内存,以便分配定时器寄存器并将其值存储在其上。这种方法允许辅助线程和主线程读取和写入寄存器。

  3. 使用信号量来同步两个线程的访问权限。我使用sem_open()创建它,sem_wait()sem_post()分别锁定和解锁共享资源。

  4. 以下代码段说明了这一概念:

    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);
        }
    }
    

    我看到这种实现的潜在问题依赖于第三点。如果程序碰巧更新定时器寄存器一旦达到不等于零的值,则辅助线程必须等待主线程解锁资源,否则将无法实现60 Hz延迟。这意味着两个线程都可以自由地更新和/或读取寄存器(在辅助线程的情况下是常量写入),这显然会引入竞争条件。

    一旦我解释了我在做什么以及我想要实现什么,我的问题是:

    设计和模拟允许并发写入和读取的定时器的最佳方法是什么,同时保留可接受的固定频率?

1 个答案:

答案 0 :(得分:2)

不要为此使用线程和同步原语(信号量,共享内存等)。实际上,我甚至会说:除非你明确需要多处理器并发,否则不要将线程用于任何。同步很难做到正确,当你弄错时甚至更难调试。

相反,找出一种在单个线程中实现它的方法。我建议采用以下两种方法之一:

  1. 跟踪最后一个值写入定时器寄存器的时间。从寄存器中读取时,计算它写入的时间,并从结果中减去适当的值。

  2. 跟踪总体执行的指令数,并且每N条指令从定时器寄存器中减去1,其中N是一个大数字,N指令约为1/60秒。

    < / LI>