File Systems
Prerequisite Knowledge
This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.
Previously Covered in This Subject
- Files as the Logical Storage Unit — covered in Lecture 1 (Introduction to Operating Systems)
- File System Implementation — covered in Lecture 2 (Operating Systems: Services, Interfaces, and Structures)
- Copying a File with System Calls — covered in Lecture 2 (Operating Systems: Services, Interfaces, and Structures)
- Logical (Virtual) and Physical Addresses — covered in Lecture 11 (Memory Management)
- Internal Fragmentation — covered in Lecture 11 (Memory Management)
- Compaction — covered in Lectures 11 and 12 (Memory Management)
14.1 Files and the Scope of This Topic
14.1.1 Where We Are Going
This session covers the file systems of the operating system: how the OS manages them, what the structure of the disk directory is, how file mounting is done, how the file system is implemented on the directory, and what the different allocation methods are that the OS uses to store files. If one or two topics are left out, they continue in the next session. After that comes the mass storage structure: the secondary storage devices and the performance of the disk. At the end comes disk management, that is, disk scheduling and all those things. With that, the scope of the unit is complete.
Exam note: the scope runs from directory structure through mounting, implementation, and allocation methods, and then on to mass storage structure and disk scheduling. Questions on this unit can come from any of these blocks, so keep the roadmap in mind as you study: directory structure → mounting → file system implementation → allocation methods (contiguous, linked/FAT, indexed) → mass storage structure → disk scheduling.
14.1.2 What a File Is
Everyone already deals with files on their own operating system. In Windows you have a drive, and inside that drive you create as many folders as you want, and each folder holds some files. But what exactly is a file, and how does the operating system keep track of all of them?
A file is a collection of some information — a logical unit of information storage. It is the basic container that all of this works with. The operating system abstracts away the physical details of the storage device (which cylinder, which track, which sector) and presents the user with a simple, named container: the file.
Some properties exist for every folder we create, and on Unix or Linux even the directory itself is treated as a file. Every such file is identified by an inode (an index node): on Unix or Linux the file is always identified in the form of an inode. The inode is a control structure that carries the key information the operating system needs for that file — its attributes, permissions, and the location of its contents. Windows has the same idea under a different name: in NTFS each file has a unique ID called a file reference, a 64-bit value made of a 48-bit file number plus a 16-bit sequence number, and the file's record lives as an entry in the master file table (MFT). So the identifier concept is universal; the names differ per system.
Think of a file as an item in a library catalog. The library has millions of books, but each book gets one catalog entry with a unique number; you never need to know which shelf row or corner the book sits in — the catalog entry tells you. Likewise, every file on a Unix system has an inode entry that tells the OS everything about it, so the OS never has to hunt through raw disk space to find out what a file is.
The operating system maps files to particular devices. These devices are usually non-volatile: even if you switch the system off, or something happens to the system and you come back later, the data can be recovered. That is the property of the devices attached to the system, the secondary storage type, as opposed to main memory or primary storage. Main memory loses everything when power goes away; the secondary storage devices keep their contents. Storage structure is discussed in the next session; here the focus is files alone.
A file is a collection of information, so we give each file a name based on the type of information it holds. Consider the logical address space: as seen previously, the address generated by the CPU is called the logical address. The space in which a file is stored is usually contiguous. A file can hold different types of data — numeric data, characters, or a binary file — and even a program, since anything can be stored in the form of a file.
14.1.3 File Attributes
Whenever a file is created, the name we give it should be in a human-readable form. Beyond the name, a file has a set of attributes — the properties the operating system records about the file:
- Identifier — a unique tag assigned by the operating system within the file system; the inode in Linux is the example, and Windows also has an identifier of its own (the file reference in the MFT).
- Type — what kind of file it is: whether it is a
.pngfile, a.txtfile, a PDF, an image, an audio, or a video file, and whether the operating system supports that type. - Location — where the file is present in the system: a pointer to the device and the position of the file on that device.
- Size — the current size of the file, in bytes, words, or blocks, and sometimes its maximum allowed size.
- Protection — who may read, write, or execute it: the access-control information of the file.
- Time and identification — when it was created, the user identification, and the dates and times. These are kept mainly for protection, security, and monitoring of the usage of the file.
All of this information about the files lives in the directory structure. The directory structure itself sits under a particular disk, and it is identified with a name and an identifier. A directory's attributes are much the same, except for one detail: because a directory is not a single file but holds any number of files, its size is greater than one block — a directory entry alone can take more than a kilobyte to record (name plus identifier plus attributes), so a directory that holds thousands of files can grow to megabytes. In other words, the directory is itself a stored structure on the disk, and its size reflects the number of entries inside it.
A common mix-up: the file's name is for humans, the file's identifier is for the operating system. Two files in the same file system can never share an identifier, even though their names may look alike. Also, do not confuse the file itself with the directory entry: the directory entry points to the file's attributes; it is not the file's contents.
A file is a named, logical container of information on non-volatile secondary storage. The operating system identifies every file by a unique tag (the inode on Unix/Linux, the file reference in Windows) and records its attributes — identifier, type, location, size, protection, and timestamps — in the directory structure. These attributes are what we meet again when files are opened and managed in the next topic.
14.2 File Operations and Open Files
14.2.1 The File as an Abstract Type
A file is an abstract type: it becomes concrete only when we perform operations on it. Creating a file simply, on Unix, is done with the command touch — touch somefile creates a file with that name and nothing else; it has no information or content yet, so it stays abstract. To make a file real, you create it properly with the help of an editor and put some contents into it. That distinction — abstract empty creation versus concrete, content-filled creation — is the starting point for all file operations.
Treat the file as an abstract data type: all the operating system promises is a defined set of operations (create, write, read, reposition, delete, truncate) on a named container. How the container is stored — which blocks, which sectors — is hidden from the user. This is the same idea as a stack or a queue in a programming course: you know the operations, not the internals.
14.2.2 Creating, Writing, Reading, and Repositioning
The operations every file system supports, in order of use:
- Create — make a new file (the
touchcase above). Two steps are needed: find space in the file system for the file, and make an entry for the new file in the directory. - Write — write some information into the file; a pointer specifies the location where the write happens. After each write the pointer moves to the end of the newly written data.
- Read — read the contents with the help of a pointer. The read pointer marks where the next read begins, and it advances after each read.
- Reposition — change the current position within the file: from the start, or from the current position, wherever it is — the middle or the end. This operation is also called a seek; it moves the current-file-position pointer without any actual I/O.
- Delete — when a file is deleted, the space occupied by the file becomes free and is placed into the pool of free space.
- Truncate — remove the data inside the file.
Because a process is usually either reading from or writing to a file at any one time, the system keeps a single current-file-position pointer shared by both operations. One pointer, not two — that saves space and reduces complexity. Repositioning (seek) simply changes this one pointer.
14.2.3 Deleting versus Truncating
Truncating and deleting are totally different. Truncation deletes the data present inside the file but keeps the file itself. Deletion removes the whole thing: the file, the data inside it, and the space it occupied — everything is deleted. This is a common point of confusion, so keep the two apart: truncate clears contents, delete removes the file entirely.
Think of a notebook. Truncating tears out all the written pages but keeps the notebook cover — the file still exists with its name, attributes, and permissions, just with length zero. Deleting throws the whole notebook away: cover, pages, and all. If you need the file to keep existing (for example, a log file that must stay on disk), truncate; if you want the space back entirely, delete.
14.2.4 Opening Files: Handles, Descriptors, and Open File Tables
To access a file you must open it. When the open succeeds, the OS returns a file handle, called a file descriptor on Unix or Linux. With that descriptor you can perform any operation you need, and you use the same descriptor to close the file.
Why open at all? Most file operations begin by searching the directory for the file's entry. If every read and write did that search, the system would waste enormous time. So the OS copies the directory entry into an in-memory table at open time, and all later operations use an index into that table instead of searching the directory again.
Closing matters because of reference counting. When a file is opened, the process opening it has its information tracked in the file open count — a counter of how many times the file is opened. Only when a process (or group of processes) closes the file does the count get decremented; when it reaches zero, the entry is removed from the open file table. If you never closed files, the reference count would keep the entry alive even after all processes finished, and the entry would occupy memory unnecessarily.
There are two tables in play. The global (system-wide) open file table holds information about all the files opened by the group of processes. Each process additionally has a per-process open file table that holds the pertinent information for that process plus a pointer to the entry in the global table. The per-process table carries the current file position pointer — the location of the next read or write — and the access rights: whether the process has read, write, execute, or append permission for that file. The per-process pointer is how each process's information is kept updated in the global table.
The two-level table design works like a shared textbook. The system-wide table is the book itself — one copy of the file's facts (disk location, size, access dates) that everyone can point at. Each per-process table entry is a bookmark — it records where that particular process is in the book and what that process may do (read only, read/write, append). Ten processes can each hold a bookmark into the same book; when the last bookmark is removed, the book is returned to the shelf.
14.2.5 File Locking
Not everyone can access a particular file at the same time. Access can be shared or exclusive. When a file is locked, sometimes the access is denied — that case is mandatory locking: the request stays denied until the holder is forced to release the lock, and a request for the locks to be released has to be made. In advisory locking, the file system does not force anything: the requester can see the status of the lock and proceed anyway. So mandatory locking denies access outright; advisory locking just informs.
The real-world rule of thumb: Windows operating systems use mandatory locking — once a process holds an exclusive lock, the OS physically prevents other processes from opening the file, whether or not they asked about locks. Unix/Linux systems use advisory locks — the OS shows the lock status, but a program that ignores the lock can still access the file. A developer writing for a mandatory-locking system must release locks quickly, or they can freeze other processes out of the file for no good reason.
14.2.6 Worked Example: From touch to a Real File
Walk the whole lifecycle of a file called report.txt:
- Step 1:
touch report.txt— the OS creates an empty file namedreport.txt. Nothing inside: no data, no content. Abstract. The directory gets a new entry pointing to the file's attributes. - Step 2: open the file with an editor, type contents, save. The file now holds real data in the file system, and its size attribute reflects what was written. Concrete. If the editor wrote 500 bytes, the size attribute shows 500 bytes.
- Step 3: the file system tracks the open, returns a descriptor, and records the open in the per-process and global open file tables. The global entry's open count becomes 1; the per-process entry holds the position pointer (initially 0) and the access rights (read/write).
- Step 4: after reading and writing, close the descriptor; the open count decrements, eventually reaching zero so the entry can be removed.
Sense-check: if two more programs now open report.txt, the open count climbs to 3, and each program gets its own per-process entry — its own bookmark — while all three share the single global entry. Closing all three brings the count back to zero and the entry disappears.
Every file system supports six basic operations — create, write, read, reposition (seek), delete, truncate — and the lifecycle is open → operate → close. Truncation keeps the file and drops its data; deletion drops both. Open files are tracked in a per-process table (position pointer + access rights) that points into a system-wide table (shared file facts + open count), and locks control who may access a file at the same time.
14.3 File Types and File Structure
14.3.1 Identifying File Types by Extension
Most operating systems recognize different types of files, and the way we identify the file type is with the extension. The extension follows the file name and tells the system what kind of file it is:
- Higher-level language source: extension based on the language name —
.c,.cpp,.java. - Word-processing document:
.doc. - PowerPoint presentation:
.ppt. - Audio or video:
.mp3,.mp4, and so on.
Most file systems support whatever type is needed. When something gets modified in the source, it must be recompiled and then executed; the extension tells you which language and which toolchain applies.
The name is split into two parts — the name and the extension, separated by a period. On MS-DOS systems only a few extensions were recognized by the OS itself (.com, .exe, .bat), and applications treated the rest as hints: an assembler expects .asm files, a word processor expects .doc files. The extension tells the OS (or the application) what operations are sensible on the file, and it tells you which compiler to invoke when the source changes.
14.3.2 Magic Numbers and Platform Behavior
On macOS, double-clicking a file does everything for you: the OS looks at the file's type attribute and opens the file name with the appropriate application automatically. On Unix there is (historically) a magic number: a value stored in the file at the first byte, which indicates what type of file it is. This magic-number mechanism has since been removed. It is the same idea that still shows up on the internet, where file types must be recognized from the content itself.
A magic number is a value embedded at the beginning of a file that identifies what kind of file it is — for example, an executable program, a shell script, or a PostScript file. Because the type is read from the content rather than from the file name, it works even when the file name carries no extension. The mechanism survives on the modern internet in the form of MIME type sniffing, where a browser or server reads the first bytes of a downloaded resource to decide how to handle it.
14.3.3 Common File Types and Extensions
The catalog of file types and their usual extensions:
| File type | Extension / notes |
|---|---|
| Executable file | runnable program |
| Object file | compiled but not yet linked |
| Source code | .c, .cpp, .java, and similar |
| Batch file | sequence of commands |
| Text file | plain readable content |
| Word-processing file | .doc, and similar |
| Library file | linked in when building programs |
| ASCII or binary file | data in either form |
| Print/view file | .ps (PostScript) or .pdf |
| Archive file | compressed collection of files |
| Multimedia file | .mov, .rm, .mp3, and similar |
With a multi-period (multi-extension) file, the extension can be .mov, .rm, or .mp3 — reading the extension tells you what kind of media it is.
14.3.4 Unix File Structure and Internal Fragmentation
Taking the Unix file system as the example: Unix supports different types of files — a directory, an executable — and everything is identified as a file. All files are stored as a string of bytes. With an editor you create the file, and the file is stored on the disk as fixed-size blocks. Because of this, when the file size is smaller than the block, the remaining space in the block is wasted — that is the internal fragmentation we have seen before, and it is always there in a block-based file system.
Work the numbers to see internal fragmentation. Suppose the block size is 512 bytes and a file holds 1,949 bytes. The file system allocates in whole blocks, so it gives the file 4 blocks: bytes. The file only needs 1,949 of those, so the last bytes inside the final block are wasted — occupied by the file but unusable by anyone else. That waste is internal fragmentation: the fragmentation is inside the block.
Sense-check: a file of exactly 512 bytes fits one block with zero waste, and a file of 513 bytes needs two blocks, wasting 511 bytes. The larger the block size, the bigger the potential waste per file.
Do not confuse the two kinds of fragmentation. Internal fragmentation is wasted space inside an allocated block (small files in big blocks). External fragmentation is free space broken into many small chunks scattered across the disk, none big enough for a large file — we meet it again with contiguous allocation. Both live in block-based systems, but they are different problems with different cures.
File types are identified by extension (with the magic number as the historical content-based alternative), and Unix treats every file as an uninterpreted string of bytes stored in fixed-size blocks. The block-based storage guarantees internal fragmentation: some space in the last block of every file is always wasted.
14.4 File Access Methods
There are different ways to access the contents of a file: sequential access, direct access, and indexed access.
14.4.1 Sequential Access
Sequential access is the method used in magnetic-tape file systems. We read each and every track and sector in order to reach the file we need. If we have to read or write from the current position, we still have to pass through everything before it — we always go through once from the beginning. The time taken to pass through the remaining space is wasted, so execution time is higher. The implementation is simple: read the next record, then the next, one by one.
Sequential access is the tape model of a file: a read next operation returns the next record and automatically advances a file pointer; a write next appends to the end of the file. Editors and compilers naturally work this way — they process a file from the first line to the last, so the traversal cost is no problem for them. The cost shows up only when you need something in the middle: to reach record 900 you must first pass records 1 through 899.
14.4.2 Direct Access
Direct access is random access. With a block number we can go straight to block : read the information from the block, or write information into it, or position to that block and continue with the next position from there. No passing through intermediate blocks — we read block directly and then move on. This removes the wasted traversal of sequential access.
Direct access is the disk model of a file: the file is a numbered sequence of blocks, and the operating system can read or write any block in any order. Instead of read next we say read n where is the block number (relative to the start of the file), or we position to n and then read. Databases rely on this: an airline reservation system stores flight 713's seats in block 713 of a file, so a query jumps straight there — no scanning of earlier flights.
14.4.3 Indexed Access
Indexed access (also called related-file access) keeps a separate index file. The index file has a key that identifies each and every record, and a value — in the sense of an address — that takes you to the particular location where the record's information is present. The disadvantage is the overhead: the index file itself occupies some space, and the remaining space holds the records or files in the system. Also there are two accesses instead of one — first access the index file, then, based on that, access the record — so the time taken is doubled.
Work a small retail-price example. A file lists products as 16-byte records (a 10-digit UPC plus a 6-digit price), and the disk block is 1,024 bytes, so each block holds records. A file of 120,000 records occupies about blocks. To find a price without scanning, we keep an index of the first UPC of each block — about 1,875 entries — and binary-search the index to learn exactly which block holds the record, then read only that block. The index costs extra space and an extra access, but it turns a massive sequential scan into a two-step lookup.
Sense-check: without the index we might read hundreds of blocks; with it we read the index (kept in memory) plus exactly one data block.
Sequential access wastes time passing through everything before the target record; direct access removes the wasted traversal by jumping straight to block ; indexed access adds an index file (extra space, extra access) so records can be located by key. Sequential access is the simplest and matches tape; direct access matches disks and databases; indexed access trades space and time for key-based lookup. Pick by how the file will actually be used.
Three access methods: sequential (one record after another, tape model), direct (jump to block , disk model), and indexed (look up a key in an index file, then read the record). The more skipping you need, the more you gain by moving from sequential to direct to indexed.
14.5 Disk Partitions, Volumes, and File System Types
14.5.1 Partitions, Raw Disks, and Volumes
The disk is the main place where our files and file systems live. A whole disk is divided into partitions, and each partition may carry a particular file system. If a partition exists but no file system has been loaded into it — the partition has simply been formatted, or not even that — then the disk or partition is raw. So: a raw disk or partition is one with no file system in it. A partition can be thought of as a mini disk; you can think of the disk as cut into slices. Each slice can hold a file system.
The terms stack like boxes inside boxes:
- Partition (slice, minidisk): a subdivision of one physical disk. A disk can be cut into quarters, and each quarter can hold its own file system.
- Raw (unformatted) partition: a partition with no file system loaded. Used where a file system is not appropriate — for example, Unix swap space, or databases that manage their own disk format.
- Volume: any entity that contains a file system. A volume may be a subset of one device, the whole device, or even multiple devices linked together (as in a RAID set) — think of it as a logical disk.
In Windows a partition is called a volume. A volume is an entity present in the file system: each volume that has a file system also keeps track of information in a directory, or table of contents, about the files present in that file system. With two partitions you may have two different file systems with two different contents, each with its own table of contents in its own directory. Beyond these there are general-purpose and special-purpose file systems — no need to worry about those in detail.
14.5.2 Spreading a File System Across Disks
The same idea works the other way around. Take the whole disk and divide it into two partitions — or better, take one file system and two disks: the same file may be stored across the two disks, with a single directory/table of contents covering the whole thing. Both arrangements are possible: several file systems on one disk, or one file system spanning several disks.
Do not get the direction wrong. Partitioning is cutting one disk into several file systems (many small slices from one cake). Spreading is the reverse — merging several disks into one file system (one big cake made from several smaller ones, as RAID does). In both cases the volume is the logical unit the OS works with, and each volume carries its own directory or table of contents.
14.5.3 Named File System Types and Their Purposes
File types and file system types are different things. On one system you can partition the disk: in one partition have the Windows file system, in another the Unix file system. The named file system types covered here:
- tmpfs — temporary file system.
- objfs — an object-based (virtual) file system.
- cdfs — a virtual file system that contains contract information.
- lofs — loopback file system.
- procfs — gives information about all the processes present in the system.
Each type of file system has its own purpose.
These are special-purpose file systems — each exists to solve one specific job, unlike general-purpose file systems (such as ufs or zfs) that store ordinary user files:
- tmpfs — a temporary file system created in volatile main memory; its contents vanish on reboot or crash.
- objfs — a virtual file system exposing kernel objects (for example, kernel symbols) so debuggers can read them.
- cdfs — a virtual file system maintaining contract information about which processes must keep running. Standard references often spell this one ctfs (contract file system); the function is the same.
- lofs — the loopback file system, which lets one file system be accessed in place of another.
- procfs — a virtual file system that presents information on every running process as if it were ordinary files.
A disk is sliced into partitions (slices); a partition with a file system inside becomes a volume, and one file system can also span several disks. Special-purpose file systems — tmpfs, objfs, cdfs (ctfs), lofs, procfs — each serve a single purpose that its name describes.
14.6 Directory Overview and Operations
14.6.1 What a Directory Is
The directory is like a simple table. In system software you have heard of the symbol table: the symbolic operands, along with their values and addresses, are stored in the symbol table. Imagine the directory the same way — it holds the file names and the location where each file is present. The directory can be organized in different ways — that is not a problem, the system takes care of it.
A symbol table in a compiler or assembler translates symbolic names (a variable called total) into their values or addresses. The directory plays the same role for the file system: it translates a human-readable file name into the file's directory entry, which in turn points to the file's attributes and the location of its data. Whatever internal organization the directory uses — a simple list, a sorted list, a hash table — the user never sees it; the system handles it.
14.6.2 Directory Operations
Whatever the file system, the directory supports a standard set of operations:
- Search for a file.
- Create a file (or directory) entry.
- Delete a file (or directory) entry.
- List the directories: go inside and check the subdirectories or files.
- Rename a file or directory.
- Traverse the directory structure.
With respect to Windows you can do creating, deleting, and so on by clicking; on Unix you just type a command and it is done. Typing commands is somewhat easier once you are used to it.
These six operations map directly onto everyday actions. Search is how ls finds what you asked for, create adds a new entry when you save a new file, delete removes an entry, list is what a folder window shows, rename changes the name shown, and traverse walks the whole tree — which is exactly what a backup program does when it copies every file to tape.
14.6.3 What Directory Organization Must Provide
Whatever the directory organization, it should provide:
- Efficiency and convenience — it should be convenient for the users; if I click, I should get my files.
- Naming without conflict — the system should not allow two different files with the same name to coexist where that would conflict. But when two different users share the system (in a network, for example), the users should be allowed to use the same name for their own files — each user's files are different even though the names match.
- Grouping — the user should be able to group files logically: all the Java programs in one folder, all games in a separate folder, arranged as the user wishes.
- Locating files — being able to locate a file is very important; click and get it, and everything is easier.
The naming rule has two sides. Within one user's space, two files with the same name must not coexist — that would be ambiguous. Across users, the same name is perfectly fine, because each user's files live in their own directory. The organization must satisfy both: uniqueness where it matters, freedom where it does not.
14.6.4 Student Questions and Answers
Q: Any doubts on the directory overview so far?
A: No doubts; the class moved on to the directory structures.
The directory is a symbol table for the file system: it maps file names to their locations. It supports search, create, delete, list, rename, and traverse, and a good organization gives efficiency, conflict-free naming, grouping, and easy locating of files.
14.7 Directory Structures
There are several directory structures, in increasing order of capability: single level, two level, three level, tree level, acyclic graph, and general graph.
14.7.1 Single-Level Directory
In the single-level structure, a directory is directly linked to its files: go into the directory and the files are right there. There are no multiple levels to pass through in order to reach a file. The efficiency is direct — click the directory and get the files. The disadvantage is the flip side of that simplicity: if something happens to this particular directory — it gets corrupted, or wrongly deleted — then the files become inaccessible. That is the single-level risk: one directory, and losing it loses access to its files.
The single-level directory is the simplest possible organization: all files live in one directory, so every file name must be unique in the whole system. If two users both call their program prog2, the rule is violated. It also fails at scale — with hundreds of files, remembering all the names becomes a burden. Its one virtue is speed: no levels to walk through.
Single point of failure. In a single-level directory, the directory is the file system's index. If it is corrupted or wrongly deleted, the files under it become inaccessible — the mapping from names to locations is gone. This is the essential drawback that motivates every richer structure that follows.
14.7.2 Two-Level Directory
The two-level structure introduces two levels of indirection. At the top is the master file directory (MFD), which has an entry for every user; each user has their own directory, the user file directory (UFD), which is the next level, and through the UFD the user accesses their files.
The advantages: each user has their own space (partition) and can store their own files; and two users can use the same file name without conflicting — the same name in two different users' spaces is fine. A failure in one user's directory does not affect the next user.
The disadvantage: coordination. When users work together in a company as a group, they need some common file or directory shared between them, and that facility is not there in the two-level structure. User isolation comes at the price of no sharing, and no coordination among the users working in an organization.
The MFD is indexed by user name or account number, and each MFD entry points to that user's UFD. When a user logs in, the MFD is searched; from then on, only that user's own UFD is searched for file names. So user A's test and user B's test are different files, and a delete in A's UFD can never touch B's file. To name another user's file, you must write the path — for example /userb/test — which is a path through the tree from the MFD down to the file.
14.7.3 Three-Level Directory and Path Notation
To represent a file present in a particular directory, the OS maintains a path. Usually each and every directory of the file is separated by a slash — that is the Unix convention. VMS, by contrast, uses a letter for the volume and square brackets for the directory specification. In VMS the volume is an entity that contains the directory information: a volume letter, then square brackets holding the directory and subdirectory names — for example a directory SST with a subdirectory JDAC, the two separated by a dot — and after the brackets the file name. So the VMS path shape is volume letter + [directory.subdirectory]filename.
The confirmed VMS syntax is exactly the shape described in the lecture: u:[sst.jdeck]login.com;1 — the volume letter u, then square brackets holding the dot-separated directory and subdirectory names, then the file name, then a version number after the semicolon. Unix uses the alternative convention: components separated by slashes, with no volume letter at all — /home/jane/report.txt means: start at the root /, go into home, then jane, and find report.txt.
The two systems differ on one point of principle. In VMS the volume name is always a part of the directory name — the path literally begins with the volume letter. In Unix the path always starts with the root, then the directory names — the volume is not part of the path; instead, each volume is mounted somewhere inside the tree, so the volume boundary is invisible to the user. For example, with home as one of the directories, we have /home, and inside that some directory, and inside that the file. Paths can be set and commands run from wherever we are — everything can be done with respect to the operating system.
14.7.4 Tree-Level Directory
The tree-level directory is a tree: the root at the top, the files at the bottom. The root is divided into many directories, under which are many subdirectories. Subdirectories may hold files directly, or point to another subdirectory, which in turn points to another — many levels. The directories in the middle are the internal nodes; where the structure ends and the files live are the leaves. The tree ends at different places — some branches end early, some go deep — so there are different levels present in one tree.
We can create a directory inside a directory, hold files inside a directory, and put another directory inside that with its own files. This is the structure in Unix systems.
The tree is the two-level directory extended to arbitrary height — it is also the structure you actually use on Unix and Windows. Each process has a current directory; an absolute path starts at the root and walks down (for example /home/jane/prt/first), while a relative path walks from the current directory (for example prt/first when you are already in /home/jane). Every file in the tree has a unique absolute path name.
14.7.5 Worked Example: Unix Commands for Files and Directories
Creating files has many ways:
The everyday Unix commands, matched to the operations they perform:
- For a text file,
cat filename— the stated way to create a text file by name. The command referred to iscat: typingcat > filenametakes the text you type and writes it intofilename, whilecat filenamealone displays the file's contents on screen. - For a program, use the
vioremacseditor — whichever editor is preferable, that is how the program file is created. - To remove a file:
rm filename. - To create a directory:
mkdir dirname.mkdircreates a directory under the current directory; writing./(dot slash) in the path explicitly says "under the current directory". The standard meaning:mkdir <name>creates a new subdirectory with that name inside the current directory — for example,mkdir mailcreates a directory calledmailright where you are standing.
Deleting a directory: first we have to delete all the contents inside it, and only then can we delete the directory itself. In MS-DOS you also delete the contents and whatever subdirectories exist manually. In Unix you additionally have the option to delete everything at a stretch — one command removes the directory with everything inside it.
Sense-check: rm on a directory refuses to remove it while it still holds files, forcing you to empty it first (the safe default); the recursive delete (rm -r style) does the whole job in one command but is dangerous — a mistyped command can erase a large subtree.
14.7.6 Acyclic Graph Directory
The acyclic graph is a graph structure that will not create any cycle — the name says it: acyclic, no cycle. Some nodes are connected to each other, but no connection forms a cycle. The advantage is sharing: two users working in two different directories can point to the same file, just pointing to a particular location. When one user updates the file, the other also gets the updated content, because it is the same file.
The flip side: if one user deletes the file, the other may no longer be able to access it — the pointer becomes a dangling pointer. That problem exists in the acyclic graph and should not be allowed to happen.
Naming flexibility also improves: inside one directory you cannot have two subdirectories with the same name, but in two different directories you can have subdirectories with the same name — that is allowed here.
A shared file is not two copies. With two copies, each user edits their own version and changes never cross over. With a shared file, there is only one actual file, and both directory entries point at it — so one user's update is immediately visible to the other. Unix implements this with links (hard links add a second directory entry counting toward the inode's reference count; symbolic links are indirect pointers that resolve through a path name).
The dangling-pointer trap: if sharing is implemented by pointers and the file is deleted, the other user's pointer still exists but leads nowhere — it dangles. The fix used by Unix is the reference count: the file's inode counts how many directory entries point to it, and the file's space is only freed when the count reaches zero. With hard links, deleting one link decrements the count; the file survives as long as any link remains.
14.7.7 General Graph Directory
The general graph directory is the generalization of the tree-level and acyclic graph structures. There is a root at the top, internal nodes below it, and leaves where the actual files are. The same directory may be shared by two different users, and the same subdirectory name present in two different directories can share a particular file. These are the advantages of the generalized form.
The general graph adds cycles: a directory can now contain a path that loops back to itself. Cycles break the simple reference-count deletion rule — the count may never reach zero even when the file is unreachable — and they can make searches loop forever. The textbook cure is garbage collection: mark everything reachable from the root, then collect all unmarked space. Garbage collection on disk is slow and rarely attempted, which is why systems usually stop at the acyclic graph, where the reference count works cleanly.
Directory structures grow in capability in a fixed order: single level (all files, one directory, single point of failure) → two level (MFD + per-user UFD, isolation but no sharing) → three level (paths and path notation) → tree (arbitrary depth, absolute and relative paths, Unix's structure) → acyclic graph (sharing via links, dangling-pointer risk solved by reference counts) → general graph (cycles allowed, but deletion needs expensive garbage collection).
14.8 File System Mounting
14.8.1 What Mounting Means
Any file system first has to be mounted — only then can it be accessed. Mounting means we attach the file system to the drive: whatever drive is present in the system has to get attached, and then we call it mounted.
Just as a file must be opened before it can be used, a file system must be mounted before it becomes available to processes. Mounting attaches a file system (on a device or volume) to a mount point — a location inside the file-system name space — so that the files on it can be reached by normal path names. Until it is mounted, the file system exists on the device but is invisible to users.
14.8.2 Mount Points and Privileges
Mounting is a privileged operation: not everyone can do this — only the administrator has the right. First, the system checks whether it is a verified file system, and the current data structure keeps track of the mount points. We must find the mount point: the place where we are going to attach the particular file system. For example, there is a partition that is a volume for us — the C drive — and that is where we can attach a file system. The mount point has to be tracked first, and then the file system is attached there.
The mount procedure has three steps: (1) the OS is given the name of the device and the mount point; (2) it verifies the device holds a valid file system — it asks the device driver to read the device directory and checks that the format is the expected one; (3) it records in its in-memory mount table that a file system of a particular type is now mounted at that point. On Unix, mounting is implemented by setting a flag in the in-memory copy of the directory's inode; the flag marks the directory as a mount point and points to the mount table entry for the device.
14.8.3 Worked Walkthrough: Mounting Over an Existing Mount Point
Take the existing setup: / (root) is at the top of the Unix file system, and under it is the partition for user — user is one of the directories, under which there are many subdirectories. Separately we have a file system that is unmounted as of now: it is not attached to any volume.
Now we take that unmounted file system and superimpose it on the existing file system at the mount point user. Once attached, the mount point becomes the user directory and everything under it. What was already under that mount point gets overridden — the existing contents are hidden by the new file system we superimpose on top. We cannot see the old contents anymore.
So the procedure is: find the mount point first, then attach. For this, the privilege must be there — everything is done by the administrator.
Sense-check: on a real Unix machine this is exactly what mount /dev/sdb1 /home does — the new device's contents appear at /home, and anything that previously sat under /home is hidden (though not destroyed) until the file system is unmounted.
14.8.4 Mounted versus Unmounted File Systems
An unmounted file system is one that at present is not attached to any mount point. A mounted file system is attached to a mount point and accessible. If a mount point such as C already has some contents and some file system, attaching a new file system there superimposes it — the previous contents are no longer visible.
Two facts about mounting to keep straight:
- Superimposition hides, it does not erase. The old contents under the mount point are not destroyed — they are simply unreachable while the new file system sits on top. Unmount the new file system and the original contents become visible again.
- Mount points are tracked by the OS. The system keeps a mount table recording which file system is mounted where, and only an administrator can perform the mount. Not every directory can serve as a mount point on every system — some systems require an empty directory, others allow mounting anywhere.
Mounting attaches a file system to a mount point; only an administrator can do it, and the OS verifies the file system is valid before attaching it. Mounting over an existing directory superimposes the new file system and hides the old contents until it is unmounted.
14.9 File System Structure
14.9.1 A Layered File System
There are different file system structures: a layered structure, or a virtual file system — the Linux file system is a different way again. Generally the file system is stored in secondary storage. We map the information present there to the physical device with the help of a sector number, that is, a block number — the block within the sector — to read or write information. I/O transfers happen like this: first we should know the track number, followed by the sector number, followed by the block number, in order to access the information present in a particular block.
The file system is built as a stack of layers, each using the features of the layers below it to provide new features to the layers above. I/O is done in units of blocks (one or more sectors, usually 512 bytes each). A physical block is addressed by a numeric disk address of the form drive number, cylinder, track, sector — the disk hardware can be told exactly which chunk of the platter to read or write.
14.9.2 The Layers and What Each One Does
Each layer in the layered file system takes care of some information:
- File control block (FCB) — takes care of the storage-structure information about a particular file.
- Device driver — controls the physical device we are using. The device drivers act as the input/output path to the system; they are software device drivers that have to be installed, and with their help the I/O devices are managed. When we give a command such as "read drive 1, cylinder 72, track 2, sector 11, into the memory location", the command is given to the specific controller, the controller does all the work, and the information comes back.
- Basic file system — helps translate the information given by the device drivers.
- File organization module — organizes the files as physical blocks: where the files are present, what the logical address is, in which physical block that particular address lives, how to translate a logical address into a physical block number, which free spaces are available, and how the space is allocated or has to be allocated.
In the layered file system, each inner layer takes help from the lower-level layers: the I/O takes help from the devices, the application takes help from the I/O, and so on. If the application has to run, all the other layers must cooperate to perform the execution.
The logical file system generally holds all the information except the data present in the file: where the file is present, the file handle, the file number or location (the inode number on Unix), and the file control blocks. From the logical file system we go into the file organization, which takes care of free spaces and allocation; the basic file system takes the given command and passes it to the device drivers; the device drivers translate it to the system and the response comes back. Each layer does its own work.
Trace a request through the layers, bottom to top:
- I/O control (device drivers): the lowest layer transfers information between main memory and the disk. It is the translator — it takes a high-level command such as "retrieve block 123" and turns it into the low-level hardware instructions for the disk controller.
- Basic file system: issues generic commands to the device driver to read and write physical blocks, and manages the buffers and caches that hold file-system and data blocks.
- File-organization module: knows files and their logical blocks. It translates a logical block address (say, block 5 of file F) into the physical block number where that data actually lives, and it runs the free-space manager that hands out unallocated blocks.
- Logical file system: manages the metadata — everything about a file except its data. It maintains the directory structure and the file control blocks (inodes in Unix), and handles protection.
A command such as "read drive 1, cylinder 72, track 2, sector 11 into the memory location" goes to the specific controller, the controller does the work, and the information comes back. Each layer cooperates with the ones below it; if any layer fails, the request cannot complete.
14.9.3 Real-World File Systems
Real systems each have their own format, organization, and implementation:
- Unix uses something called the FFS (Unix Fast File System).
- Windows uses the FAT (File Allocation Table), with variations of it.
- Unix file system versions are named ext2, ext3, ext4.
- As of now many more exist: ZFS, the Google File System, and FUSE (Filesystem in Userspace) are all there.
Each named file system is a concrete instance of the layered design: UFS/FFS is the Unix file system (ext2, ext3, and ext4 are its Linux descendants), FAT is the MS-DOS/Windows table-based scheme (with FAT16/FAT32 variations and NTFS as the modern Windows file system), ZFS is Sun's large-scale file system with its own space maps, the Google File System is designed for huge distributed storage, and FUSE lets users run file systems as user-level programs instead of kernel code.
14.9.4 Virtual File System in Unix
The virtual file system (VFS), present in Unix, provides an object-oriented approach. Everything is treated as an object, with the operations we can perform on the object. The details of how an object is represented and how it is implemented are usually hidden: how it is implemented is unknown to the caller, and how it is represented is also unknown — everything sits behind the system. What we see is an interface: we ask for something and get some information, with the help of the APIs.
The schematic view: a file system interface helps the user interact with the VFS interface. Each VFS has its own local file system, and it can also point to a remote file system. The local file system points in turn to the secondary storage device. When we have to access a piece of information, first we check whether it is in the local file system; if it is there, we take it from the disk and give it to the user. Otherwise it has to be accessed through the remote file system, and that is done with the help of the remote file system type.
The VFS layer sits between the file-system interface (the open(), read(), write(), close() calls and file descriptors) and the concrete file-system implementations. It exists so that many different file systems — local, remote, of different types — can coexist behind one uniform interface. The user calls read(); the VFS routes the call to whichever file system actually holds the file, without the user ever knowing which one it was. For remote files (for example, over NFS), the VFS calls the remote-file-system protocol procedures instead.
14.9.5 The Four Linux VFS Objects
Linux is different from Unix, though Linux also has a virtual file system. Four main objects are involved:
- inode — represents the individual file.
- file — represents how many files have been opened.
- super block object — represents the entire file system information.
- directory entry (dentry) — represents an individual directory.
With these four object types, a set of operations is implemented. Each object has its own pointer that points to a function table; going to the function table gives the list of addresses of the operations we can perform with respect to that object. For an inode, the object holds the addresses of the actual functions we are going to perform, and the call goes to the particular location to access it. The same applies to the different files present in the file system.
The four objects and their jobs, precisely:
- inode object — represents an individual file (permissions, owner, access times, and pointers to the file's disk blocks).
- file object — represents an open file: the position where the next read or write will happen, and the access mode.
- superblock object — represents an entire mounted file system: its device, block size, and free-block and free-inode counts.
- dentry object — represents an individual directory entry: the link between a name and the inode it names.
The object-oriented trick is the function table. Every object contains a pointer to a table listing the addresses of the operations that object type supports (open, read, write, and so on). The VFS calls read() on an object by jumping through the table — it never needs to know whether the inode stands for a disk file, a directory, or a remote file. The right function for that object is always in the same slot of the table.
The file system is a stack of layers — device drivers (I/O control), basic file system, file-organization module, logical file system with the FCB — each serving the layer above. Real systems instantiate it as FFS/ext (Unix), FAT/NTFS (Windows), ZFS, the Google File System, or FUSE. The VFS is the object-oriented glue that lets many file systems hide behind one interface, using four Linux objects — inode, file, superblock, dentry — each with a function table of operations.
14.10 File System Implementation and In-Memory Structures
14.10.1 Boot Control Block and Volume Control Block
Whenever a file system is going to be implemented, the first thing executed is the boot control block: it controls the boot information and is the first block of the volume. Then there is the volume control block, which contains the super block and the master file table. The super block holds the entire file system information. The master file table holds information about the total numbers: how many blocks are free, how many blocks are available, what the pointer to a particular block is, and which block is free.
Two on-disk control blocks start every file system:
- Boot control block (per volume): contains the information needed to boot an operating system from that volume; typically the first block of the volume. In UFS it is called the boot block; in NTFS the partition boot sector. If the disk does not hold an operating system, the block can be empty.
- Volume control block (per volume): holds the volume's details — number of blocks in the partition, block size, free-block count and free-block pointers, free-FCB count and FCB pointers. In UFS this is the superblock; in NTFS this information lives in the master file table (MFT). The superblock carries the whole file system's vital statistics: how many blocks exist, how many are free, and where the free blocks are.
14.10.2 The File Control Block
With respect to each and every file, we refer to the file control block (FCB), which gives detailed information about that file:
- The inode number.
- The permissions.
- The size and the dates — a file system keeps three dates: when the file was created, when it was last accessed, and when it was last modified; these dates are updated.
- Who is the owner of the file and which group it belongs to.
- The access control — a list with respect to every person, be it owner, group, or others.
- The file size and how many data blocks it uses.
- The pointers to the particular data blocks.
Everything is present inside this file-system structure.
The file control block (the inode in most Unix file systems) is the per-file metadata record. It is the file's identity card: a unique identifier (the inode number) links the FCB to its directory entry; permissions and access control decide who may read, write, or execute; the three timestamps (created, last accessed, last modified) support backup and monitoring; and the block pointers list where the file's data physically lives on disk. On NTFS the same information is stored as a row in the master file table, using a relational structure with one record per file.
14.10.3 In-Memory File System Structures
In-memory structures connect the user's call to the data. If the user issues an open on a file name, control transfers to the kernel. The kernel searches the directory structure for the entry of the particular file. That entry takes us to the location where the file control block with all the information lives. If the directory is not already in memory, it first has to be brought from secondary storage, and then the file control block is searched.
For reading: if the user is going to read some information, first the open file table is checked. With respect to each and every process, an entry is made when a file has been opened — an entry in the process's open file table — which takes a pointer to the system-wide (throughout the file system) open file table entry, indicating how many processes have opened the particular file. From there we get the exact location of where the data blocks are present in secondary storage.
When a file is opened, the in-memory structures are built up in this order:
- The
open()call passes a file name to the logical file system, which first searches the system-wide open-file table — if another process already has the file open, we reuse its entry instead of reloading from disk. - If not already open, the directory structure is searched (with parts cached in memory); once the file is found, its FCB is copied into the system-wide open-file table, which also tracks the number of processes with the file open.
- An entry is made in the per-process open-file table: it holds a pointer to the system-wide entry, the current position pointer for the next read or write, and the access mode.
- The
open()call returns a pointer into the per-process table — the file descriptor/handle — and every later I/O operation goes through that pointer, with no further directory searching.
The other in-memory structures are the mount table (which volumes are mounted), the directory-structure cache, and buffers holding blocks being read or written.
14.10.4 From Open Call to Data Blocks
To know about a particular file: first the information is brought from the file control block; the FCB knows how many data blocks are being used and the location of each and every data block. Once we get the information from the file control block, it directly takes us to the data blocks. That is how the in-memory file system structure works.
Trace the full journey of a read request, end to end:
- A process calls
read(fd, buffer, n). - The kernel follows
fdinto the process's per-process open-file table entry: it finds the current position pointer and the pointer to the system-wide entry. - The system-wide entry holds the copy of the FCB (the inode), which lists the addresses of the file's data blocks.
- The kernel asks the file-organization module to convert the logical block number (computed from the position pointer) into a physical block number.
- The basic file system and device driver read that physical block from secondary storage into memory, and the requested bytes are copied into the process's buffer.
Sense-check: every step is a pointer chase — per-process table → system-wide table → FCB → data blocks — which is why an open file costs so little to use after the first open.
Common confusions about the FCB and the tables:
- The FCB is metadata, not data — it describes the file; the data lives in the data blocks it points to.
- The per-process table entry is private to one process (its own position pointer); the system-wide entry is shared by all processes that have the file open, which is why the open count lives there.
- The directory entry is not the FCB — the directory entry (name + inode number) points to the FCB. Three distinct structures: directory entry, FCB, data blocks.
Implementation of a file system starts with the boot control block and the volume control block (superblock/MFT), and every file gets an FCB (inode) holding its identity, permissions, dates, and block pointers. In memory, the open-file tables — per-process and system-wide — carry the open call through to the data blocks, so that after the first open, every read and write is a simple pointer chase.
14.11 Partitioning, Boot Block, and Mounting at Boot
14.11.1 Boot Block and Root Partition
Whenever we do a partition: the partition can be a volume, or a volume can have two different partitions. The partition can be raw — without any file system — or it can have a boot block. The boot block is only a single block; that is enough to load the kernel from the file system. There is also the root partition: from the root partition we can have other partitions and other file systems.
A partition has two possible states: it can be raw (no file system — used for swap space or databases that manage their own format) or cooked (formatted with a file system). A cooked partition may carry a boot block — a single block, usually the first block of the volume, holding the code that boots the operating system. The boot information must live outside the file system's format because at boot time the file-system code is not yet loaded; the boot block is a plain sequence of blocks loaded as an image into memory, and a boot loader reads it, then loads the kernel from the file system.
14.11.2 Mounting at Boot and Consistency Checks
The root partition is mounted at boot time; the other partitions can be mounted either automatically or manually. Why do this at boot? First, the file system consistency is checked. You have seen it when the system is booting: booting information scrolls by while all the folders and file systems are checked, and only then is access to the file system allowed.
The root partition is special: it contains the operating-system kernel and is always mounted at boot time — the system cannot run without it. Other volumes are then mounted automatically at boot (from a configuration file listing devices and mount points) or manually later. As part of each mount, the OS verifies that the device contains a valid file system by reading its device directory and checking the format.
Why consistency is checked at boot: a crash can leave the on-disk structures inconsistent — a directory updated but the free-block count not, an FCB allocated but the directory not pointing to it. So before access is allowed, a consistency checker (fsck on Unix, chkdsk on Windows) compares the directory structures with the data blocks on disk and repairs what it can. That is the "checking" you see scrolling past during boot.
Partitions are raw (no file system) or cooked (file system, possibly with a boot block). The boot block is a single block that loads the kernel; the root partition holds the kernel and is mounted at boot, after the file system's consistency has been checked — which is why boot time shows the file systems being verified before access is granted.
14.12 Contiguous Allocation
14.12.1 How Contiguous Allocation Works
Now we know the file system, the directory structure, and the file system structure; the remaining question is how the files are allocated in the disk blocks. The first method is contiguous allocation. With a file stored contiguously, the blocks are occupied one after another — consecutively. This method is best in some cases because it is very simple: start from the first block and access all the blocks, no problem. If we know the block number and how many blocks are required for the allocation, the allocation is done by the operating system.
Contiguous allocation requires each file to occupy a set of consecutive blocks on the disk. A file that is blocks long and starts at block occupies blocks . The directory entry for each file stores just two numbers: the address of the starting block and the length of the allocated area. Access is fast: for sequential access the system remembers the last block read; for direct access to block , it jumps straight to block . Because consecutive blocks are physically adjacent, the disk head barely moves — that is why it is fast.
14.12.2 The Growth Problem: Compaction and Downtime
The problem comes when the file is going to be extended. Instead of the file staying at, say, 1 MB, it grows by another 1 KB. Now the space that was allocated is not enough. We cannot put the remaining contents into some other block — with contiguous allocation that is not possible; the file must stay in one consecutive stretch. So we have to move the file to a free space big enough to hold the contiguous allocation again. This moving is compaction — it is also downtime: the time taken to move the file is high, and it is a cost-consuming process.
The growth problem. Because the file must remain one consecutive stretch, extending it in place is impossible whenever the neighboring blocks are already taken. Two bad outcomes follow:
- Finding space: free space gets broken into little chunks (external fragmentation); if no chunk is large enough, the request fails.
- Compaction: to fix fragmentation, the whole file system is copied to another disk, freed, and copied back into one big contiguous hole. Compaction costs time — hours on large disks — and during it the file system is typically offline: that is downtime. This is why contiguous allocation suits files of fixed, known size (a read-only file, an image) and is painful for files that grow.
Additionally, the file's final size must be known at creation time. Overestimate, and the wasted space is internal fragmentation; underestimate, and the file cannot grow in place.
14.12.3 Worked Example: A Directory Table of Blocks
The directory may hold, for each file, the file name, the starting block number, and how many blocks are used:
| File | Starting block | Blocks used |
|---|---|---|
| first file | 0 | 2 — blocks 0 and 1 |
| file f | 6 | 2 — blocks 6 and 7 |
The first file starts at block 0 and occupies only 2 blocks, blocks 0 and 1. File f starts at the 6th block (block 6) and uses only 2 blocks, blocks 6 and 7. This is contiguous allocation as seen in the directory, and the growth problem we discussed applies to every such entry: extend the file, and the whole contiguous stretch may have to be moved.
Work the directory table step by step:
- File "first file": starting block 0, length 2 → occupies blocks 0 and 1. To read its second block, the system computes and reads block 1 directly.
- File f: starting block 6, length 2 → occupies blocks 6 and 7. Direct access to its first block is .
- Growth scenario: suppose file f grows and needs a third block. Block 8 is free, but block 5 is also occupied by something else — file f cannot simply extend to block 8, because then blocks 6, 7, 8 are still consecutive... but if block 5's owner does not matter, the real problem appears when the neighbor is taken: file f would have to be moved to a fresh consecutive stretch big enough for all 3 blocks (say blocks 20, 21, 22), which is exactly the moving/compaction cost described above.
Sense-check: the directory table needs only starting block + length per file — minimal bookkeeping — but that simplicity is exactly why growth forces relocation.
Contiguous allocation stores each file in one consecutive stretch of blocks, described in the directory by start + length. It is simple and fast for both sequential and direct access, but it cannot extend a file in place: growth forces moving the whole file (compaction), which costs time and downtime, and free space suffers external fragmentation.
14.13 Linked Allocation and the File Allocation Table
14.13.1 How Linked Allocation Works
In linked allocation, each file is a linked list of blocks: from one block to another, we have a pointer (the link). There is no need for compaction and no external fragmentation. Adding a new block is easy: extend the last block of the file to point to the next block that is free.
The file allocation table (FAT) sits at the beginning of the volume and is indexed by block number — for a given block, the table tells you the next block in the chain. With the linked list we can move faster from one block to the other.
Linked allocation solves every problem of contiguous allocation: each file is a linked list of disk blocks that may be scattered anywhere on the disk. The directory entry stores only the pointer to the first block (and often the last). Each block carries a pointer to the next block in the file. Because blocks can be anywhere, any free block satisfies a request — so there is no external fragmentation and no need to declare the final size or to compact. The file allocation table (FAT) is a variation used by MS-DOS and OS/2: a table at the start of the volume has one entry per disk block, indexed by block number; the entry for block holds the number of the next block in the chain, a value of 0 means the block is free, and a special end-of-file value marks the chain's end.
14.13.2 Worked Example: A Block Chain (File g)
The directory entry tells you in which block the file starts and where the chain ends. Example: a file called g starts at block 9. From block 9 a pointer points to block 16, then a pointer points to block 1, then to block 2, and from there to the last block. The end of the block chain is mentioned as -1 — the minus one marks the end of the chain. By following the pointers we can understand how many blocks are used.
Chain for file g: 9 → 16 → 1 → 2 → -1 (end).
Follow the chain of file g block by block:
- The directory entry says the file starts at block 9.
- Block 9 contains a pointer to block 16.
- Block 16 contains a pointer to block 1.
- Block 1 contains a pointer to block 2.
- Block 2 contains -1, the end marker.
So file g occupies 4 data blocks: 9, 16, 1, and 2 — scattered across the disk in a non-consecutive chain. To read the whole file sequentially, we follow the links; to jump to, say, the 3rd block, we must walk from block 9 through block 16 to reach block 1 (this is why linked allocation is inefficient for direct access).
Sense-check: counting the links from start to end marker tells us the file uses exactly 4 blocks, even though the directory entry itself never lists them all.
14.13.3 Worked Example: The Windows FAT
This is how the FAT-type allocation works in Windows. The directory has an entry per file with the starting block mentioned; it points to that particular block. If the entry mentions 0, it means that as of now no block has been allocated for this file. When we allocate a block for this file, we remove the 0 and attach the entry to the particular address. From there the continuation goes to the next location: from that block the FAT chain goes to 339, and from 339 to some other location, and so on, until it reaches the end marker. That is the FAT allocation done in Windows.
Work a Windows FAT example with concrete numbers. Suppose a file's chain runs through disk blocks 217, 618, and 339:
- The directory entry for the file gives the starting block: 217.
- The FAT entry indexed by 217 contains 618 — the next block in the chain.
- The FAT entry indexed by 618 contains 339.
- The FAT entry indexed by 339 contains the end-of-file value, closing the chain.
- A FAT entry of 0 means that block is free — when a new file is created, its directory entry initially points to a free block (the 0 is replaced by the allocated block's address), and allocating a new block to an existing file means finding a 0 entry, linking the old end-of-file entry to the new block, and marking the new block as the new end.
Sense-check: instead of storing a pointer inside every data block, the FAT concentrates all the chain pointers in one table at the start of the volume — the chain 217 → 618 → 339 → EOF is entirely described by three FAT entries.
14.13.4 Worked Example: General Linked Allocation
A more general linked allocation works the same way with many files. The directory mentions only the starting block of each file. For the first file, the starting block alone is mentioned: 19. Going to block 19 shows the list of blocks where the particular file is present — blocks 1, 9, 10, 16, all in the chain. The rest of the entries are -1, which means there are no other blocks with respect to this file.
First file chain: 19 → (blocks 1, 9, 10, 16) → -1.
Expand the general example. The directory lists many files, each with a single starting block. Take the first file, which starts at block 19:
- Block 19 is in the chain — but the chain is read by following pointers: 19 points to 1, 1 points to 9, 9 points to 10, 10 points to 16, and 16 points to -1.
- The blocks of this file are 19, 1, 9, 10, and 16 — five blocks in all.
- The remaining entries in the chain (those showing -1) mean: no further blocks exist for this file.
The same pattern repeats for every file: the directory gives one starting block per file, and walking the pointers from that block enumerates the file's blocks until the -1 end marker. This is exactly how linked allocation scales to many files at once.
Sense-check: starting blocks are all the directory stores — everything else is discovered by following links.
14.13.5 The Broken-Pointer Problem
The problem with linked allocation: we have to maintain the pointers, and that maintenance is itself an overhead. If one pointer gets disconnected, we no longer have any way to get the particular block — the information in that block and everything after it cannot be accessed. The pointer overhead and the single point of failure are the disadvantages of linked allocation.
Three costs of linked allocation to remember:
- Pointer overhead: if each 512-byte block carries a 4-byte pointer, about 0.78% of the disk is used for pointers rather than data. (Grouping blocks into clusters of four cuts this waste fourfold.)
- Broken-pointer failure: the chain is the file. If any pointer is lost or damaged — an OS bug or a disk error — the block it should have pointed to and everything after it becomes unreachable. The rest of the file is orphaned. This is the single point of failure of linked allocation.
- No efficient direct access: finding the -th block means walking pointers from the start, one disk read per pointer — far too slow for random access. The FAT cures this partially by putting all pointers in one place, so the location of any block can be found by reading the table.
Linked allocation stores each file as a chain of scattered blocks with pointers (or FAT entries) linking them: no external fragmentation, easy growth, no compaction. Its costs are pointer overhead, the broken-pointer single point of failure, and slow direct access. The FAT variation concentrates the chain in one table indexed by block number, with 0 = free and an end-of-file value ending each chain.
14.14 Indexed Allocation and the Unix Inode Scheme
14.14.1 How Indexed Allocation Works
In indexed allocation, we create an index table. The table has one attribute as the key, and next to it the address, the pointer to the particular location. With the help of the key we can directly access the particular data block that is pointed to and get the information. Access is dynamic, and there is no external fragmentation — but the problem is the overhead of the index block itself.
Indexed allocation brings all of a file's pointers together into one place: the index block. Each file has its own index block — an array of disk-block addresses — and the -th entry of the index points to the -th data block of the file. The directory stores only the address of the index block. To read the -th block, the system reads the -th pointer from the index and jumps straight to that data block. Because any free block on the disk can serve any file, there is no external fragmentation and no compaction — the same benefits as linked allocation — and unlike linked allocation, direct access is efficient, since all pointers sit in one table. The trade-off is the overhead of the index block itself: even a tiny file (one or two blocks) must own a whole index block.
14.14.2 Access Cost of the Index
Suppose the block size is 512 bytes. To map the logical to the physical — to find where the data block is present — we first access the index: the displacement into the index table has to be computed, and from the displacement we go to the block where the information is present. On the whole, at least one block access is required (the index table) and at most two. The capacity of the index table depends on the index table size and the data block size: an index block can hold roughly as many entries as fit in the block:
where bytes is the block size and is the pointer size in bytes. With one level of indexing, a lookup costs:
so the minimum is one access (the index block) and the maximum is two (the index block plus the data block).
The standard pointer size is bytes (a 32-bit disk address). So with a 512-byte block, an index block holds
and with 1,024-byte blocks it holds pointers — the classic Unix numbers. The lookup procedure: compute the displacement (which entry of the index corresponds to the desired logical block), read the index block (access 1), then read the data block the pointer names (access 2). If the index block is already in memory — which is common for small, frequently used files — the lookup costs only one disk access.
Work a concrete indexed lookup with 512-byte blocks and 4-byte pointers. Suppose a file uses 5 data blocks and its index block holds their addresses: entries 0–4 list the blocks.
- To read logical block 0: displacement = 0. Read the index block, take entry 0, read that data block. Cost: 2 accesses (or 1 if the index is cached).
- To read logical block 4: displacement = 4. Same two accesses — no matter which block, the cost never depends on position, unlike linked allocation.
- The file can grow to bytes (64 KB) with this single index block before a second level is needed.
Sense-check: the same file in linked allocation would need up to 4 pointer walks; indexed allocation reads any block in at most 2 accesses.
14.14.3 Multiple Levels of Indexing
In the linked scheme with multiple indexing, the size is not limited. First we go to the first index table; from the first index table we get the displacement for the particular block, which takes us to the second index table; from the second index table we get the displacement for the block of the file where it is located. The problem: with a minimum of two levels of indexing, we need three levels of access in order to find a particular file.
The multilevel cost. A single index block can hold only pointers, which caps the file size. To support bigger files, the index itself is indexed: a first-level index block points to second-level index blocks, which point to the data blocks. With two levels of indexing, a lookup touches: first-level index → second-level index → data block — three accesses instead of two. Each extra level adds one more block access and one more table to maintain. With 4,096-byte blocks holding 1,024 four-byte pointers, two levels already cover data blocks (a 4 GB file), so most files never need more.
14.14.4 The Unix Scheme: Direct, Single-Indirect, Double-Indirect, Triple-Indirect
The Unix file system in fact does this type of indexing scheme. Either you get the data block directly with the help of the direct blocks — with 10 direct blocks you can straight away access the data block, and the data block size may be up to 1024 bytes — or you go through index tables:
- 10 direct blocks: straight access to the data block. With block size 1024 bytes, the 10 direct blocks serve files up to:
- Single indirect: we do not go to the data block first; we go to one table — an index table — and from there to the data block, in order to access the data.
- Double indirect: two levels of indirection — two index tables before the data block.
- Triple indirect: three levels of indexing before the data block.
In the double- and triple-indirect cases the time taken to access the data grows, and we have to maintain the three levels of indexing, which is an overhead. So the trade-off is: direct blocks give instant access, and each extra indirect level adds another table visit in exchange for supporting much larger files.
The Unix inode combines the schemes: a fixed set of pointers in the inode, some direct and some indirect. With 1,024-byte blocks and 4-byte pointers (256 pointers per index block), the classic numbers are:
The design insight: most files are small, and for them the 10 direct blocks give instant access with zero table visits. Indirection kicks in only as the file grows, each level multiplying capacity by 256 at the cost of one extra table read per access. Small files are fast; huge files are merely possible. (Some systems use 12 direct pointers — for example FreeBSD — but the principle is identical.)
14.14.5 Student Questions and Answers
Q: Should we continue with the allocation methods now, or stop here?
A: Enough for today; we continue in the next session with free-space management, and after that come the mass storage structure and disk management.
Indexed allocation stores all of a file's block pointers in an index block: no external fragmentation, direct access in at most two accesses, at the cost of index-block overhead. Multi-level indexing removes the size limit but adds an access per level. The Unix inode combines 10 direct blocks with single-, double-, and triple-indirect blocks, giving instant access to small files and a theoretical ceiling around 16 GB with 1 KB blocks — with free-space management up next.
Exam Guidance Summary
No mark distribution, question pattern, or study references were given in this session. What the session did establish:
- The unit scope, which is exam-relevant: disk directory structure, file mounting, file system implementation, and the allocation methods (contiguous, linked/FAT, indexed), followed by mass storage structure and disk scheduling.
- A problems session is scheduled ahead — an upcoming problems session was announced during the directory discussion.
- The core distinctions to know: truncation versus deletion, mandatory versus advisory locking, mounted versus unmounted file systems, sequential versus direct versus indexed access, and the strengths and weaknesses of each allocation method.
Exam note: the question material for this unit covers the whole roadmap — directory structure, mounting, file system implementation, and allocation methods — then mass storage structure and disk scheduling in the sessions to come. The distinctions most likely to be tested are the contrast pairs: truncate vs delete, mandatory vs advisory locking, mounted vs unmounted, sequential vs direct vs indexed access, and contiguous vs linked/FAT vs indexed allocation. For the allocation methods, be ready to state each method's directory bookkeeping, its access cost (number of block accesses), and its failure modes (compaction for contiguous, broken pointer for linked, index overhead for indexed).
Key Industry Applications
- Real-world: Windows organizes files under drives and folders, and calls a partition a volume; Windows uses the FAT (File Allocation Table) type of linked allocation, with a chain per file and 0 meaning "no block allocated".
- Real-world: Unix/Linux treats directories as files, identifies every file with an inode, and uses the FFS (Fast File System) with the ext2, ext3, and ext4 file system variants; its indexed allocation uses 10 direct blocks plus single-, double-, and triple-indirect blocks with 1024-byte blocks.
- Real-world: VMS uses a volume letter and square brackets in its path notation (for example
volume:[dir.subdir]file), while Unix separates path components with slashes (/home/.../file). - Real-world: macOS uses the file type attribute to open a file with the right application on double-click; Unix historically used a magic number in the first byte of the file for the same purpose.
- Real-world: ZFS, the Google File System, and FUSE are modern file systems with their own format, organization, and implementation.
- Real-world: tmpfs, objfs, cdfs, lofs, and procfs are special-purpose file system types, each with its own purpose (temporary, object-based, contract, loopback, and process information respectively).
- Real-world: common extensions such as
.c,.cpp,.java,.doc,.ppt,.mp3,.mp4,.ps,.pdf,.mov, and.rmidentify file types across systems.
The file system ideas in this lecture are the ones running under every everyday system: Windows' FAT chains and drive volumes, Unix/Linux inodes with the ext family, VMS and Unix path conventions, macOS double-click type resolution, and the special-purpose file systems (tmpfs, procfs, and the rest) that modern operating systems expose alongside ordinary storage. When you format a USB drive, mount a network share, or check disk space, you are operating this lecture's machinery.
OS Lecture 14 notes · File Systems
Sections Breakdown
Covers: 14.1.1 Where We Are Going, 14.1.2 What a File Is, 14.1.3 File Attributes.
Covers: 14.2.1 The File as an Abstract Type, 14.2.2 Creating, Writing, Reading, and Repositioning, 14.2.3 Deleting versus Truncating, 14.2.4 Opening Files: Handles, Descriptors, and Open File Tables, 14.2.5 File Locking, 14.2.6 Worked Example: From `touch` to a Real File.
Covers: 14.3.1 Identifying File Types by Extension, 14.3.2 Magic Numbers and Platform Behavior, 14.3.3 Common File Types and Extensions, 14.3.4 Unix File Structure and Internal Fragmentation.
Covers: 14.4.1 Sequential Access, 14.4.2 Direct Access, 14.4.3 Indexed Access.
Covers: 14.5.1 Partitions, Raw Disks, and Volumes, 14.5.2 Spreading a File System Across Disks, 14.5.3 Named File System Types and Their Purposes.
Covers: 14.6.1 What a Directory Is, 14.6.2 Directory Operations, 14.6.3 What Directory Organization Must Provide, 14.6.4 Student Questions and Answers.
Covers: 14.7.1 Single-Level Directory, 14.7.2 Two-Level Directory, 14.7.3 Three-Level Directory and Path Notation, 14.7.4 Tree-Level Directory, 14.7.5 Worked Example: Unix Commands for Files and Directories, 14.7.6 Acyclic Graph Directory, 14.7.7 General Graph Directory.
Covers: 14.8.1 What Mounting Means, 14.8.2 Mount Points and Privileges, 14.8.3 Worked Walkthrough: Mounting Over an Existing Mount Point, 14.8.4 Mounted versus Unmounted File Systems.
Covers: 14.9.1 A Layered File System, 14.9.2 The Layers and What Each One Does, 14.9.3 Real-World File Systems, 14.9.4 Virtual File System in Unix, 14.9.5 The Four Linux VFS Objects.
Covers: 14.10.1 Boot Control Block and Volume Control Block, 14.10.2 The File Control Block, 14.10.3 In-Memory File System Structures, 14.10.4 From Open Call to Data Blocks.
Covers: 14.11.1 Boot Block and Root Partition, 14.11.2 Mounting at Boot and Consistency Checks.
Covers: 14.12.1 How Contiguous Allocation Works, 14.12.2 The Growth Problem: Compaction and Downtime, 14.12.3 Worked Example: A Directory Table of Blocks.
Covers: 14.13.1 How Linked Allocation Works, 14.13.2 Worked Example: A Block Chain (File g), 14.13.3 Worked Example: The Windows FAT, 14.13.4 Worked Example: General Linked Allocation, 14.13.5 The Broken-Pointer Problem.
Covers: 14.14.1 How Indexed Allocation Works, 14.14.2 Access Cost of the Index, 14.14.3 Multiple Levels of Indexing, 14.14.4 The Unix Scheme: Direct, Single-Indirect, Double-Indirect, Triple-Indirect, 14.14.5 Student Questions and Answers.
Exam Guidance Summary.
Key Industry Applications.
Exam Revision Notes
Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.
Files and the Scope of This Topic
Must-know: A file is a named logical unit of information storage identified by an inode (Unix/Linux) or file reference (Windows); its attributes are identifier, type, location, size, protection, and time/identification.
⚠️ Top pitfall: Confusing the human-readable file name with the OS-assigned file identifier (inode); the identifier is unique within the file system.
Self-check: Why can the data in a file survive a power failure?
Connects to: File Operations and Open Files; File Types and File Structure
File Operations and Open Files
Must-know: Six operations: create, write, read, reposition, delete, truncate. Truncate clears contents and keeps the file; delete removes the file entirely. Open returns a handle/descriptor backed by per-process and system-wide open file tables with an open count that drops to zero on the last close.
⚠️ Top pitfall: Treating truncate and delete as the same operation; forgetting that the open count only reaches zero when every opener closes the file.
Self-check: What happens to the open count when two processes open the same file and one closes it?
Connects to: Files and the Scope of This Topic; File System Implementation and In-Memory Structures
File Types and File Structure
Must-know: File type comes from the extension; historically Unix read a magic number from the first byte. Files are stored in fixed-size blocks, so the tail of the last block is always wasted — internal fragmentation.
⚠️ Top pitfall: Mixing up internal fragmentation (waste inside a block) with external fragmentation (scattered free chunks).
Self-check: A 1,949-byte file in a 512-byte block system uses 4 blocks; how many bytes are wasted?
Connects to: Files and the Scope of This Topic; File Access Methods
File Access Methods
Must-know: Sequential access reads records in order (tape model, wasted traversal); direct access jumps to block n (disk model, no traversal); indexed access uses a key-address index file at the cost of extra space and a second access.
⚠️ Top pitfall: Thinking sequential access is efficient for random lookups — reaching record 900 always passes records 1-899.
Self-check: Which access method would a database query use to find one record by key, and what two costs does it pay?
Connects to: Indexed Allocation and the Unix Inode Scheme
Disk Partitions, Volumes, and File System Types
Must-know: A partition is a slice of a disk; raw means no file system; a volume is any entity that holds a file system (part of a disk, a whole disk, or several disks). tmpfs, objfs, cdfs, lofs, procfs are special-purpose file systems.
⚠️ Top pitfall: Confusing file types (.png, .txt) with file system types (tmpfs, procfs) — they are different concepts.
Self-check: What does it mean for a partition to be raw?
Connects to: Partitioning, Boot Block, and Mounting at Boot
Directory Overview and Operations
Must-know: A directory maps file names to directory entries (like a symbol table). Operations: search, create, delete, list, rename, traverse. It must provide efficiency, conflict-free naming, grouping, and locating.
⚠️ Top pitfall: Thinking same-named files are always forbidden — they are allowed across different users' directories.
Self-check: Which operation would a backup program use to visit every file in the system?
Connects to: Directory Structures
Directory Structures
Must-know: Single-level: one directory, unique names, losing it loses the files. Two-level: MFD points to per-user UFDs, same names across users allowed, no sharing. Tree: root, internal nodes, leaves, absolute/relative paths. Acyclic graph: sharing via links, dangling pointers if deleted without reference counts. General graph: cycles break deletion, needing garbage collection.
⚠️ Top pitfall: Thinking a shared file means two copies — it is one file with two directory entries; updates cross over.
Self-check: Why does the reference count prevent the dangling-pointer problem in acyclic graphs?
Connects to: Directory Overview and Operations; File System Mounting
File System Mounting
Must-know: A file system must be mounted before access. Only the administrator mounts. The OS verifies the file system is valid, tracks mount points, and mounting over an existing directory superimposes the new file system, hiding the old contents until unmount.
⚠️ Top pitfall: Thinking mounting over a directory erases the old contents — it hides them until the file system is unmounted.
Self-check: What happens to the files under /user when a new file system is mounted at /user?
Connects to: Directory Structures; Partitioning, Boot Block, and Mounting at Boot
File System Structure
Must-know: Layers: I/O control (device drivers), basic file system, file-organization module (logical-to-physical translation, free space), logical file system (metadata, FCBs/inodes). VFS provides a uniform interface over many file systems using four objects: inode, file, superblock, dentry, each with a function table.
⚠️ Top pitfall: Thinking the logical file system stores the file data — it stores everything about the file except its data.
Self-check: Which layer translates a logical block address into a physical block number?
Connects to: File System Implementation and In-Memory Structures; Files and the Scope of This Topic
File System Implementation and In-Memory Structures
Must-know: Boot control block = first block of the volume; volume control block (superblock/MFT) = whole-file-system statistics. FCB/inode holds inode number, permissions, three dates, owner, group, access control, size, block pointers. Open flow: directory search -> FCB into system-wide table -> per-process entry -> data blocks.
⚠️ Top pitfall: Confusing directory entry, FCB, and data blocks — the directory entry points to the FCB, the FCB points to the data blocks.
Self-check: Where is the open count stored and why there?
Connects to: File Operations and Open Files; File System Structure
Partitioning, Boot Block, and Mounting at Boot
Must-know: A partition is raw (no file system) or cooked (has one). The boot block is a single block that loads the kernel; the root partition holds the kernel and mounts at boot; other partitions mount automatically or manually, after consistency checks.
⚠️ Top pitfall: Thinking the boot block must be inside the file system format — it cannot be, because the file system code is not loaded yet.
Self-check: Why is the file system consistency checked at boot time?
Connects to: Disk Partitions, Volumes, and File System Types; File System Mounting
Contiguous Allocation
Must-know: A file of n blocks starting at block b occupies blocks b..b+n-1; directory stores start + length. Growth cannot happen in place; the file must be moved (compaction), causing downtime. External fragmentation and needing the final size up front are further costs.
⚠️ Top pitfall: Thinking a contiguous file can simply grab the next free block when it grows — it cannot; the whole stretch must stay consecutive.
Self-check: Why does compaction cause downtime?
Connects to: Linked Allocation and the File Allocation Table; Indexed Allocation and the Unix Inode Scheme; File Types and File Structure
Linked Allocation and the File Allocation Table
Must-know: Linked allocation: directory stores the starting block; each block points to the next; -1/EOF ends the chain; no external fragmentation, no compaction; costs are pointer overhead (0.78% at 4 bytes/512-byte block), broken-pointer orphaning of the tail, and no direct access. FAT concentrates pointers in one table with 0 = free.
⚠️ Top pitfall: Thinking a broken pointer loses only one block — it loses that block and everything after it in the chain.
Self-check: What does a FAT entry of 0 mean, and what does the end-of-file value mean?
Connects to: Contiguous Allocation; Indexed Allocation and the Unix Inode Scheme
Indexed Allocation and the Unix Inode Scheme
Must-know: Index entries per block = B/p (512/4 = 128; 1024/4 = 256). One-level lookup costs 1-2 accesses. Two levels cost up to 3. Unix inode: 10 direct blocks serve 10 x 1024 = 10240 bytes (~10 KB); single indirect = 256 KB, double = 64 MB, triple = 16 GB with 1 KB blocks.
⚠️ Top pitfall: Forgetting that each additional level of indirection adds one more block access before the data is reached.
Self-check: With 512-byte blocks and 4-byte pointers, how many entries does one index block hold?
Connects to: File Access Methods; Linked Allocation and the File Allocation Table; Contiguous Allocation
Exam Guidance Summary
Must-know: Unit scope: directory structure, file mounting, file system implementation, allocation methods (contiguous, linked/FAT, indexed), then mass storage structure and disk scheduling.
⚠️ Top pitfall: Confusing the contrast pairs: truncate vs delete, mandatory vs advisory locking, mounted vs unmounted, sequential vs direct vs indexed access.
Self-check: What are the three allocation methods and the main weakness of each?
Connects to: Files and the Scope of This Topic; Contiguous Allocation; Linked Allocation and the File Allocation Table; Indexed Allocation and the Unix Inode Scheme
Key Industry Applications
Must-know: Windows = FAT chains + volumes; Unix/Linux = inodes + FFS/ext2-4 + 10-direct-block indexing; VMS uses volume:[dir.subdir]file; special-purpose types: tmpfs, objfs, cdfs, lofs, procfs.
Self-check: Which file system types are special-purpose, and what is each one's purpose?
Connects to: Disk Partitions, Volumes, and File System Types; File System Structure; Linked Allocation and the File Allocation Table
Was this lecture useful?
BitsNotes AI Assistant
Subject Notes AssistantConfigure AI Chat
Choose how to access the chatbotSigned in as
Powered by BitsNotes — 20 messages per day. No API key needed. Want unlimited access? Use "Bring Your Own Key" mode.
Sign in to use AI Chat
Get 20 free AI messages per day to ask questions about your lecture notes. Sign in with Google or GitHub — it takes 5 seconds.
Sign In to BitsNotesSwitch to "Bring Your Own Key" tab above for unlimited access with any OpenAI-compatible provider.