TL;DR我需要在 C 中模拟一个计时器,它允许并发写入和读取,同时保持 60 Hz 的恒定递减(不完全准确,但大致准确)。它将成为 Linux CHIP8 仿真器的一部分。使用共享内存和信号量的基于线程的方法会引发一些准确性问题,以及取决于主线程如何使用计时器的竞争条件。
设计和实现这种计时器的最佳方法是什么?
我正在用 C 语言编写一个 Linux CHIP8 解释器,一个模块一个模块,以便深入仿真世界。
我希望我的实现尽可能准确地符合规范。在这方面,计时器已被证明是我最困难的模块。
以延迟计时器为例。在规范中,它是一个“特殊”寄存器,最初设置为 0。有特定的操作码可以设置一个值,并从寄存器中获取它。
如果将非零值输入到寄存器中,它将以 60 Hz 的频率自动开始递减,一旦达到零就停止。
我对其实施的想法包括以下内容:
使用辅助线程自动递减,频率接近 60 Hz,使用
nanosleep()
. 我fork()
暂时用来创建线程。通过使用共享内存
mmap()
来分配定时器寄存器并将其值存储在上面。这种方法允许辅助线程和主线程读取和写入寄存器。使用信号量来同步两个线程的访问。我
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 延迟将无法实现。这意味着两个线程都可以自由地更新和/或读取寄存器(在辅助线程的情况下持续写入),这显然会引入竞争条件。
一旦我解释了我在做什么以及我试图实现的目标,我的问题是:
设计和模拟允许并发写入和读取同时保持可接受的固定频率的计时器的最佳方法是什么?