# A Lightweight File System Design for Unikernel
## 4 Implementations of ULFS  
In this section, we describe the details of the proposed non-hypervisor-based Unikernel file system, which we call ULFS (Ultra Lightweight File System) [19]. Note that we design and implement ULFS based on our open-source project ULV (Ultra Lightweight Virtualization) [20]. ULV is designed as a non-hypervisor Unikernel, so it works directly on a host operating system without a hypervisor. Since Unikernels are intended to run a single application, we aim to implement ULFS under the assumption that the file I/O traffic in a Unikernel application is not heavy and file access patterns are simple (e.g., sequential). Based on this philosophy, ULFS is developed with a lightweight structure by removing features unrelated to a single process that performs primitive file I/O, such as access permission and state management. In particular, we attempted to improve performance largely by eliminating complicated lock structures throughout the file system that are not needed for single-thread applications. ULFS uses a host's file as a guest's file system storage to perform file I/O. Since a file on the host can be mapped to the address space of a host process through mmap, file I/O can be performed by using memory reference interfaces. As the memory-mapped area uses the host's memory space in the form of a page cache, it can enhance file I/O operation latency. This is achieved by processing subsequent I/O requests directly from the cache, thereby eliminating the need for storage access. Also, ULFS improves file system performance since it is designed as a lock-free file system with a single-threaded and non-re-entrant style. ULFS manages the entire file system space in blocks of 4 KB, and blocks can be identified with an integer block id (bid). The superblock, which is the leading block, has a bid value of 0 and contains basic information about the file system. Currently, it only maintains information about the maximum number of blocks. ULFS makes use of map blocks for managing block allocation of the file system in the form of a bitmap. As shown in Figure 2, the first map block is the block following the superblock and has bid number 1. From then on, map blocks are placed at regular intervals, as the maximum number of blocks managed by a single map block is fixed in our design. Thus, the locations of map blocks can be quickly searched by tracking the number of unallocated blocks for each map block when searching for available blocks. Files in ULFS are managed using traditional inode structures [21], and each inode contains the information of a file. The inode block is a block that manages a group of inodes in a tabular form, as shown in Figure 3. The bid number 2 is always assigned to the first inode block, and the maximum number of inodes managed by a single inode block is fixed to 170, as determined by the following equation: _inode block size-meta data size in inode block =_ 400e-16_. If more inodes are necessary, a new inode block is allocated. As shown in Figure 3, inode blocks are managed as a linked list, and bid values for the preceding and following inode blocks are maintained. File system APIs of the POSIX standard require an inode number to identify a file. In ULFS, inode numbers are sequentially assigned to inodes on a logical table consisting of inode blocks. Thus, inode information of a file can be easily identified by sequentially scanning inodes. However, as the inode block location (bid) and the inode offset within an inode block are internally maintained, quick access to inode information is possible. That is, the inode number can be calculated by maintaining the number of the first inode in the block (ino_start in Figure 3). Additionally, the inode block records the count of in-use inodes (n_used in Figure 3), in order to quickly check whether a new inode can be allocated.