Uma das regras citadas na documentação da Intel e seu DTRNG (Digital True Random Number Generator) é que o intervalo entre duas leituras não pode ser feito em menos que 10 μs. Na verdade, pode, mas não é garantido que o valor seja “aleatório”.
Outra coisa é que, daqui pra frente, buscarei não usar mais o inline assembler do GCC. O problema é que existem alguns quirks, como a impossibilidade de retornar registradores de 64 bits diretamente… Então usarei o NASM, quando tivermos código em assembly.
O código para leitura do valor aleatório (de 64 bits) não pode ser mais simples:
; ; rdrand.asm ; ; Resolvi simplificar as coisas aqui... Se RDRAND retornar CF=0, então tenta de novo. ; CUIDADO: Isso tem o potencial de cria um loop infinito ; ; Código para x86_64. ; Compilar com: ; nasm -f elf64 -o rdrand.o rdrand.asm ; bits 64 section .text global getRandomNumber:function getRandomNumber: rdrand rax jnc getRandomNumber ret
Deixei de fora a função IsRdRndPresent() porque a implementação, num post anterior, é aceitável.
O problema agora é esperar 10 μs antes de uma leitura:
/* wait10us.c */ #include <time.h> /* OBS: Esse código só funciona no linux! */ void wait10us(void) { struct timespec a, b; a.tv_sec = 0; a.tv_nsec = 10000; /* 10000 ns = 10 us */ while (nanosleep(&a, &b)) a = b; }
A função nanosleep(), usando um timer de alta resolução, espera por uma quantidade de nanossegundos (com uma granularidade, certamente, bem maior que 1 ns!) e retorna 0, se o intervalo passou sem que a função tenha sido interrompida por uma sinalização… Se foi interrompido, nanosleep() retorna -1, mas antes, coloca em b o tempo que ainda resta… Por isso o loop, na listagem acima.
Vale aqui dar uma dica de como você pode implementar nanosleep() no Windows. Uma das maneiras é usando o par de funções QueryPerformanceFrequency() e QueryPerformanceCount() e contar a quantidade de ciclos necessária para atingir a espera desejada… QueryPerformanceFrequency() devolve a quantidade de contagens por segundo e QueryPerformanceCount() retorna a contagem de ciclos feita pela CPU desde o power-up. Com algum cálculo você será capaz de obter a quantidade de nanossegundos por ciclo e a quantidade de ciclos…
Uma vez que temos a função que espera 10 μs, podemos criar nossa função que obtém números aleatórios:
/* random.c */ extern void wait10us(void); extern unsigned long long getRandomNumber(void); unsigned long long __random(void) { wait10us(); return getRandomNumber(); }
Atenção para o fato de esta ser a primeira implementação que leva em conta alguma regra definida pela Intel. A medida que for me familiarizando com outras, implemento e explico por aqui, ok?