Primeira tentativa de usar RDRAND em conformidade com as especificações da Intel

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(&amp;a, &amp;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()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?

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s