Random numbers from the Linux kernel
I recently shared a Guess the number game to demonstrate a few “first concepts” in C programming. Whenever I learn a new programming language, I like to start with this simple number-guessing game because it exercises the basics of programming, including how to store values in variables and how to compare values.
That program used the rand
function to generate pseudo-random values between zero and some large maximum value. Pseudo-random numbers are okay if you aren’t working on anything very sensitive—and writing a number-guessing game is pretty straightforward. But if you need to have more randomness to the random numbers you generate, you should instead use the getrandom
system call in the Linux kernel.
Random bits
A system call acts like a function when you use it in a program, but behind the scenes it interacts directly with the Linux kernel. The getrandom
system call prompts the kernel to generate a series of random bits (ones and zeroes) to “fill” a variable.
Use the getrandom
system call like this:
ssize_t getrandom(void buf, size_t buflen, unsigned int flags);
The buf
buffer should be the address to a variable, so the system call can fill this variable directly. The buflen
value tells getrandom
how many bytes to fill.
Note that the flags
value indicates whether the random number should be blocking (GRND_RANDOM
) or nonblocking (GRND_NONBLOCK
). This is important because if the random number source on the system hasn’t been initialized yet, or if the source doesn’t contain enough entropy, then using GRND_RANDOM
with the getrandom
call will wait for the source to fill up with enough random data before the system call will return its own random bits.
On a desktop system where the user frequently uses the mouse, clicks buttons in apps, and types on the keyboard, the system entropy should be high enough to instantly generate a few bytes of random data. But if you’re not sure, you can use GRND_NONBLOCK
to prevent blocking, and the system will generate other random bits on its own.
You can see this in action by writing a very short sample program:
#include <stdio.h>
#include <sys/random.h>
int main()
{
unsigned int num;
getrandom(&num, sizeof(unsigned int), GRND_NONBLOCK);
printf("%u\n", num);
return 0;
}
Every time you run the program, the program will use the getrandom
system call to generate random bits in the num
variable, which it then prints. Note that getrandom
creates random bits; if you store the result in a signed variable such as a regular int
, the result could be positive or negative. In this sample, I’ve stored the result in an unsigned variable, so the result can only be zero or some other positive number.
Updating the number-guessing program
With the getrandom
system call, we can update the Guess the number program to generate a truly random secret number. If you recall the previous number-guessing program, it used srand
to seed the pseudo-random number generator, and rand
to generate new pseudo-random numbers. We can replace this by using randval
to generate random data in a variable, then using the modulo (%
) operator to “fold” the random number into the range 0 to 99, then add 1 to make the secret value between 1 and 100:
unsigned int randval, secret;
getrandom(&randval, sizeof(unsigned int), GRND_NONBLOCK);
secret = (randval % 100) + 1;
Every time you run the program, the Linux kernel will generate random data in the randval
variable, so the value in secret
will always be a truly random value between 1 and 100.
The rest of the program is the same. Here’s the completed program:
#include <stdio.h>
#include <sys/random.h>
int main()
{
unsigned int randval, secret, guess;
getrandom(&randval, sizeof(unsigned int), GRND_NONBLOCK);
secret = (randval % 100) + 1;
puts("Guess a random number from 1 to 100");
do {
puts("Your guess:");
scanf("%d", &guess);
if (guess < secret) {
puts("Too low");
}
else if (guess > secret) {
puts("Too high");
}
} while (guess != secret);
puts("That's right!");
return 0;
}
Guess the number
If we save this updated “Guess the number” program as guess2.c
we can compile it like this:
$ gcc -o guess2 guess2.c
And now we can play the number-guessing game as before. The program will use if
and else if
to indicate if the guess is too low or too high, and exit the program with a “That’s right” message when the guess equals the secret number:
$ ./guess2
Guess a random number from 1 to 100
Your guess:
50
Too high
Your guess:
25
Too low
Your guess:
35
Too high
Your guess:
30
Too high
Your guess:
28
Too low
Your guess:
29
That's right!
Using srand
and rand
is okay if you don’t mind pseudo-random numbers. But if you need to use random values that are actually random, you should use the getrandom
system call instead. Read the manual page (in section 2) to learn more about the getrandom
system call:
$ man 2 getrandom