1. Overview
A pseudo file is a fake file that isn’t written to the storage of a computer but, instead, exists only in a computer’s memory. In this tutorial, we’ll learn how to create pseudo files in Linux using two different methods. The first method we’ll learn is through the use of process substitution. Then we’ll learn a more complex but powerful method using RAM disks.
2. How to Create a Pseudo File Using Process Substitution
Process substitution allows us to take the output from a process and redirect it to another process as a file. Since this file only exists in memory and doesn’t exist on the actual drive of the computer, it’s a pseudo file. In this section, we’ll learn how to use process substitution to create and use pseudo files.
2.1. Process Substitution and File Descriptors
File descriptors are integers representing files within Linux. File descriptors are typically unique to a specific process but may be shared between processes. When a process is created, special file descriptors 0, 1, and 2 are automatically created for its standard input, output, and error (stdin, stdout, stderr), and are examples of pseudo files.
There are other ways in which pseudo files are created as file descriptors — for example, during process substitution:
$ file <(echo "Beep")
/dev/fd/63: symbolic link to pipe:[432677]
Here, we use process substitution to redirect the stdout of the echo command as a file to the file command. When utilizing process substitution, a file descriptor is output that is linked to that process’s stdout. The file that’s created is /dev/fd/63, a file descriptor that points to the output of echo in memory. We just created a pseudo file.
One of the limitations of this method, however, is that after the command we redirect to is finished, the file descriptor created by the process substitution is automatically closed:
$ file <(echo "Beep")
/dev/fd/63: symbolic link to pipe:[449919]
$ file /dev/fd/63
/dev/fd/63: cannot open `/dev/fd/63' (No such file or directory)
In this example, we did the same thing as in the previous one. However, we then ran the command again on the same file, but since the file descriptor automatically closes after the first command finishes, the /dev/fd/63 file no longer exists, and so, the second command fails.
2.2. Using a Named File Descriptor
If we want to create a pseudo file using process substitution while being able to read from or write to it multiple times, we can utilize named file descriptors:
$ exec {pseudo_file}<> <(:)
In this example, we created a named file descriptor using exec {pseudo_file}. The variable pseudo_file now contains the integer of the opened file descriptor. The pseudo file created using process substitution is opened for reading and writing using <> on the newly created named file descriptor. We can now use the named file descriptor as a pseudo file, and it’ll stay open until we close it.
After running that command, we can read and write to pseudo_file as we would any other named file descriptor:
$ echo "ABC" >&"$pseudo_file"
$ echo "DEF" >&"$pseudo_file"
$ cat <&${pseudo_file}
ABC
DEF
^C
$ echo "123" >&"$pseudo_file"
$ cat <&${pseudo_file}
123
^C
As we can see, our pseudo file will stay open, and we can read from and write to it multiple times with multiple different commands.
After we’re finished with our named file descriptor, we should be sure to close it:
$ exec {pseudo_file}>&-
In this example, we close pseudo_file like we would close any other file descriptor — by running exec and redirecting the output to &-.
One issue with using this method of creating a pseudo file is that since the named file descriptor is opened for reading and writing when we read the contents of the file with cat, it’ll stay open and will have to be manually closed using ^C (ctrl-c). The other issue is that reading from the file will automatically cause it to be emptied.
2.3. Reading From a Pseudo File With a Custom Function
To allow us to read from our pseudo file without any side effects, let’s code a new cat function specifically for our named file descriptor pseudo file:
my_cat() {
echo '\0\0' >&"${1}"
while read -r -u "$1" line; do
[[ "$line" = '\0\0' ]] && break
echo "$line" | tee "/dev/fd/$1"
done
}
In this example, we create a function that takes a file descriptor and reads through it line by line using read -u. It detects the end of the file by appending \0\0 to the file, which it will then detect while looping through. This string can be customized to prevent accidentally exiting on valid lines.
While it loops through the lines of the file, it will output each line to stdout, but also output each line back to the file descriptor using tee. This ensures that the contents of the file don’t get erased when running the function.
Let’s test out our function by reading and writing to our file multiple times:
$ echo "ABC" >&"$pseudo_file"
$ my_cat "${pseudo_file}"
ABC
$ echo "DEF" >&"$pseudo_file"
$ my_cat "${pseudo_file}"
ABC
DEF
$ exec {pseudo_file}>&-
As we can see in this example, we don’t have to use ^C to manually close our file after printing it, and reading from the file doesn’t erase its contents.
3. How to Create a Pseudo File Using a RAM Disk
In circumstances in which extensive use of pseudo files is needed, we can create a RAM disk. A RAM disk is a partition that uses memory instead of hard drive space. This allows us to create not only files but also directories, without writing to disk.
3.1. Creating a RAM Disk Using the tmpfs Filesystem
An easy way of creating a RAM disk in Linux is creating a tmpfs partition. To begin, let’s create the directory where we’ll mount our partition:
$ mkdir -p /mnt/virtual_drive/
Here, we use mkdir to make the /mnt/virtual_drive/ directory. We use the -p flag to ensure the parent directories of virtual_drive are created if they don’t exist already.
After this, we can create the RAM disk as a partition located in the directory we created:
$ mount -t tmpfs -o size='2048M' tmpfs /mnt/virtual_drive/
To create a RAM disk, we use the mount command and specify the type as tmpfs using the -t flag. We then specify options using the -o flag. In this case, we set just one option: the size of our partition, which we set to 2048M (2048 megabytes). It should be noted that there’s a limited size for tmpfs partitions that depends on the system — typically, it’s around half the total amount of RAM.
After that, we specify the device we’re creating the partition from, tmpfs. Finally, the last argument is the location where we want to mount our partition. Once we run our command, we should have our RAM disk mounted at /mnt/virutal_drive/.
3.2. Creating a Pseudo File With a RAM Disk
Creating and using a pseudo file on our RAM disk is the same as creating and using a regular file on any other drive:
$ touch /mnt/virtual_drive/pseudo_file.txt
$ echo "HI" > pseudo_file.txt
$ cat pseudo_file.txt
HI
Here, we use touch to create a file in */mnt/*virtual_drive. Any file or directory created on the mount point of our tmpfs partition exists on the RAM disk and is a pseudo file/directory.
After we’ve finished using our RAM disk, we should remove it using the umount command:
$ umount /mnt/virtual_drive/
Here, we use umount and the location of our partition to delete the RAM disk we just created. Files that existed on the partition will be deleted, and the RAM used by our partition will be available again for our system.
3.3. Limitations of RAM Disk and Usable Memory
A limitation of the tmpfs partition is the size. Not only are we limited in size by the amount of RAM we have in our system, but the more data we store in a tmpfs filesystem, the less usable RAM we’ll have.
However, it should be noted that the memory allocated for our tmpfs partition is still available to be used by our system as long as it isn’t currently storing any data. This means that we can allocate a sizable partition for our tmpfs partition without necessarily affecting usable RAM:
$ free -h
total used free shared buff/cache available
Mem: 7.5Gi 2.1Gi 4.4Gi 330Mi 1.5Gi 5.3Gi
Swap: 0B 0B 0B
$ mount -t tmpfs -o size='5G' tmpfs /mnt/virtual_drive/
$ free -h
total used free shared buff/cache available
Mem: 7.5Gi 2.1Gi 4.4Gi 330Mi 1.5Gi 5.3Gi
Swap: 0B 0B 0B
To list available memory, we can use the free command and use the -h flag to output in a human-readable format. We run free, then create a 5 GB RAM disk, and then re-run free. The total RAM that is usable by applications or programs (ignoring swap) is the sum of columns free and available. As we can see, the usable RAM in our system didn’t change after creating our tmpfs partition. This is because unused space on a tmpfs partition is still available as memory for our system.
Let’s now see what happens to our usable memory when a large file is saved to our RAM disk:
$ fallocate -l 3G /mnt/virtual_drive/large_file
$ free -h
total used free shared buff/cache available
Mem: 7.5Gi 4.8Gi 1.7Gi 3.3Gi 4.5Gi 2.7Gi
Swap: 0B 0B 0B
In this example, we create a 3 GB file in our RAM disk using the fallocate command. We then run free to see the amount of usable memory. As we can see, the amount of usable memory is 3 GB less. This is because the amount of data saved to our RAM disk is the amount of memory that becomes unusable for our system.
4. Conclusion
Pseudo files can be useful when we need to have the functionality of a file, but without writing to disk. In this article, we learned how to create pseudo files using two different methods. The first method was simple and used process substitution and file descriptors. The second method was more complex and involved the creation and use of a RAM disk.