如何在C中以较低的时钟速度运行程序

Win*_*ing 5 c c++

我需要模仿硬件的工作原理(不适用于视频游戏).

该组件以1 Ghz运行,而我的PC以2.5和2.7 Ghz运行.

所以我试图告诉电脑以较低的速度运行这个特定的过程.

我已经尝试过定时器,但它不会这样做:当处理很小的时间间隔时,过程将无法准确跟踪时间(我需要跟踪毫秒并且不能整齐地完成)计算时间间隔会导致CPU时间浪费

请记住,我不是外包给社区,我正在自己工作,但也许你们可以帮助头脑风暴:)

Enz*_*ber 3

前提

根据我从你的问题和评论中了解到的,你需要在12.5 HzCPU 上运行一个程序。我可以想到单步执行指令,就像调试器一样,但它不是等待您单步执行指令,而是在每次延迟时执行每条指令(就像您所说的尝试过的那样)。所以,如果这个前提是错误的,请告诉我,我会删除我的答案,因为它是基于它的。

想法

如果你的时钟计数是80ms,这意味着你可以至少执行一条指令80ms。不幸的是sleep函数只会以unsigned int为单位接受参数,所以这是行不通的。但是,有一个系统调用nanosleep ,它可以让您以纳秒为单位调整睡眠。

因此,要将毫秒转换为纳秒,请将其乘以 10 6,这将得到80000000 nanoseconds睡眠时间。正如您已经提到的,函数调用和模拟器时间会浪费一些时间,但我认为这是您为模拟器付出的代价(并且您始终可以修改时间以进行更精细的调整)。所以,就是nanosleep

#include <time.h>

int nanosleep(const struct timespec *req, struct timespec *rem);

struct timespec {
       time_t tv_sec;        /* seconds */
       long   tv_nsec;       /* nanoseconds */
};
Run Code Online (Sandbox Code Playgroud)

另一种是 Linux 系统调用ptrace

#include <sys/ptrace.h>

long ptrace(enum __ptrace_request request, pid_t pid,
               void *addr, void *data);
Run Code Online (Sandbox Code Playgroud)

这个功能可以让你用跟踪的进程做各种各样的事情,我建议你阅读手册。这非常有启发性。该系统调用是调试软件的基本功能,您可以在此处阅读有关调试器如何工作的教程。

事实上,我的想法来自那个教程(我几天前读过),我会稍微修改一下代码来做模拟器,所以我也建议阅读该教程。


代码

#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;
}
Run Code Online (Sandbox Code Playgroud)

为了进行快速测试,我fat(5)用 Assembly 编写了这个简单的计算器,它有 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
Run Code Online (Sandbox Code Playgroud)

组装、链接、运行并查看结果:

$ as -o fat.o fat.s
$ ld -o fat fat.o
$ ./fat 
$ echo $?
120
$
Run Code Online (Sandbox Code Playgroud)

所以,它可以工作并计算 5 的阶乘。所以,如果我的数学正确的话,65 条指令65/12.5在 12.5Hz CPU 上运行需要几秒钟,对吧?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
Run Code Online (Sandbox Code Playgroud)

手册参考