gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:10:13 24 4
gpt4 key购买 nike

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() 分别锁定和解锁共享资源。

下面的代码片段说明了这个概念:

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. 跟踪最后一个值写入定时器寄存器的时间。从寄存器读取时,计算写入时间,并从结果中减去适当的值。

  2. 跟踪总共执行了多少条指令,每 N 条指令从定时器寄存器中减 1,其中 N 是一个很大的数字,因此 N 条指令大约需要 1/60 秒。

关于C 中的 CHIP8 - 如何正确处理延迟定时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38339445/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com