Shouldn't my machine have a /dev/ram0 file?

I have a college assignment where I have to create a RAM Disk.

I have been told to then make a C program that writes multiple times on a file placed inside the RAM Disk and then do the same on a file placed on the hard disk to compare the write speeds.

I've been given the following script in order to create the RAM Disk:

#!/bin/bash
# RAM Disk
ROOTUSER_NAME=root
MOUNTPT=/tmp/ramdisk
SIZE=2024 # 2K blocs
BLOCKSIZE=1024 # block size: 1K (1024 bytes)
DEVICE=/dev/ram0 # First RAM Disk
username=`id -nu`
[ "$username" != "$ROOTUSER_NAME" ] && echo "not authorised" && exit 1
[ ! -d "$MOUNTPT" ] && mkdir $MOUNTPT
dd if=/dev/zero of=$DEVICE count=$SIZE bs=$BLOCKSIZE
/sbin/mkfs -t ext4 $DEVICE 
mount $DEVICE $MOUNTPT # the mount
chmod 777 $MOUNTPT
echo $MOUNTPT " ready"
exit 0

The problem here is that my machine doesn't seem to have /dev/ram0 in the /dev directory; this is the output of ls /dev:

ferran@amsa:~/Desktop$ ls /dev
autofs          lightnvm    sda     tty22  tty5     ttyS18   vcs3
block           log         sda1    tty23  tty50    ttyS19   vcs4
bsg             loop0       sda2    tty24  tty51    ttyS2   vcs5
btrfs-control   loop1       sda3    tty25  tty52    ttyS20   vcs6
bus             loop2       sda5    tty26  tty53    ttyS21   vcsa
cdrom           loop3       sg0     tty27  tty54    ttyS22   vcsa1
cdrw            loop4       sg1     tty28  tty55    ttyS23   vcsa2
char            loop5       shm     tty29  tty56    ttyS24   vcsa3
console         loop6       snapshot  tty3   tty57      ttyS25   vcsa4
core            loop7       snd     tty30  tty58    ttyS26   vcsa5
cpu             loop8       sr0     tty31  tty59    ttyS27   vcsa6
cpu_dma_latency  loop9      stderr  tty32  tty6     ttyS28   vcsu
cuse            loop-control  stdin     tty33  tty60    ttyS29   vcsu1
disk            mapper      stdout  tty34  tty61    ttyS3   vcsu2
dma_heap        mcelog      tty     tty35  tty62    ttyS30   vcsu3
dmmidi          mem         tty0    tty36  tty63    ttyS31   vcsu4
dri             midi        tty1    tty37  tty7     ttyS4   vcsu5
dvd             mqueue      tty10   tty38  tty8     ttyS5   vcsu6
ecryptfs        net         tty11   tty39  tty9     ttyS6   vfio
fb0             null        tty12   tty4   ttyprintk  ttyS7 vga_arbiter
fd              nvram       tty13   tty40  ttyS0    ttyS8   vhci
full            port        tty14   tty41  ttyS1    ttyS9   vhost-net
fuse            ppp         tty15   tty42  ttyS10   udmabuf  vhost-vsock
hidraw0         psaux       tty16   tty43  ttyS11   uhid    vmci
hpet            ptmx        tty17   tty44  ttyS12   uinput   zero
hugepages       pts         tty18   tty45  ttyS13   urandom  zfs
hwrng           random      tty19   tty46  ttyS14   userio
initctl         rfkill      tty2    tty47  ttyS15   vcs
input           rtc         tty20   tty48  ttyS16   vcs1
kmsg            rtc0        tty21   tty49  ttyS17   vcs2

And this is the output of fdisk -l:

ferran@amsa:~/Desktop$ sudo fdisk -l
[sudo] password for ferran:
Disk /dev/loop0: 4 KiB, 4096 bytes, 8 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop1: 55,45 MiB, 58130432 bytes, 113536 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop2: 65,22 MiB, 68378624 bytes, 133552 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop3: 55,51 MiB, 58191872 bytes, 113656 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop4: 65,1 MiB, 68259840 bytes, 133320 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop5: 50,98 MiB, 53432320 bytes, 104360 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop6: 32,3 MiB, 33865728 bytes, 66144 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/loop7: 32,45 MiB, 34017280 bytes, 66440 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/sda: 100 GiB, 107374182400 bytes, 209715200 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x70d3f840

Device  Boot    Start       End   Sectors  Size Id Type
/dev/sda1  *        2048 117186559 117184512 55,9G 83 Linux
/dev/sda2       117188606 132810751  15622146  7,5G  5 Extended
/dev/sda3       132810752 132812799     2048    1M 82 Linux swap / Solaris
/dev/sda5       117188608 132810751  15622144  7,5G 82 Linux swap / Solaris

Partition table entries are not in disk order.


Disk /dev/loop8: 219 MiB, 229638144 bytes, 448512 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

For the write speed testing I use the following C program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 10000000

int main(int argc, char *argv[]){

    char *c =               /* This is compiled into one long string, with no newlines. */
         "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
         "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
         "Ut enim ad minim veniam, quis nostrud exercitation                "
         "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
         "Duis aute irure dolor in reprehenderit in voluptate velit esse "
         "cillum dolore eu fugiat nulla pariatur. Excepteur sint                "
         "occaecat cupidatat non proident, sunt in culpa qui "
         "officia deserunt mollit anim id est laborum.";
    FILE *fp;

    fp = fopen(argv[1],"w");
    for(int i=0; i < MAX; i++){
        fputs(c,fp);
        fseek(fp, 0, SEEK_SET);
    }
    fclose(fp);
}

I call it two times:

  1. ./writeTest ~/Desktop/test
  2. ./writeTest /tmp/ramdisk/test

And the results aren’t quite convincing, because the RAM Disk file takes 8s and the hard disk file takes 11s, which doesn't seem to be the improvement I should get, right?

When I run the bash script, /dev/ram0 gets created, but I don't know if that is what it's supposed to happen and the write speed improvement is as expected, or if I should already have a /dev/ram0 created in my /dev directory.

Any help would be appreciated.

Edit 1:

I did a few changes according to what @user1686 said.  First of all I loaded the needed module modprobe brd, and then all the /dev/ram# appeared.

Then, I tried to improve my C program in order to reduce the seek calls, even though I know it's still not the best idea to test write speed this way, I've been told to do it using a C program (maybe the goal was just that we saw an improvement when writing on RAM instead of hard disk, and not that we knew the exact increment in write speeds).

In order to reduce seek calls I made the RAM Disk bigger (it went from 2 MB to 32 MB), and then instead of calling seek each iteration of the loop, I approximated how many loops it takes to write almost 32 MB, and then every MAX iterations I call seek, and finally repeat the outer loop LOOPS times.

The program looks like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LOOPS 50
#define MAX 70000

int main(int argc, char *argv[]){

        char *c = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor inci";

        FILE *fp;

        fp = fopen(argv[1],"w");
        for(int i=0; i < LOOPS; i++){
                for(int i=0; i < MAX; i++){
                        fputs(c,fp);
                }
                fseek(fp, 0, SEEK_SET);
        }
        fclose(fp);
}

With this new version and with both the main hard disk and the RAM Disk mounted with the sync option I manage to get an improvement of 30× faster, and it seems to increase as I increase the value of LOOPS.


Solution 1:

Shouldn't my machine have a /dev/ram0 file?

Yes and no, but mostly no.

Linux indeed has a "ramdisk" driver that could provide /dev/ram# devices, and it might be available on your system, but it is usually not loaded by default and those device nodes will not exist unless you load that driver first. To do so, insert the brd.ko kernel module and you should get 8 or 16 ram devices:

# modprobe brd

If the driver was not loaded, and your script blindly went and created /dev/ram0 using dd, then it did not create a ramdisk this way – it created a file. (You should actually remove that before loading the "brd" driver.)

However, by some luck, your script still ends up achieving the goal of "better write speeds" even without /dev/ram0 being available. This is because on most Linux systems, the entire /dev is not stored on physical disk, but has a tmpfs filesystem mounted on it1.

A "tmpfs" is already in-memory – it is kind of like using a ramdisk, except ramdisks are fixed-size and need to be combined with another filesystem, whereas a tmpfs is dynamically-sized on its own.

So any new file you create in /dev – and sometimes in /tmp, too2 – will be stored completely in memory. Even though your script didn't establish a real old-style "ramdisk", but merely created a regular file3 named "/dev/ram0", the fact that it's stored on tmpfs means you still get the expected speed increase.4

(But you would've gotten that by pointing your ./writeTest program directly at /dev/testfile, too.)

So in short, while Linux has ramdisks, tmpfs is the modern replacement of ramdisks in just about every situation. You don't need to decide on its size, or to explicitly "mkfs" a filesystem – just mount a tmpfs wherever you need one:

# mount -t tmpfs horse /tmp/ramdisk

1 (On most Linux systems nowadays, /dev uses "devtmpfs", a special tmpfs variant where device nodes automatically spring into existence as soon as a driver recognizes a device; e.g. as soon as you load the 'brd' driver, /dev/ram0 will appear entirely on its own. But other than that it behaves like a regular tmpfs.)

2 (Use findmnt or mount to check whether your distribution indeed mounts a tmpfs on /tmp or not.)

3 (And even though it's a file, you can still mount it like a disk because the mount command was updated in 2011 to automatically set up a "loop device" without requiring -o loop to be specified.)


And the results aren’t quite convicing, because the RAM file takes 8s and the hard disk file takes 11s, which doesn't seem the improvment I should get, right?

One reason you see both tests running equally fast is that writes are buffered in memory – your program doesn't wait for each write to be fully committed to disk before continuing with the next; instead, everything is written to disk in short bursts.

(And because your program seeks back to 0 and overwrites the data in full, there probably isn't that much that needs to be moved from buffer to disk.)

The other reason you see both tests running fast is because modern HDDs are quite fast, and SSDs even more so. Meanwhile, if your assignment asks you to set up a ramdisk, that makes me think it was written in 1999 with the expectation of disk speeds being in tens of MB/s.