Understanding Raspberry Pi File System
Raspberry Pi is one of the most widely used single board computers among IoT enthusiasts. In this article we will be Understanding Raspberry Pi File System and how they are used to boot Linux (Raspbian OS) from an SD card. This is the only officially supported boot method available on the Raspberry Pi, and although it is written specifically with the Raspberry Pi in mind, many other boards can work in a similar way.
To start with, we need to understand the basics of file systems, as the SD card contains multiple partitions with different types of file systems.
On Linux and Unix operating systems, the df utility can be used to check disk space, mount points and file system types, among other things. It is a very useful tool to know what file systems are available and where each one is mounted, as some of these may vary by distribution or platform.
Running df -Th (-Th tells df to display the filesystem types and size in human-readable format) on the Raspberry Pi, we can see that there are several different filesystems mounted.
Understanding the output of df is important to understanding how Linux works. The first thing to notice from the output is that there is a mount point called /boot. The existence of this mount point indicates that the device most likely booted from a file system. On many embedded systems, the operating system boots directly from raw flash memory. Supporting a file system-based approach makes it easier to update and harder to lock down. The user just needs to update the files on the SD card, reboot and that’s it.
File Systems – Overview
A file system is a piece of software, usually part of the operating system (but not necessarily) that stores data in some form of non-volatile memory.
It translates generic OS calls like open/close/read/write etc to its own format and then stores the data on media or memory.
There are many different types of file systems, some of which are designed for general use, while others are for specific types of hardware (flash, hard drive, etc.) or performance requirements (servers and data centers).
LET’S START BY LOOKING AT SOME EXAMPLES OF DIFFERENT MEDIA
In embedded systems, the typical memory of choice is flash, so we’ll focus on this. However, there are many types of flash. The two main categories are raw flash and managed flash.
In the raw flash memory category, the two most common types are NAND and NOR. They usually come with a serial interface, like SPI, or a parallel interface. Although NAND and NOR are fundamentally different technologies, they both work using the concept of sectors or blocks (sector is for NOR, block is for NAND).
A block is the smallest section of memory that can be erased at one time. On these types of devices, you cannot write over existing data. When data needs to be written, the block must first be cleared, which sets all bits to ‘1’.
As you can imagine, writing to a raw memory device requires some additional software to handle all these details. This can degrade performance and is extremely complex. To make matters worse, deleting a block can take a very long time, on the order of seconds.
Finally, they both have a limited number of erase cycles. When they are erased too many times, the blocks can be damaged and cannot be erased anymore or they can corrupt the data when writing or reading, this for example, is one of the important parameters that you have to consider when buying SSD drives.
On a NOR flash device, the erase cycles are typically over 100,000 cycles, while on some higher density NAND devices it can be as low as 3,000 program/erase cycles.
The physics behind bad blocks is different between the two technologies, but the point is that special algorithms are required to distribute writes to the device so that a specific block is not continuously erased. These algorithms are called wear leveling. Both wear leveling and bad block tracking are the responsibility of the file system. Examples of file systems for raw flash memory devices are UBIFS, YAFFS.
The other category of flash memory technology is managed memory. While most of the time the underlying memory is based on NAND, these devices manage bad blocks and wear leveling without any software interaction. They have their own built-in firmware and memory controller that take care of wear leveling and bad block management for you.
SD cards as well as (e)MMC and USB keys are examples of managed memory. Because managed memory handles everything flash-specific in hardware, you can run any generic file system on the device with reasonable confidence.
SD cards typically use the FAT file system. FAT stands for File Allocation Table, which refers to how the file system stores metadata for the volume. FAT has a few different formats that depend on the size of the device, such as FAT16, FAT32, and exFAT.
FAT32 is the de facto standard for most mass memory devices, such as USB keys, SD and MMC cards. This is because it is well known and relatively easy to understand and implement, especially for environments with limited resources. FAT is sometimes called vfat, or a DOS file system.
Raspberry Pi Boot Partitions
Devices like Raspberry Pi don’t just boot directly into Linux. They have a bootloader that runs first and loads the operating system. However, what you may not know is that there are actually multiple stages of the bootloader running, each of which is responsible for setting up some specific hardware function in preparation for the next stage. This is very common in other devices in this class, such as the Beaglebone.
The first-stage bootloader is often called a ROM boot because the firmware is burned directly onto the chip during manufacturing, so it cannot be updated. So it makes sense for chip designers and firmware engineers to support a well-known, widely implemented, and easy-to-implement file system like FAT. The boot ROM on the Raspberry Pi actually runs on the GPU, not the CPU, and its job is to read the SD card’s FAT32 file system, look for a file called bootcode.bin, load that file into memory (cache at this point) and then run it. Opening the /boot directory we saw using df, we can start to see how the boot process works.
We can see here in the /boot directory that there is a file called bootcode.bin. This is the second stage GPU bootloader. It is responsible for configuring the SDRAM and loading the GPU firmware from the SD card. SDRAM is required at this point probably because the cache is not large enough to load the GPU firmware. The other component of bootcode.bin is an ELF loader. This is necessary because the GPU firmware is start.elf.
An ELF file is a type of object file that is created outside of the compiler. It contains the machine code but it also contains information about where to load each section of code into memory.
The GPU firmware is responsible for reading the configuration data from the SD card and loading the kernel. There are several files in the directory that are part of this process:
- kernel.img – The kernel itself in the zImage file format, a type of compressed image
- fixup.dat – Split SDRAM between GPU and CPU.
- *.dtb – the device tree blobs and overlays (in the overlays directory)
- cmdline.txt: the kernel command line parameters
- config.txt: more OS configuration
We’ll go into this in a bit more detail shortly, but to wrap up the GPU firmware, once you’ve loaded all of these files into memory, that’s when control is transferred from the GPU to the CPU.
All files with the .dtb extension are device tree blobs (.dtb). The device tree is an open standard used to describe hardware. Using a device tree, a single kernel image can be built that can support multiple boards using the same processor. In earlier versions of the kernel, there was a C file that described the hardware, meaning that hardware support was compiled directly into the kernel.
There are multiple .dtb files to support the multiple revisions and types of Raspberry Pi boards. The bootloader chooses the file that applies to the board it is running on and reads the correct device tree from the SD card.
It is then passed to the Linux kernel, which on boot loads the appropriate drivers based on what it finds in the device tree. Device trees are written in plain text. There are many specifications depending on architecture, controller type, etc. The plain text device tree file that is written by humans has the extension .dts. It is compiled to a binary .dtb file using a device tree compiler.
Inside the boot directory is another directory called ‘overlays’. This contains various device tree overlays that can be enabled via the config.txt file. The GPU firmware merges the base device tree blob with any overlays defined in config.txt. This allows you to easily add support for other hardware peripherals, assuming compatible support for them is compiled into the kernel (or included as kernel modules).
ROOT FILE SYSTEM
There is one more crucial component required by the Linux kernel to boot: the root file system (rootfs). The kernel image only includes the low-level components: the actual kernel, drivers, and some utilities. By itself, it’s pretty useless.
The rootfs contains everything else we think of when using a Linux distribution i.e. all user-level functions and system utilities. After the kernel loads all the drivers, it looks in rootfs and calls the init program. Depending on the distribution built, the initialization can change substantially, but it will basically load all the functions that the user faces, such as the console and the command line, user support, a graphical interface if there is one, etc. All of the standard Linux utilities we use are built daily as part of rootfs.
Going back to our df output once again, we can see that the first entry is /dev/root, which is mounted to / . This is the root file system. However, one thing to note here is that /dev/root is not actually a device. The kernel command line parameters tell us that the actual device is at /dev/mmcblk0p1, where mmblk0 represents the SD card interface and p7 represents the partition on that device.
Remember how we said earlier that the SD card was formatted with FAT32. Well, there was actually only one partition. The NOOBS installer actually splits the SD card into multiple partitions. This partition is for rootfs and is formatted as ext4. Ext4 is a Linux-specific file system, a journal file system that is commonly used in almost all major Linux distributions.
But this is running on a SD card ?
Yes, that’s right, a SD card, as we saw earlier, is memory managed. So we can treat it like you would a normal hard drive. In fact, Solid State Drives (SSDs) use the same memory technology as SD cards, but there’s a lot more to it.
So what’s in rootfs? Well, rootfs is defined by the File System Hierarchy Standard (FHS), which was created by the Linux Foundation to help standardize distributions.
Different File Directories
/bin : Directory where system executables (such as commands) go and are available to all users.
/boot : Contains all the files required by the bootloader to boot the kernel.
/dev : This directory is not actually part of rootfs, it is created every time it starts and contains files representing each of the devices that can be accessed in user space (look at the output of df to see what is a special type of file system called devtmpfs).
/etc : where system and application configuration files are stored.
/home : Contains another ‘private’ directory for each user. All personal user files are normally stored here. My Documents, Desktop, Downloads, etc. all make up a user’s /home directory.
/lib : The directory where shared libraries and kernel modules are stored. When you compile and need to link libraries, this is a directory that the linker would normally point to.
/media : The default mount point for removable devices; for example, if you connect a USB key
/mnt : The default mount point for temporary file systems (i.e. network shares)
/opt : This used to be where some third-party apps were installed, supposedly for add-on apps.
/proc : Another virtual directory that is not actually part of the root file system. It contains files that represent each of the system processes.
/root : the home directory for root
/run : A directory for applications to store data at runtime. This isn’t actually part of rootfs, it’s a temporary directory created at runtime.
/sbin : Contains executables like the /bin directory, but is typically only used by the system and administrator.
/sys : Another virtual file system that exposes some of the hardware interfaces in the kernel. Provides a way to view the hardware kernel configuration. Although not recommended, you can access some hardware through this interface (ie read/write GPIO, LED, etc.)
/tmp : A temporary file system, which is not actually part of the file system. Essentially a RAM disk that can be used by applications and the system to store temporary files
/usr : A directory where binary files, libraries, header files, and user application documentation are stored. Usually the files here are all read-only. On some distributions, this directory is on a separate partition from the root file system.
/var : Another directory where application runtime information can be stored so it doesn’t end up in the /usr directory. Normally used for registers, locks. Some functions replaced by the /run directory but kept for compatibility.
An important thing to keep in mind is that different distributions, applications, etc. manage and use these directories differently. It’s not always that simple, and to maintain compatibility, many distribution maintainers prefer to keep files and directories legacy rather than deprecate them.
The FHS specification contains guidelines for how applications should use these directories, but that doesn’t mean that all application developers follow them. It is only a guide and will vary even from version to version of the same distribution or application.
The point of all this is that the root file system is essential for Linux to function. You can start the kernel, but it won’t be long after that. The filesystem can be built to suit your own needs which include specific utilities, applications, etc. It gives you the freedom to have a really big filesystem, like Ubuntu which requires 4GB straight out of the box. Well, after this tutorial, I’m sure you will get some idea about the file system of the Raspberry Pi and Raspbian OS.
Other Interesting Tutorials:
- Web Server Pulse Oximeter using NodeMCU ESP8266
- Getting Started with Arduino IoT cloud using NodeMCU and DHT11
- Weather Station with Raspberry Pi PICO and DHT11
- Controlling a servo motor using Raspberry Pi Pico
- Configuring BMP280 Sensor with Raspberry Pi
- Weather Station with BMP280 Sensor and Raspberry Pi Pico
- IoT Pulse Oximeter Using NodeMCU MAX30100 & Blynk
- DIY Digital clock with RTC DS1307 and Raspberry Pi PICO
- IoT Security Camera using ESP32-Cam Blynk and PIR Sensor
- ESP8266 based IoT Panic Alarm for Old Age People using Blynk
- How to send sensor data to Thingspeak using Raspberry Pi
- ESP32-CAM based Email Notification System
- ESP32 based Gas Leakage Detection using Email Notification
- IoT based Motion Detection Alarm using ESP8266 and Blynk
- IoT based Fire Security Alarm System using NodeMCU
- NTP Digital Clock using ESP8266 and OLED Display
- DHT11 Sensor with ESP-NOW and ESP32