我需要模拟一款硬件的工作方式(不是用于视频游戏)。
这个组件以1 Ghz的速度运行,而我的电脑以2.5和2.7 Ghz的速度运行。
因此,我正试图告诉电脑以更低的速度运行特定的进程。
我尝试过使用定时器,但这样做行不通:当处理时间间隔很小的时候,进程无法准确地跟踪时间(我需要跟踪毫秒,并且不能整洁地完成)。 而且计算时间间隔会损失一些CPU时间。
请记住,我不是外包给社区的,我是自己在工作,但也许你们可以帮助进行头脑风暴 :)
我需要模拟一款硬件的工作方式(不是用于视频游戏)。
这个组件以1 Ghz的速度运行,而我的电脑以2.5和2.7 Ghz的速度运行。
因此,我正试图告诉电脑以更低的速度运行特定的进程。
我尝试过使用定时器,但这样做行不通:当处理时间间隔很小的时候,进程无法准确地跟踪时间(我需要跟踪毫秒,并且不能整洁地完成)。 而且计算时间间隔会损失一些CPU时间。
请记住,我不是外包给社区的,我是自己在工作,但也许你们可以帮助进行头脑风暴 :)
根据您的问题和评论,我理解您需要在 12.5 Hz
的 CPU 上运行程序。我可以考虑单步执行指令,就像调试器一样,但是它会在每个 时间延迟 中执行每个指令(类似于您尝试过的内容)。所以,如果这个前提是错误的,请告诉我,我将删除我的答案,因为它是基于此的。
如果您的时钟计数为 80ms
,那么这意味着您至少可以在 80ms
内执行一个指令。不幸的是,sleep 函数只接受以 秒 为单位的无符号整数参数,所以这不起作用。然而,有一个系统调用 nanosleep,它可以让您调整睡眠时间,单位为 nanoseconds。
因此,要将毫秒转换为纳秒,您需要将其乘以106,这将给您80000000纳秒
的睡眠时间。正如您已经提到的,从调用函数和模拟器时间中会有一些时间浪费,但我认为这是使用模拟器所必须付出的代价(并且您可以随时调整时间以进行更精细的调整)。因此,nanosleep
是:
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
另一个是Linux系统调用ptrace
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,
void *addr, void *data);
这个函数将让您对跟踪的进程进行各种操作,建议您阅读手册,它非常有用。该系统调用是调试软件的基本功能,并且您可以在调试器如何工作教程中阅读相关内容。
实际上,我的想法来自于那篇教程(我几天前读了它),我将稍微修改那段代码以执行模拟器,因此我也建议您阅读该教程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#define MILLI 80
#define HZ ((double)1000/(double)MILLI)
/* milli to nano */
#define m2n(a) (a*1000*1000)
void run_target(char *prog)
{
printf("Emulating %.2lf Hz to proccess %s...\n\n", HZ, prog);
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
perror("ptrace");
return ;
}
execl(prog, prog, (char*)NULL);
}
void run_emulator(pid_t child)
{
int wait_status;
struct timespec req;
unsigned long int count = 1;
/* set up the emulation speed */
req.tv_sec = 0;
req.tv_nsec = m2n(MILLI);
/* wait for stop on first instruction */
wait(&wait_status);
while (WIFSTOPPED(wait_status)) {
/* this loop will repeat at every instruction, so it executes the
* instruction and sleeps for the amount of time needed to
* emulate the wanted speed.
*/
if (ptrace(PTRACE_SINGLESTEP, child, 0, 0) < 0) {
perror("ptrace");
return ;
}
wait(&wait_status);
/* this does the sleep */
nanosleep(&req, NULL);
}
}
int main(int argc, char *argv[])
{
pid_t child;
if (argc < 2) {
fprintf(stderr, "Usage: %s [prog_name]\n", argv[0]);
exit(EXIT_FAILURE);
}
child = fork();
if (!child)
run_target(argv[1]);
else if (child > 0)
run_emulator(child);
else {
perror("fork");
exit(EXIT_FAILURE);
}
return 0;
}
fat(5)
计算器,它有 65 条指令(在我的机器上,当然)。 .section .data
.section .text
.globl _start
.globl factorial
_start:
pushq $5
call factorial
movq %rax, %rdi
movq $0x3c, %rax
syscall
.type factorial, @function
factorial:
pushq %rbp
movq %rsp, %rbp
movq 16(%rbp), %rax
cmpq $1, %rax
je end_factorial
decq %rax
pushq %rax
call factorial
movq 16(%rbp), %rbx
imulq %rbx, %rax
end_factorial:
movq %rbp, %rsp
popq %rbp
ret
要进行组装、链接、运行并查看结果:
$ as -o fat.o fat.s
$ ld -o fat fat.o
$ ./fat
$ echo $?
120
$
65/12.5
秒,对吗?65/12.5 = 5.2
。$ time ./lower ./fat
Emulating 12.50 Hz to proccess ./fat...
Returned: 30720
real 0m5.211s
user 0m0.000s
sys 0m0.008s