Files, Directories and Device Management
9.1 Files and Directories — The Unix View
9.1.1 Everything Is a File and Basic Operations
Hook — why does Unix say a keyboard is a file? If every object looks different — a text file, a printer, a folder — how can one set of calls handle all of them? The answer is the Unix decision to treat everything you can read or write as a file.
In Unix and Linux everything is treated as a file. The system deals with any and every object as a file, whether it looks like plain text, a device, or a folder. That single view lets the kernel use the same set of calls for many different objects. The classic statement from the textbooks is simple: a file — a sequence of bytes with no imposed structure — is the only abstraction the kernel needs at the lowest level. Meaning comes from the program that interprets the bytes, not from the kernel.
On a file we can do a small set of core actions. We can open a file, close a file, read from a file, and write into a file. The phrasing matters: we read from a file and write into a file. Opening sets up a handle, reading and writing move bytes, closing releases the handle. Those four verbs are the base of almost all file work that follows.
Everything-is-a-file, built step by step.
- A byte is 8 bits — one character for our purpose.
- A file is a sequence of bytes stored on disk or produced by a device. The kernel attaches no structure or type to it.
- A file descriptor is the integer the kernel returns on
open()— your ticket to laterread()andwrite()on that sequence. - A device file is a sequence too: typing on a keyboard produces bytes, a line printer consumes bytes, data flowing in a pipe is bytes. Even though the source looks different, the kernel sees a byte stream in each case.
So a regular file /etc/passwd, a character device /dev/tty, and a directory /home all pass through the same four calls. The uniformity is what keeps the interface small.
Think of a file like a named box that holds bytes, and the directory like a list that tells where each box lives. The file itself holds the content; the directory holds the map. Another everyday picture is a universal power socket: many appliances look different, but they all use the same plug shape. Here the plug is the open-read-write-close pattern, and everything — disk file, tape, terminal — fits it. Where the analogy breaks: real devices have timing and error behaviour that a plain disk file does not, so programs must still handle device-specific errors even though the call looks the same.
Visual intuition: picture a long tape of bytes with a moving finger. open puts the finger at the start, read advances it and copies bytes to memory, write inserts bytes at the finger, close lifts the finger. For a device file the tape is not stored beforehand — bytes are produced or consumed on the fly — but the finger idea still holds.
Scope — when the view helps and when it hides.
- Applies: kernel I/O path, shell redirection,
cat,ls,cp— all use the same file calls whether the argument is a regular file or a device under/dev. - Does not hide: permissions, file type, and blocking behaviour still differ. A directory cannot be written with a plain
write()in the same way as a text file; a terminal may block until a line is ready. The unified call does not mean identical semantics. - Assumes: byte-stream view. Modern systems add richer interfaces (memory-mapped I/O, ioctl) for cases where byte-stream alone is not enough.
9.1.2 What a Directory Holds
A directory — a folder that lists what is inside it — holds information about what is present inside that particular directory. What can be inside? Either files or other subfolders, often called subdirectories, can be present inside that directory. In implementation terms a directory is itself a file whose bytes are a table of entries, not free-form text.
File names inside a directory may be handled in two broad ways: fixed length or variable length. Fixed length means every name slot reserves the same number of bytes even if the real name is shorter. Variable length means each slot grows or shrinks with the name. Unix in particular follows the fixed length structure for directory entries, and that choice has direct limits on how long a name can be.
Everyday analogy — fixed pigeonholes vs elastic folders. Fixed length is like a wall of mailboxes each with the same sized label slot: short names waste space, long names spill over. Variable length is like hanging folders that expand to the label: no waste, but you need extra bookkeeping to find the next folder. Classic Unix chose the fixed mailbox wall to make offset math trivial.
Real-world: This same idea of treating everything as a file is why you can list devices under /dev with the same tool you use to list normal files. Running ls /dev and ls /home goes through the same directory-read logic because both /dev and /home are directories whose entries the kernel reads as tables.
Pitfalls for this idea.
- Do not think a directory contains the file bytes. It contains only the name-to-inode map; the bytes live in data blocks pointed to by the inode.
- Do not mix up fixed-length with “always 14 characters today.” The 14-character limit is the classic Version 7 layout; modern ext4, xfs and others use variable names and allow 255 characters.
- Remember the spelling:
read from,write into— examiners notice the prepositions.
9.1.3 How This Connects to What Came Before
This session continues directly from the previous one that introduced what a file is and what a directory is. The plan here is to summarize those ideas quickly and then move forward into how the system turns a path name into the bytes on disk, and how to inspect disks and devices from the command line.
The lecture flow from here is intentional: first the static layout — how a directory entry maps name to number (section 9.2) — then the bridge from name to content via inodes (section 9.3), then the dynamic search that turns a path string into an inode (section 9.4), then the cost of that search in disk fetches (section 9.5), and finally the user-visible tools df, lsblk and fdisk that let you see the same structures from the shell (sections 9.6–9.8).
Recap — 9.1 in one line: Unix treats every I/O object as a byte-sequence file accessed by open-read-write-close, and a directory is itself a file that maps names to inode numbers — the fixed-length table idea sets up the 16-byte entry you see next.
Bridge: With that map idea in mind, the next step is to open the table itself and measure its rows: 16 bytes, split 2 plus 14.
9.2 Unix Directory Entry Structure
9.2.1 The 16-Byte Entry and Its Two Parts
Hook — how does the kernel jump straight to the 5th file in a folder without scanning names? Because every row in the directory table is the same size, so position is arithmetic, not search.
Unix stores directory entries in a small table with three columns visible in the teaching slide. The first column gives the byte offset in the directory — that is, where the next item sits inside the directory file itself. The source phrasing for the reconstruction is: "the difference between various row entries in that byte offset ... is 16 bytes ... every row is of size 16 bytes" and "every row ... consists of 16 bytes ... divided into two parts ... I node number ... 2 bytes ... 14 bytes ... file name."
Formalize — the 16-byte entry, step by step.
An offset is the distance in bytes from the start of the directory file. An I-node number is a small integer that identifies the file's metadata structure. A file name field holds the characters of the name.
- Look at successive rows in the slide's offset column: offsets read . The gap is constant .
- So each row — one directory entry — must be bytes.
- The row is split into two fields: the first bytes hold the I-node number, the remaining bytes hold the name characters.
Reconstructed size relation:
where
- is the size of one directory entry in bytes,
- bytes bits hold the I-node number — a small integer that points to the file's metadata structure,
- bytes hold the file name characters under this old fixed length layout.
Variable view inline: an entry is bytes of inode number bytes of name, and the byte offset steps by each row, so offsets read .
The geometry check is simple: if each entry is bytes, then the -th entry starts at . That matches the observation that successive row offsets differ by .
The verbal audit trail for this math is kept as quoted above so the rebuild can be checked. The symbol rule here is that byte counts are in bytes, the inode number is a 16-bit unsigned integer in this classic layout, and offsets are directory byte positions.
Visual intuition: picture a ruler marked every 16 bytes. At sits entry , at entry , at entry . Each 16-byte block is cut after 2 bytes: left slice is the number, right slice is the name. Because the cut is at the same place every time, the kernel computes the address of entry without reading names — a direct calculation.
Worked example — 16-byte entry table, step by step.
Take a tiny directory that contains three files. The slide's table looks like this conceptually:
| Byte offset | I-node number (2 bytes) | File name (14 bytes, padded) |
|---|---|---|
file1.txt |
||
notes |
||
a.out |
- Entry size confirmed: bytes per row.
- Offset of entry : . So entry at , entry at , entry at .
- If you want entry 's inode, seek to byte , read 2 bytes → . Those 2 bytes are the only link to the file's content.
- Sense-check: bytes bits can represent distinct inode numbers (0 to 65535), which was the limit in this classic Version 7 design.
This fixed wall of 16-byte slots is why the byte offset column steps by 16 — it is counting slots, not characters.
9.2.2 The 14-Character Name Limit and What Happens on Overflow
Because bytes are fixed for the name, Unix under this layout restricts a file name to a maximum of characters. Anything longer will be truncated. The practical warning given is that if two files share the same first characters, the system may treat them as colliding and the older file might be replaced by the newer one, with a prompt in interactive use. In short, a truncated name is not a safe name.
Concretely, a name like my_very_long_filename.txt (24 chars) would be cut to my_very_long_f — only the first 14 characters kept. A second file my_very_long_fileB.txt also becomes my_very_long_f in the table, so the two entries collide.
Real-world: Modern file systems like ext4 now allow much longer variable names — typically 255 characters — but the 14-character fixed slot is the classic Unix Version 7 layout that explains the design trade-off between simple table math and name flexibility. The limit also explains old stories of name-collision bugs on legacy installations.
Assumptions & scope — when the 14-character rule applies.
- Applies: classic Unix Version 7 and the teaching slide's 16-byte layout. The arithmetic and the -character truncation follow directly from that fixed design.
- Does not apply: modern ext4, xfs, btrfs, ntfs — all use variable-length entries and longer limits. Do not quote 14 characters as a current Linux limit in an exam unless the question says “classic/Version 7” or “16-byte entry.”
- Assumes: one byte per character (ASCII). With multi-byte encodings the byte vs character distinction would matter, but the vintage layout predates that concern.
Visual — truncation collision: imagine two mailboxes both labelled my_very_long_f because the label cutter chops after 14 letters. From the outside they look identical, so mail for one can overwrite the other. Modern variable-length systems print the full label, so no collision.
Pitfall — the “first-14 match” trap. Students often think a long name is simply stored longer. Under the old fixed slot it is silently cut, and two files whose first characters match may appear as one entry. The older file might be replaced by the newer one, with a prompt in interactive use. If an exam asks why a long file name appears truncated, answer: the 14-byte fixed name field was exceeded, so the system keeps only the first 14 characters and risks collision.
Exam note: Expect a short theory or output-justification question on why a long file name is stored in truncated form and what the risk is when first characters match.
9.2.3 Special Entries: Dot, Dot-Dot and Root
The last column of the table shows example file names such as file1.txt, along with two special entries . and ...
.indicates the current directory itself...indicates its parent directory./indicates the root — the top of the whole hierarchy.
Those three symbols are not ordinary files; they are entries that let the system walk up or stay in place without extra bookkeeping. In the 16-byte table they look like normal rows — for example offset might be . with the inode of the current directory, offset might be .. with the parent's inode — but their meaning is fixed: . loops to the same inode, .. moves one level up, and / is the top.
Q: What is this . and what is this .. in the directory listing?
A: . is the current directory and .. is its parent directory. The root itself is written as /. The entries behave like any other directory entry in the table, but their meaning is fixed: . loops to the same inode, .. moves one level up, and / is the top. The question came up as a quick recall check, and the follow-up clarified that root is a single forward slash, not dot notation.
A helpful memory picture: inside any folder you always find two hidden signposts — one that says “you are here” (.) and one that says “go back one level” (..). Without them the kernel would need separate logic to stay put or go up; with them the same lookup loop works for every component, including “stay” and “up.”
Recap — 9.2 in one line: A classic Unix directory is a table of -byte rows ( bytes inode number bytes name) at offsets with formula and , names beyond characters are truncated and can collide, and . / .. / / are the fixed signposts for current, parent and root.
Bridge: The 2-byte number in each row is only a pointer — next we see what it points to and why the name itself is called a link.
9.3 The Link Between Name and Content — Inodes
9.3.1 The File Name as a Link
The first two bytes in each directory entry — the I-node number — are the only connection between the name of a file and its contents. For that reason, a file name inside a directory is called a link in this context. Do not confuse this use of link with hard link or soft link in their fuller forms; here link simply means the name acts as a bridge in the directory hierarchy that points to the I-node.
Think of it like a phone book entry: the name you search is the link, and the phone number you dial is the inode number. The name lives in the directory; the contents live where the inode points. Tear out the phone book page and the phone still exists — similarly, removing a directory entry does not delete bytes until no link points to the inode and no process holds it open.
Everyday analogy — library shelf mark. A book title on the catalogue card is the link; the shelf mark is the inode number; the shelf with the actual book is the data blocks. Many catalogue cards can point to the same shelf mark (hard links), but in this lecture the word “link” just means the single name→number bridge inside one directory row.
Scope — what “link” means here vs later.
- Here (this lecture): link = the file name field in a 16-byte entry that bridges to an inode number. Every file name is a link in this minimal sense.
- Later (full Unix): hard link = two directory entries (maybe in different directories) that hold the same inode number, so the same bytes have two names. Soft / symbolic link = a tiny file whose content is another path, which the namei walk replaces and reprocesses. Do not carry the richer meanings back into this table-reading step.
The exam warning for this moment is exactly that confusion: if a question says “file name is called a link”, it means the bridge role, not yet the hard/soft classification.
9.3.2 I-Node Number versus I-Node Structure
Care is needed with the word I-node:
- I-node number is the 2-byte integer stored in the directory entry — a short handle for a file or directory. In the classic 16-bit design it ranges to .
- I-node structure (sometimes written inode) is the larger data structure that holds ownership, timestamps, permission bits, file type, size, link count, and pointers to the various data blocks where the actual file bytes are stored.
So the directory gives you the number; the number leads you to the structure; the structure leads you to the data blocks.
The inode structure, field by field.
When the kernel fetches inode it gets a record like:
- Mode / type — regular file, directory, character device etc., plus permission bits.
- Owner and group — who may read/write/execute.
- Times — last access, modification, status change.
- Size — bytes of file content.
- Link count — how many directory entries point here.
- Block pointers — array of disk block addresses. Direct pointers aim at data blocks; indirect pointers aim at blocks that themselves hold more block addresses for large files.
The shape check: if inodes and block pointers are disk addresses, then following inode → block pointers → data blocks → bytes moves from metadata to content in two hops. The 2-byte number is only the index into this structure; the structure is where the kernel learns where on disk to look.
A small picture was sketched: a directory such as /home/you contains an entry FOO with number 123 which goes to inode 123. That inode holds the file details and pointers, which lead to the data blocks, which finally yield the data. The chain is name inode number inode structure data blocks bytes.
Visual intuition: picture three layers. Top layer is the directory table (names + numbers). Middle layer is the wall of inode records (one per number). Bottom layer is the field of data blocks. An arrow drops from a name in the top layer to its numbered box in the middle, then fans out to blocks at the bottom. The top layer uses the 16-byte math; the middle and bottom use block math (typically bytes per block).
9.3.3 Putting It Together
A directory hierarchy is a tree-like nesting. At the top is /, inside it are subdirectories, inside those are further subdirectories and files. Each level uses the same 16-byte entry rule, and each name maps through its inode number to the next inode structure. Real-world: this is why moving a file within the same file system can be fast — the inode and blocks stay in place and only directory links move. A mv /home/a.txt /home/b.txt on the same partition just rewrites two directory rows; no byte is copied.
Worked mini-walk: /home/you/FOO with FOO → . Start at /'s inode, get /'s data block, find home → get its inode, get home's data block, find you → inode, find FOO → → inode 123 → block pointers → “hello world”. Every hop repeats the same 16-byte row read and inode fetch pattern you saw in section 9.2, just nested level by level.
Pitfalls.
- Saying “the directory contains the file” — it contains the link (name plus number), not the bytes.
- Swapping number and structure: the 2-byte field is the number; the larger record with mode, size and block pointers is the structure. Mixing them makes the chain collapse.
- Thinking inode numbers are global file handles like descriptors — they are file-system–local numbers baked into directory tables; descriptors are per-process handles returned by
open.
Recap — 9.3 in one line: The file name in the 16-byte entry is a link — the only string→number bridge — and the 2-byte I-node number points to the larger I-node structure that fans out to data blocks via .
Bridge: Knowing the chain, the next question is how the kernel walks the whole chain for a full path like /home/ubuntu/sp/file_name.txt — that loop is the namei algorithm.
9.4 Path Name to Inode Translation — The namei Algorithm
9.4.1 Absolute versus Relative Paths and a Motivating Example
Hook — why does the kernel care how you type the path? You type characters; the kernel must end with a number. The whole job of this section is that translation: path string → inode number.
Initial access to a file from a user point of view is through its path name. You must know where the file lives to name it. The kernel also starts from that path name, but internally it must work with I-nodes. It converts the path plus file name into the file's I-node.
A running example carries through the whole discussion:
cat /home/ubuntu/sp/file_name.txtwherecatdisplays the contents of a file.- Equivalent form
cat /home/spiderman/hello.txtused when counting disk transfers.
A path can be of two types:
- An absolute path always starts from the root
/, for example/home/ubuntu/sp/file_name.txt. - A relative path is relative to the current directory and does not start from
/, for exampleexercises/sys_pgm1/test1/mydetails.pxtwhen the current directory is/home/sys_pgm.
The kernel passes the path name one component at a time, where a component — the text between slashes — is the unit of lookup. Even the first / itself counts as a component that means root. The next component is the first directory name, then the next directory name, and so on, with the last component being the file name itself including its extension. The distinction that the extension is part of the name, not a separate component, was made explicit.
A path component example for /home/ubuntu/sp/file_name.txt:
- component 1:
/(root) - component 2:
home - component 3:
ubuntu - component 4:
sp - component 5:
file_name.txt(name plus extension together)
Q: What will be the last component of the path?
A: A student offered "file extension." The clarification is that the file extension is part of the file, so the last component is the whole file name with name plus extension together. For /home/ubuntu/sp/file_name.txt the last component is file_name.txt, not just txt. The extension is not a separate hop; the kernel looks up the full string file_name.txt as one entry in the sp directory.
Why the confusion feels real: in everyday talk we say “the txt file,” so it sounds like txt should be its own level. In the kernel table there is no row for txt — only a row whose 14-byte name field holds file_name.txt.
Exam note: The split name + extension is one component is a common point for a short question.
9.4.2 Where the Search Starts: Root and Current Directory Inodes
All path name searches start from the current directory of the process unless the path name starts with /, which indicates start from root. The kernel therefore keeps two inode numbers handy:
- the root inode number, always available,
- the inode number of the current working directory, updated whenever a
cdis issued.
An extra piece of context stored in the U area (user area) is the per-process information: what files are open for that process, what the current directory is, and what signal action to take when signals such as control-D, control-X, or control-Z arrive.
Why the U area matters here. The U area is the kernel's per-process scratch pad. For pathname work it remembers the current directory inode so a relative path can start without mentioning /. For shell life it also remembers open descriptors and disposition for terminal signals — control-D (end-of-file), control-X or control-Z patterns the professor mentioned as examples of per-process signal choices. The point is not the signal details but that “current directory” is per-process, not global: two shells can cd differently because each has its own U area entry.
A small detail that helps intuition: inside the kernel, you think in paths, but the kernel always works with inodes. The path is the user view; the inode chain is the kernel view. The path is what you type; the inode number is what the kernel caches and locks.
9.4.3 Working Inodes and the Left-to-Right Walk
Components of the path are processed from left to right — that is the order the path is written and the order the kernel follows. A light aside noted that if a file system supported a language written right to left, the order might feel different, but on the systems in use the walk is left to right.
Every component except the last one should be either a directory or a symbolic link:
- Those intermediate inodes are called working inodes. They are the stepping stones that let the kernel reach the final file inode.
- If the working inode is a directory, the kernel looks for the current path component inside the directory entry list for that working inode. If it is not found, it returns an error. If it is found, the inode number tied to that matched name becomes the new working inode.
- If the working inode corresponds to a symbolic link, the path name up to and including the current component is replaced by the contents of the symbolic link and the pathname is reprocessed from the start, because a symlink is a shortcut that points elsewhere.
Intuition — stepping stones. The teaching line that was restated several times is: home is the working inode, search for ubuntu inside it, then ubuntu becomes the working inode, then search for sp inside ubuntu, then sp becomes the working inode, and so on.
Picture a river crossing: each stone is a directory inode. You stand on home, you look inside home's table for the name ubuntu, you hop to ubuntu's inode-stone, then look for sp, hop again. The last hop lands on the file's inode. You never jump over a stone; you always move one component at a time, left to right.
The walk is exactly the left-to-right scan of the string you typed. For /home/ubuntu/sp/file_name.txt the kernel sees the stream / → home → ubuntu → sp → file_name.txt and processes them in that string order. The Arabic aside — whether a right-to-left language would flip the order — was answered as left-to-right for almost all systems in use because the path string itself is built left-to-right.
9.4.4 The namei Algorithm Laid Out Step by Step
The algorithm that does this conversion is called the namei algorithm — spoken as "name I" — and every cat, open, or search for a file goes through this loop. “namei” literally means “name to inode.”
Pseudo-steps preserved verbatim alongside the reconstruction:
"if path name starts with the root meaning that it is absolute path ... working I node is root I node ... if not ... working I node will be the current working I node ... while there are more in the path name ... read the next component ... read the directory content ... if component matches entry ... get I node number ... release working I node ... I node of match component becomes working I node ... return working I node."
Reconstructed as structured prose with symbols:
- Let be the path string, let be the inode number of the current working directory, let be the inode number of
/, and let be the working inode.
Then
When the loop ends, is the inode number of the file name itself. The algorithm returns that working inode. That inode then leads via its block pointers to the data blocks that hold the actual bytes.
Reading the loop line by line.
- Initialization: The conditional on chooses the start stone: root inode if the path is absolute, otherwise the process's current directory inode from the U area.
- Read directory content of : fetch the 16-byte entry table for the current directory (via its inode structure and data blocks).
- Match: linear search for the string in the 14-byte name fields. On hit, pull the 2-byte number .
- Release: drop the in-memory hold on the old — “release the working inode” means the kernel no longer pins that directory's inode as the focus.
- Update: — the matched entry's inode becomes the new stepping stone.
- Error: if no row has name , the path is bad (
No such file or directory).
Symbols in one place: is a string, and are inode numbers (16-bit in the classic layout, larger in modern systems), and are inode numbers, is a component string like home or file_name.txt.
The verbal audit trail above is kept intact so the step "release the working inode" is not lost — it means the old working inode's in-memory copy is no longer held as the focus moves forward. The step matters for locking: without release, the kernel would pin every directory on the path at once.
Real-world: This same loop explains why a broken intermediate directory name fails fast, while a dangling symlink causes reprocessing rather than a simple miss. Example of symlink: if sp is a symlink to /srv/data, the kernel replaces the path up to sp with /srv/data and restarts the walk from root — it does not just look for the next component inside sp.
Worked example — namei walk for /home/ubuntu/sp/file_name.txt.
Start: /home/ubuntu/sp/file_name.txt, so .
-
/is implicit root — already root. -
home— read directory content of (root's table), find entryhome→ , release old , (home is now working inode). -
ubuntu— read home's table, findubuntu→ new , release home, ubuntu's inode. -
sp— read ubuntu's table, findsp→ new , release ubuntu, sp's inode. -
file_name.txt— read sp's table, findfile_name.txt→ new , release sp, file's inode. Loop ends. Return .
At each line the pattern is “home is the working inode, search for ubuntu inside it, then ubuntu becomes the working inode, then search for sp inside ubuntu, then sp becomes the working inode” — exactly the professor's stepping-stones phrasing. If any lookup fails, the kernel returns an error before reaching the file. If an intermediate inode is a symlink, the prefix up to that component is replaced by the link content and the whole walk restarts.
For a relative path exercises/sys_pgm1/test1/mydetails.pxt with current directory /home/sys_pgm, the same loop runs but starts with (the inode of /home/sys_pgm) and components exercises, sys_pgm1, test1, mydetails.pxt in order.
Visual intuition: plot the path horizontally. Put a token on the start stone (root or wd). Each iteration moves the token one notch right to the next stone, after peeking inside the current stone's 16-byte table. The token never leaps; symlink is the only case that teleports the token back to the start with a new path string.
Assumptions & scope — what namei assumes.
- Assumes: every intermediate component except the last resolves to a directory or a symlink. If an intermediate component is a regular file, the walk errors — you cannot look up a name inside a file.
- Assumes: directory tables are readable and follow the 16-byte / 2-plus-14 layout in this teaching model. Modern systems generalize the entry size, but the lookup logic is the same.
- Scope: namei finds the inode; it does not check permissions or open the file — later steps do that. Caching (inode and buffer cache) can avoid disk fetches for repeated components, but the logical walk stays left-to-right.
Common pitfalls.
- Thinking the extension is a separate component — it is not;
file_name.txtis one lookup. - Thinking the walk is right-to-left for some locales — it is left-to-right because the string is built that way; the Arabic joke was only a hook.
- Forgetting the release: “get inode number, release working inode, inode of matched component becomes working inode” — order matters; the new inode is not valid until the old one is released and the next table is read.
Exam note: Expect to apply the same left-to-right working-inode walk to a relative path example and to state how many inode and data block fetches occur.
9.4.5 Student Questions During the Walk
Q: If Arabic were used for file systems, would the walk go right to left?
A: The note was that the professor was not sure for such a system, but for almost all systems in use the walk is left to right because that is how the path string is built. The question worked as a memory hook: follow the string order you see on screen — /home/... is read from the slash on the left toward the file name on the right — and the kernel follows that same left-to-right token motion.
Why it is useful anyway: it reminds you that namei is not scanning the disk in some optimized order; it is literally consuming the characters you typed from first to last, one slash-separated piece at a time.
The aside serves as a memory hook rather than a technical rule: follow the string order. The same hook helps with the exam: when asked to “show the working inode at each step,” list components left-to-right and update after each match — exactly the loop invariant above.
Recap — 9.4 in one line: The namei (“name I”) algorithm turns a path into an inode by initializing working inode to for absolute paths or for relative, then looping left-to-right — read directory of , match component to a 14-byte name, grab its 2-byte inode , release , set — with symlink prefix replacement as the only detour, finally returning for the file.
Bridge: Once the final is known, the next cost appears — each hop inside that loop can touch disk, so even a simple cat pays an inode-plus-data-block price at every level.
9.5 Disk Access Walkthrough — From cat Command to Data Blocks
9.5.1 The Example Path Used for Counting
Hook — why does cat pause before printing? The bytes you asked for are not in memory yet. The kernel must first fetch a chain of tables and records, one per directory on the path, before the file's own bytes arrive.
The walkthrough example uses cat /home/spiderman/hello.txt and by analogy cat /home/ubuntu/sp/file_name.txt. The path is absolute, so the start is the root. Along the way concrete inode numbers were given: root is , home is , and hello.txt is . Those numbers are the short handles that move while the full inode structures stay on disk until fetched.
What those numbers mean. In this teaching demo the kernel's root inode number is — the well-known fixed root used by ext-family file systems (inode is often reserved). home → and hello.txt → are the 2-byte values you would read from the 14-byte name fields' companion numbers in the directory tables. The numbers themselves are small; the structures they point to are larger records with mode, size and block pointers.
For /home/spiderman/hello.txt the components are / → home → spiderman → hello.txt — three directories before the file, matching the four-component shape used for counting. For /home/ubuntu/sp/file_name.txt the shape is / → home → ubuntu → sp → file_name.txt — four directories before the file. The counting pattern scales with depth: each extra directory adds another inode-plus-data-block pair.
9.5.2 Step-by-Step Transfer Sequence
Each level does two fetches if not already cached: the inode structure itself and then the data block(s) that hold the directory entries. A data block — the unit the kernel moves between disk and main memory — is typically , that is bytes.
Worked trace — cat /home/spiderman/hello.txt with disk transfers.
Path is absolute, so working inode starts at root .
- The inode number of
/is always with the kernel. That inode structure must be on persistent storage, so it is copied from disk to main memory. Then the data block of/is transferred to main memory. Now search forhomeinside the data block of/and obtain its inode number, which is .
- Transfers so far: inode
/(disk→memory) + data block/(disk→memory) = 2.
- Transfer the inode structure for (home) from disk to main memory. Then transfer the data block associated with the home directory to main memory. Search for
spidermanin that data block and obtain its inode number (call it ).
- Adding: inode + data block home = +2 (total 4).
- Transfer the inode structure for
spidermanto main memory, then its data block to main memory. Search forhello.txtin that data block and obtain its inode number, which is .
- Adding: inode spiderman + data block spiderman = +2 (total 6).
- Transfer the inode structure for (
hello.txt) into main memory, and then transfer its data block — the one that holds the actual file bytes — into main memory. From there the CPU can access it andcatdisplays the content on screen.
- Adding: inode + data block hello.txt = +2 (total 8).
The pattern spelled out for each directory is: fetch its inode fetch its data block look up next component name get next inode number. The file's final data block fetch is the one that actually yields the text to print. The same sequence applies if the path were /home/ubuntu/sp/file_name.txt: / to home to ubuntu to sp to file_name.txt, with inode + data block pairs at each hop — here depth gives inodes (root + 4 dirs/file) and data blocks, total 10 transfers before the first byte of user data is seen.
The block size reminder heard is that a data block is typically K, that is bytes, so each directory fetch brings a block-sized chunk of entry tables into memory. Formally:
and the earlier directory offset still steps by bytes within that block.
Sense-check: transfers for a 3-directory path is not “slow” — each transfer is one disk I/O, and caching removes most repeats. But the count explains why the first cat after boot costs more than the second: the buffer cache already holds the inodes and blocks the second time.
Visual intuition: draw a ladder with two rails — left rail is inode structures, right rail is data blocks. For each level of the path you climb one rung on the left (inode) and one on the right (block). The last rung on the right finally holds the file's text. Without caching you climb both rails at every level; with caching many rungs are already warm in memory.
9.5.3 Counting Disk Operations
How to count.
- Define a disk operation as one transfer of an inode or a data block from disk to main memory.
- For a path of depth directories before the file, the first access costs:
where the “+1” is the final file itself — even if you quote “per directory plus file,” the file also needs its inode and its data block. For cat /home/spiderman/hello.txt (home, spiderman) plus root handling gives the 8 counted above once root's pair is included. For /home/ubuntu/sp/file_name.txt gives 8 in the simplified per-directory count, or 10 including root's pair — examiners accept either breakdown if you label what you include.
Each transfer from disk to main memory is a disk operation. That is why even a simple cat can touch many disk structures before the first byte of user data is seen. Caching can reduce later repeats, but the first walk must materialize each inode and directory block.
Real-world: on a modern ext4 host the block size may be , or rather than classic , and the inode number is 32-bit, but the “two per level” pattern stays identical — inode then block, lookup, next inode. Tools like stat and debugfs let you inspect the same inode numbers (stat /home shows Inode: 13 in the demo's vintage numbering) without running the full walk by hand.
Assumptions & scope.
- Assumes: cold cache — no inode or block is already in memory. In practice the buffer cache keeps recently used inodes/blocks, so repeated
caton the same path may need zero disk reads. - Assumes: one data block per directory suffices to hold its 16-byte entry table for the lookup. Huge directories that span many blocks would need more than one block fetch per level.
- Scope: counting inode + data block per hop is a disk-operation estimate, not a timing estimate — seek and rotational costs are not modelled here.
Pitfall — forgetting root or the file's own pair. Students often count only intermediate directories and omit root's inode+block or the final file's inode+block. Name them explicitly: “root inode, root block, home inode, home block, …, file inode, file block.”
9.5.4 A Second Set of Paths and an Exercise
Two shorter examples contrast absolute and relative starts:
- Absolute:
/etc/passwd— starts from/as in the main example. - Relative: current directory is
/home/sys_pgmand the path given isexercises/sys_pgm1/test1/mydetails.pxt. The exercise left to the class is to walk the namei loop starting from the current directory inode rather than root and process componentsexercises,sys_pgm1,test1,mydetails.pxtin order, applying the same working-inode replacement at each match.
Exercise walk — relative path exercises/sys_pgm1/test1/mydetails.pxt from /home/sys_pgm.
Let (the wd saved in the U area).
-
exercises— read data block of , findexercises→ , release , . -
sys_pgm1— read data block of , findsys_pgm1→ , release, . -
test1— same: findtest1, hop. -
mydetails.pxt— findmydetails.pxt(= name plus extension as one component) intest1's table, final is the file's inode. Then fetch its inode structure and its data block — the 1K block at 1024 bytes — to get the bytes.
Counting: 4 components → 4 levels of inode+block if warm-cache-free, plus the final file's own pair already included as the last level. Compare with the absolute /etc/passwd which starts at root , finds etc → its inode, then passwd → -like number: only 2 hops, so far fewer transfers.
Exam note: Expect to apply the same left-to-right working-inode walk to a relative path example and to state how many inode and data block fetches occur. Label each hop as “inode → data block → lookup name → inode .”
Recap — 9.5 in one line: For cat /home/spiderman/hello.txt the kernel does disk→memory for root inode + root data block → lookup home () → home inode + home block → lookup spiderman → spiderman inode + block → lookup hello.txt () → file inode + file data block ( bytes), so transfers per level (inode then -byte block, offsets still inside directories) before cat can print.
Bridge: The inode-and-block cost you just counted is exactly what df summarizes from the other side — how many blocks and inodes the file system has, uses, and leaves free.
9.6 Disk Usage Inspection — The df Family
9.6.1 What df Shows at Base
Hook — you counted individual block fetches; now step back: how full is the whole disk? That summary view is df.
df — introduced as "disk file system" and in practice disk free — lists the file system view: file system name, total blocks, used blocks, available blocks, use percentage, and what it is mounted on. A plain df prints those columns for each mounted file system.
A typical df header reads Filesystem 1K-blocks Used Available Use% Mounted on. Each “1K-block” is the same byte unit you met in the transfer walk, so the numbers tie back: a file that needed a 1K data block contributes one to “Used,” and the free count drops by one.
The base table, column by column.
- Filesystem — device name, e.g.,
/dev/sda1. - 1K-blocks — total blocks on that file system, in units of 1024 bytes.
- Used — blocks that hold inodes’ data blocks, directory blocks, and file blocks.
- Available — blocks free for new files.
- Use% — . A value like means mostly free; means a full file system where writes will fail.
- Mounted on — the directory where the file system appears in the single tree, e.g.,
/for root,/homefor home.
No flag means “one line per mounted file system” in the raw 1K-block unit — useful for scripts that parse numbers but hard for a human to eye-scan.
9.6.2 Options That Change the View
df -a: include all file systems, even dummy ones. In the demo the output ofdfanddf -alooked much the same on that system, but the flag concept is that-aforces display of entries that an ordinarydfmay hide — pseudo and dummy file systems with zero blocks that still appear in/proc/mounts.df -i: switch from blocks to inode information. Instead of size, it shows total inodes, used inodes, free inodes for each file system — the handle space rather than the byte space.df -h: human readable sizes. The rawdfprints large block counts;df -hprints sizes like98G,9.4G,84Gand a use percentage such as11%. The demo highlighted/dev/sda1with , , , mounted on/with11%use, plus other lines at100%use. The math is the same blocks, just scaled: K-blocks.df -T: add file system type column. The demo showed types such asext4,ext3,ntfs,fat,fat32for normal read/write,squashfsfor a compressed file system that stores a whole image in squeezed form, andtmpfsfor a temporary file system that keeps data in local memory for fast read/write and transfer.
Worked snippets — what you type and what changes.
df→ columnsFilesystem 1K-blocks Used Available Use% Mounted on. Example line (raw):/dev/sda1 102584320 9856000 87500000 11% /.df -i→ same rows but columnsInodes IUsed IFree IUse%. Example:/dev/sda1 6553600 120000 6433600 2% /— two percent of inodes used, so you can still create many files even though 11% of blocks are used.df -h→ sizes inK,M,Grather than raw block counts. Example:/dev/sda1 98G 9.4G 84G 11% /— the exact demo numbers, easier to quote in viva.df -T→ addsTypecolumn, e.g.,/dev/sda1 ext4 98G 9.4G 84G 11% /. The extra column tells you which driver family backs the blocks.df -h /ordf /home→ single line for the file system holding that path — use when a question asks “how full is/?”.
Each snippet was run live, and the back-and-forth corrected the case of -t versus -T when a try with the wrong case did not give the expected filter.
Real-world: tmpfs is often used for /tmp or caches where speed matters and persistence is not needed; squashfs is common for live images. ext4 is the typical read/write default on Linux; squashfs is compressed read-only, so its “Used” is the squeezed image size, not a dynamic count.
Visual intuition: picture two gauges side by side. df -h is the fuel gauge in gigabytes — 98G tank, 9.4G used, 84G left. df -i is the ticket-roll gauge — total tickets (inodes) 6.5M, used 0.12M, free 6.43M. A file system can run out of tickets before it runs out of fuel if millions of tiny files are created — that is why -i exists.
Assumptions & scope.
- Assumes: the
1K-blocksunit unless-his added. Scripts that parsedfshould use the default ordf -kexplicitly;df -his for humans. - Scope:
dfsummarizes the file system that contains a path, not the directory itself.df /homeanddf /home/ubuntu/spcan print the same line if both live on/dev/sda1. - Case matters: lowercase
-tis a filter (include only this type), uppercase-Tis a column (show the type). Confusing them is the classic viva trap.
Pitfall — dummy vs real. df -a can add zero-block pseudo entries; students sometimes think the count changed. On the demo host df and df -a looked alike — that only means no extra dummy was mounted there, not that -a does nothing.
9.6.3 Targeted and Filtered Queries
A path can be given, for example df -h / or df /home, to show only the file system that holds that mount. That answers “which device backs this directory?” without listing all mounts.
To filter by type:
df -t ext4: include only file systems of typeext4. On an ext4-only host this shows the real disks;squashfsandtmpfsrows disappear.df -x ext3: exclude file systems of typeext3. This hides a type you are not interested in and shows everything else.- Lowercase
-tmeans include type, uppercase-Tmeans show type column — the case matters. Tryingdf -t ext3on a system with noext3prints that no file system matched, whiledf -x ext3falls back to showing the rest.
Combinations like df -T -t ext4 or df -T -x ext3 are allowed; the same include/exclude logic applies while also printing the type column. So df -T -t ext4 means “show only ext4 and add the Type column,” which on the demo host highlighted the /dev/sda1 ext4 line alone.
Exam note: A question may ask to give the exact flag that yields a human-readable size view, an inode view, or a type-filtered view, or to justify output lines such as the 11% versus 100% entries. The 100% entries in the demo were the squashfs/tmpfs loop rows — they are sized exactly to their content, so used equals total by design, not because the disk is actually full.
9.6.4 Worked Command Snippets
Live-typed patterns to memorize.
df→ columnsFilesystem 1K-blocks Used Available Use% Mounted on.df -i→ same rows but columnsInodes IUsed IFree IUse%— inode view, not byte view.df -h→ sizes inK,M,Grather than raw block counts. Recall98G,9.4G,84Gfor/dev/sda1at11%.df -T→ addsTypecolumn, e.g.,/dev/sda1 ext4, plussquashfs(compressed) andtmpfs(memory) rows.df -h /→ single line for the file system holding/.df -t ext3→no file systems processedwhen none match;df -x ext3→ all others. As demoed,df -t ext4keptext4,df -x ext3hidext3.df -T -t ext4→ filtered plus typed;df -T -x ext3→ excluded plus typed.
Each snippet was run live, and the back-and-forth corrected the case of -t versus -T when a try with the wrong case did not give the expected filter. A handy memory aid: little t = tiny filter, big T = big Table adds a column.
Recap — 9.6 in one line: df reports file system use as 1K-blocks Used Available Use%, and flags reshape it — -a adds dummy file systems, -i swaps blocks for Inodes IUsed IFree, -h scales to 98G/9.4G/84G 11% for humans, -T adds the Type (ext4, ext3, ntfs, fat, squashfs, tmpfs), -t/-x filter by type while a path argument like df -h / narrows to one mount.
Bridge: df tells you how much space and how many inodes are left; the next layer tells you what kind of device that space sits on and whether the kernel buffers it.
9.7 Unix Input/Output — Character versus Block Devices
9.7.1 Memory and Device Setting
Before device talk, the memory ladder was named: main memory is RAM (random access memory), secondary memory is the hard disk, tertiary examples are tapes, and cache sits above RAM in levels L0, L1, L2 with no separate common name. That ladder matters because where bytes live decides how they move.
The ladder, bottom to top.
- Tertiary: tapes — cheapest per byte, slowest, often offline.
- Secondary: hard disks — your
sdaand partitions; block devices whose bytes are moved in byte chunks. - Primary: RAM — main memory where the kernel copies inode structures and data blocks before the CPU can use them; some views expose RAM itself as a character device.
- Cache:
L1,L2(and sometimesL0) — tiny, fastest, no separate “disk-like” name; they hold recently used bytes so repeatedcatfetches avoid disk.
The device classification that follows is about the interface the kernel exposes for each rung, not just the hardware itself.
Visual intuition: picture a pyramid. The base is wide tapes, the middle is the disk, the top is narrow RAM, and the tip is cache. Bytes flow upward on read (disk block → RAM → cache → CPU) and downward on write. Character devices cut across this pyramid by streaming bytes without staging a whole block.
9.7.2 How to Tell the Type: ls -li on /dev
A student asked how to know whether a device is block or character oriented.
Q: Is there a simple command that tells which devices are block devices and which are character devices?
A: Use ls -li /dev. The first character in the long listing tells the file type: - is a regular file, d is a directory, c is a character device, and b is a block device. Entries that start with c are character devices and those with b are block devices among the /dev files. That same flag -i also prints the inode number, so you see both the type and the handle in one view.
Example fragment from a live ls -li /dev (inodes illustrative):
1234 crw-rw-rw- 1 root root 1, 5 ... /dev/zero (c = character)
5678 brw-rw---- 1 root disk 8, 0 ... /dev/sda (b = block)
9012 crw--w---- 1 root tty 4, 0 ... /dev/tty0 (c = character)
The first column c vs b is the answer; the 1, 5 or 8, 0 after it are the major, minor numbers you meet in the next section.
Because /dev is itself a directory, the 16-byte entry logic still applies — each device name occupies a row whose 2-byte inode number points to a special inode that, instead of block pointers, stores major/minor device numbers. So ls -li is literally reading the same directory table you studied in section 9.2, plus dereferencing the device inode.
Exam tip: if a question shows crw- vs brw- and asks “character or block?”, answer from the first letter alone: c → character, stream, no kernel block buffering; b → block, structured, kernel buffers.
9.7.3 Character Devices — Stream of Bytes, No Buffering
Character devices treat data as a stream of bytes. Examples given are main memory (RAM itself as a char device view), line printers, and terminals. The interface between the two ends is unstructured or raw, and there is no buffering provided at that interface. Whatever bytes are produced at one end should be consumed at the other with internal protocol handling to avoid overflow, but the kernel does not stage the bytes in a separate block buffer.
Think of it like a pipe with no tank: bytes flow as they come, and the sender and receiver must pace each other. If the receiver is slow, the sender must wait or bytes are lost — there is no pallet to park a whole block.
Character device traits.
- Granularity: one byte at a time — a stream, not a block.
- Structure: unstructured / raw — no record boundaries imposed by the kernel; a line printer receives raw bytes, a terminal sends keystrokes as they occur.
- Buffering: none provided by the kernel at the device interface for block staging. Flow control is by protocol between ends (e.g., terminal handshake), not by a kernel block buffer.
- Examples: RAM viewed as
/dev/memor/dev/zero(character view), line printer (/dev/lp0), terminal (/dev/tty).
The “no buffering” phrase means no block buffer that holds a full -byte pallet. Tiny per-character queues may exist in hardware or driver, but the semantics presented is stream with no kernel block staging.
Where the analogy breaks: real terminals do have small driver queues and line disciplines, but from the programmer's view read() on a character device returns whatever bytes are available now, not a full block.
9.7.4 Block Devices — Blocks, Structure and Kernel Buffering
Block devices move data in blocks rather than single bytes. Examples are disks and tape drives. The interface is structured: a block is read or a block is written. Because a whole block is moved at once — typically bytes was quoted — the other end may not be ready to accept or process it immediately. For that reason buffering is required, and that buffering is provided by the kernel.
Think of it like pallets versus letters: character moves letters one by one, block moves a whole pallet that must be staged until a forklift is free. The pallet is the -byte block; the staging area is the kernel's buffer cache. Without the buffer, a block read would have to wait for the consumer to be instantly ready, which at block granularity is wasteful.
Block device traits, side-by-side with character.
| Dimension | Character (stream) | Block (structured) |
|---|---|---|
| Unit moved | single byte, stream | whole block, e.g., bytes |
| Interface | unstructured / raw | structured — “read block N / write block N” |
| Kernel buffering | none at block interface | kernel provides buffer cache |
| Examples | RAM (char view), line printer, terminal | disks (sda), tape drives |
| Random access | generally sequential | random — can seek to any block number |
When to pick which: If you need random access and efficiency for large data, you want block — file systems sit on block devices for this reason. If you need low-latency byte-by-byte interaction (typing, printing), you want character — the stream matches the device's natural timing.
Visual intuition: picture the disk as a wall of 1024-byte pallets. read( block 5 ) lifts one pallet into the kernel buffer. If the process is not ready, the pallet waits on the staging floor (buffer cache) rather than being resent. Character's “letter” needs no staging floor — it is handed directly.
Exam note: A contrast question may ask for two differences and an example each, plus who provides buffering: kernel for block, no buffering for character. A safe two-point answer: (1) stream vs block, (2) kernel buffering is required for block and provided by kernel, no buffering for character — with examples line printer / terminal vs disk / tape.
Assumptions & scope.
- Assumes: classic byte block quoted in lecture; modern disks often use blocks but the principle is the same.
- Scope: buffering discussed is kernel block buffering for the structured interface. Character devices may still have driver-level queues, but those are not the block buffer meant here — do not claim “character has zero bytes buffered anywhere.”
- Pitfall: tapes were listed as block devices in lecture — even though tapes feel stream-like, their transfer here is described as block-structured, so classify them as block in exam answers tied to this lecture.
Common traps.
- Saying block devices need no buffering because blocks are efficient — the lecture says the opposite: because a whole -byte block is moved at once, buffering is needed and the kernel provides it.
- Mixing up the
-iinls -li(shows inode number) with the-iindf -i(shows inode counts). Same letter, different command.
Recap — 9.7 in one line: The RAM → disk → tape → cache ladder sets the stage; ls -li /dev reveals c (character, stream, no kernel block buffering, e.g., RAM/line printer/terminal) versus b (block, structured -byte blocks, kernel buffering, e.g., disks/tapes) via the first letter, with the c/b plus inode view echoing the 16-byte directory and inode ideas from earlier.
Bridge: Once you can tell c from b, the next step is to read the numbers that appear beside those letters — major and minor — and the tools lsblk and fdisk that expose them for disks and partitions.
9.8 Device Identification — Major and Minor Numbers, lsblk and fdisk
9.8.1 Major as Driver, Minor as Instance
Every hardware item in Linux is seen as a device, and a device works through its device driver installed in the system. The driver is the interface that makes the hardware operate.
Two numbers, two questions.
- Major device number indicates the type of the device and, by that, which driver is needed. When a device file is opened, Linux looks at its major number and forwards the call to the driver registered for that number. It is the “which kind” key.
- Minor device number indicates the specific instance among possibly many of that kind. There can be more than one hard disk, more than one RAM region, more than one CPU. The minor number picks one instance from the list for that major. It is the “which one” key.
So major selects the driver family, minor selects the instance within the family. If major is the department, minor is the desk number inside that department.
A helpful rephrase from class: major is “what kind of device” and minor is “which one of that kind.” Opening /dev/sda with major routes to the SCSI/SATA disk driver; minor picks the whole disk, minor picks its first partition sda1, minor might pick a logical partition. The same majors appear under /dev as the MAJ:MIN pair you see in lsblk.
Visual intuition: picture a switchboard. The major number chooses the bank of operators (disk bank, loop bank, terminal bank). The minor number chooses the operator within that bank. The kernel looks up the major table once, then dispatches to the minor entry — two-level routing, not one flat number.
Why not just use the file name? Because names can vary (/dev/sda vs /dev/disk/by-uuid/...) but the major/minor pair is the kernel's stable dispatch key. The inode for the device-file stores this pair instead of block pointers — that is why ls -li /dev shows both inode number and major:minor together.
9.8.2 lsblk — List Block Devices
lsblk stands for list block and reports only block devices. Its columns were walked one by one:
NAME— device name such assda,sda1,loop0.MAJ:MIN— major and minor numbers, e.g.,8:0forsdaand8:1forsda1,7:0onward for loop devices.RM— removable flag as0or1, where0means non-removable and1means removable.SIZE— size of the device, e.g.,100Gfor the main disk.RO— read-only flag as0or1, where1means read-only and0means read and write.TYPE— type such asdisk,part(partition),loop.MOUNTPOINT— where it is mounted, if at all, such as/.
Worked reading — lsblk rows from the demo, decoded.
In the demo:
sdaandsda1shared major8, which marks the disk driver family, with minors0and1marking whole disk and first partition. So8:0= disk driver, instance 0 (whole disksda);8:1= same driver, instance 1 (first partitionsda1).- Loop devices shared major
7across about twenty-three instances such asloop0throughloop22, all showingRO1for read-only,RM0for non-removable, and typeloop. So7:0…7:22= loop driver, instances 0–22 — the squashfs images you saw underdf -T. sda1andsr0showedRO0— readable and writable — while many loop entries showedRO1— read-only because they back compressed images.- One entry alone showed
RM1to mark a removable disk drive — typically a USB stick or card reader. - Sizes:
sdaas100Gin lsblk aligns withsda1as98G–100GBindf -handfdisk -l— the small gap is partition overhead and rounding.
These numbers make the earlier abstract major/minor idea concrete: 8 routes to the disk driver, 1 after the colon picks the instance; 7 routes to the loop driver for the many squashfs mounts.
How to read a row in order: pick MAJ:MIN first (driver + instance), then RM/RO flags, then TYPE and MOUNTPOINT to see if it is mounted. Example mental voice: “sda1 8:1 0 100G 0 part / — disk driver instance 1, non-removable, read-write partition mounted as root.”
Counting sense-check: major 7 with 23 loop entries shows the minor space is not limited to 0/1 — minors enumerate every instance for that major.
Visual intuition: picture lsblk as a roster. Leftmost NAME is the human label, middle MAJ:MIN is the kernel routing pair, flags RM/RO are sticky notes (“removable?”, “read only?”), TYPE is the role, MOUNTPOINT is where it hangs in the tree. The demo's wall of loop0…loop22 with identical major 7 looks like a block of identical uniforms — same driver, many instances.
Scope — lsblk vs ls.
- lsblk = only block devices, structured table with
MAJ:MIN,RM,RO,TYPE. It will not show character devices — usels -li /devfor those (crows). - Scope:
lsblkwithout flags shows the current topology; add-ffor file system types if needed, but the lecture demo used the default columns listed above.
9.8.3 fdisk — Partition Tables and Details
fdisk is the partition tool known to Linux administrators for creating and editing partitions, but here it was used to inspect.
fdisk -llists all disks with details. Withoutsudoit returnedpermission denied, which was the cue that a privileged view is needed. Withsudo fdisk -lit printed for each disk: size such as100GB, number of sectors, total bytes, sector size512bytes, I/O size512bytes, disk label typeDOS, disk identifier such as a hex mark, and forsda1fields like boot flagyes, start sector2048, number of sectors, size,Id, and type such asLinuxor the operating system installed.
sudo fdisk /dev/sdaopens the interactive view for that disk. Options walked in the help output were:mhelp,atoggle bootable flag,ddelete a partition,Flist free unpartitioned space — which in the demo was0because the whole GB was already partitioned,Llist known partition types — includingFAT12,XENIX,FAT16,FAT32,AIX,NTFS,ZFSand many others,pprint the partition table — which repeated thefdisk -ldetail forsdaalone,vverify the partition table andiprint information about a partition (device, boot, sectors and so on).
Worked inspection — fdisk outputs decoded.
The sudo fdisk -l run for sda and the p run inside sudo fdisk /dev/sda echoed the same line conceptually:
- Disk
sda:100GB, sectors = million sectors, sector size bytes, I/O size bytes, label typeDOS, identifier like0xabc123. - Partition
sda1: bootyes(the bootable flag is set), start sector2048, number of sectors covering almost the whole disk, size ≈100G(or98Gas seen indf -h— same disk, different accounting),Ide.g.,83for Linux,TypeLinux. - Free space (
F):0— no unpartitioned gap, becausesda1starts at2048and runs to the end. The 2048-sector offset is intentional alignment: sector is the MBR, sectors – are reserved for alignment, so the first usable sector is . At bytes per sector, that reserves bytes MB before the partition. - Type list (
L): includesFAT12,XENIX,FAT16,FAT32,AIX,NTFS,ZFSand many more — you scroll this list to setIdwhen creating a partition, but the exam only asks thatLlists types andpprints the table.
Permission lesson: bare fdisk -l → permission denied. With sudo fdisk -l → full detail. That split is exactly the teaching moment: partition tables are privileged state, so inspection needs elevated rights.
The actual p run for sda echoed the earlier line: sda1 starts at 2048, occupies the bulk of the disk, shows as bootable, and carries its Id and system type. The number appears in both fdisk -l and lsblk narratives because the first partition is aligned to 1 MB — a modern default that avoids misaligned I/O on 4K-sector drives.
Real-world: In practice most day-to-day inspection uses lsblk and df; fdisk -l is the lower-level view an administrator pulls when planning partitions. You would run lsblk to see “what is mounted where,” df -h to see “how full,” and sudo fdisk -l to see “how the disk is sliced at sector granularity” including the -byte sector and start.
Pitfalls & exam traps.
- Permission:
fdisk -lwithoutsudo→ permission denied — always addsudofor the full table. That verbatim error is a teaching moment, not a typo. - Case matters again:
fdisk -l(lowercase L) lists tables; insidefdisk /dev/sdathe commands are single letters where case matters —pprints,Fshows free space,Llists partition types,mshows help. Do not mixlandL. - Start
2048is not magic: it is MB alignment padding. If a question asks why the first partition starts at 2048, answer “1 MB alignment / DOS label reserved area,” not “random offset.” - Sector size is two places: both “sector size 512” (logical) and “I/O size 512” appear; quote bytes for both as seen in the demo.
Recap — 9.8 in one line: Every device file routes by major (driver family — for disks sda/sda1, for loop loop0…loop22) and minor (instance — for whole disk, for sda1, for loops), visible as MAJ:MIN in lsblk with RM/RO/TYPE/MOUNTPOINT and at sector level as -byte sectors, start (≈1 MB), DOS label, 100GB size, p/F/L views, and permission denied without sudo in fdisk.
Bridge: With major/minor you can now read any /dev entry the way you read the 16-byte directory entry — a small integer key that the kernel expands into a full structure, just as an inode number expands into inode plus blocks.
Exam Guidance Summary
All exam-facing advice is gathered here, with inline markers also placed where the topics appear:
- Syllabus for the mid-semester exam is exactly the files and directories material covered up to this session, including directory entry layout, inode links, path-to-inode translation via the namei algorithm, and the df / device identification commands. No new heavy theory beyond that was added in this session. The session was the last class before the mid-semester, with no class next week.
- Question style is almost entirely commands. Expect to justify a specific output given a command, or to give the command plus option that produces a given output, with variations on flags. One or two very simple theory questions at most may appear, but the bulk is practical command use. Example patterns: “what does
df -hshow for/dev/sda1at 98G/9.4G/84G 11%?”, “which flag ofdfshows inode counts?”, “what doesls -li /devshow forcvsb?”. - Study source is the slides and the options listed in the slides. There is no need to learn every flag a command supports; the flags shown in class — such as
df -a,df -i,df -h,df -T,df -t/-xinclude/exclude,ls -li /dev,lsblk,fdisk -landfdisk /dev/sdainteractiveF,L,p— are sufficient, even though that list is already long. Fordfremember11%vs100%interpretation; forlsblkremember major8forsda/sda1and major7for loop, plusRM/ROandTYPE; forfdiskremember sector size512bytes, start sector2048, disk size100GB,DOSlabel, and the need forsudo. - Past paper — an old question paper with practice questions is or will be posted on the Taksila portal. That paper shows the pattern of asking for command and option and of interpreting listings such as the
dftable orlsblkrows. Practise by covering the output and predicting which flag produced it. - Specific concept spotlights carried from sections:
- 14-character truncation risk (9.2): when first characters match the truncated names collide; the older file might be replaced — expect a theory justification.
- Name + extension is one component (9.4): last component of
/home/ubuntu/sp/file_name.txtisfile_name.txt, nottxt— expect a short question on component counting. - Working inode walk (9.4): left-to-right for absolute else , then
16 = 2 + 14style lookup per component — expect to walk a relative path likeexercises/sys_pgm1/test1/mydetails.pxtfrom/home/sys_pgm. - Disk operation counting (9.5): each level needs inode plus -byte data block; root , home , hello style numbers — expect to count transfers for a given path.
- df flags (9.6):
-a(all including dummy),-i(inodeInodes IUsed IFree),-h(human98G/9.4G/84G 11%),-T(typeext4/squashfs/tmpfs),-t/-x(include/exclude type) plus case trap-tvs-T. - lsblk reading (9.8): major
8disk vs7loop, minors0/1/…/22,RM0/1,RO0/1,TYPEdisk/part/loop,SIZE100G;fdiskneedssudo, sector512, start2048. - Presentation hints heard earlier still apply: write assumptions, show work in tables where possible, and keep handwriting and formatting clear for grading, because a table makes the
16 = 2 + 14split and the inode walk easier to award marks for. For namei, a three-column table — step, component , working inode — scores well. - Timing — this was the last class before the mid-semester, with no class next week. Wishes for the exams were extended to all courses.
Exam note: Do not spend time learning every fdisk partition type by heart; knowing that L lists types (including FAT12, XENIX, FAT16, FAT32, AIX, NTFS, ZFS) and that p and F show table and free space ( in demo because 100GB was fully partitioned) is the level tested. Focus on the flag meanings above and the 16 = 2 + 14 → offset geometry, which is a reliable two-mark derivation.
Key Industry Applications
Consolidated real-world connections for quick review:
- Real-world: Everything-is-a-file underlies the way Linux exposes devices as files under
/dev, listed withls -li(cfor character,bfor block,-for regular,dfor directory). The sameopen-read-write-closepath handles/etc/passwdand/dev/tty— the uniformity keeps drivers small and tools reusable. - Real-world: The 14-byte fixed entry explains legacy systems and name-collision bugs on older Unix installations; modern ext4, xfs, ntfs and others use variable names to avoid truncation. When you see a vintage tar archive complain about truncated names, it is this limit at work; today’s 255-character limit is the variable-length successor.
- Real-world: The namei walk powers every
openandcaton absolute paths such as/etc/passwdand on relative paths such asexercises/sys_pgm1/test1/mydetails.pxt. Container runtimes and web servers do millions of such walks; understandingW = \text{root vs wd}and the left-to-right stepping explains why symlink attacks must be handled by path reprocessing. - Real-world:
squashfsappears in live USB images and container layers as a compressed read-only file system;tmpfspowers/tmpand fast staging where memory speed matters. Thedf -Trow that showssquashfsat100%is not an alarm — it is a fixed image that is full by design, just like a container layer. - Real-world:
lsblkmajor8for disks and major7for loops is the standard driver numbering seen on any Linux host;RMandROflags let scripts distinguish removable or read-only mounts. Fleet monitoring parses8:0vs7:0…7:22to separate real storage from loop-backed snaps;RO 1marks the squashfs images that should never be written. - Real-world:
fdiskwith sector size bytes and start sector is the usual alignment seen on aDOSlabel100GBdisk with a singlesda1partition. The MB gap is the modern alignment rule that avoids 4K-sector penalties — every cloud VM image you launch inherits it. - Real-world: The inode-plus--byte-block counting explains cold-start latency: the first
catafter boot touches root → home → file inode+block pairs; the secondcathits the buffer cache and avoids disk. Capacity planning usesdf -i(inodes free) alongsidedf -h(blocks free) because a mail spool can exhaust inodes with tiny files while blocks look healthy.
These links stay within the lecture’s scope — each mirrors a flag, number, or walk you already traced: 16 = 2 + 14 at byte offsets , working inode , block , 98G/9.4G/84G 11% and 100% loop rows, MAJ:MIN 8:0/8:1 vs 7:0…7:22, sector and start .
SP Lecture 9 notes · Files, Directories and Device Management
Sections Breakdown
Unix treats every object as a byte-sequence file with open/read/write/close and directories as tables that map names to inode numbers.
Classic Unix directory is a table of 16-byte entries (2 bytes inode number + 14 bytes name) at offsets 0,16,32...; names beyond 14 chars are truncated and can collide; . / .. are fixed entries.
The 2-byte inode number in the directory entry is the only name→content bridge (called a link); the number points to the larger inode structure which fans out to data blocks.
namei converts a path string to an inode by initializing working inode W to root (absolute) or wd (relative) and walking components left-to-right with lookup, release and hop; symlink triggers path replacement.
First cat on an absolute path does disk→memory for each level's inode plus 1K=1024-byte data block, looking up each component (root 2, home 13, hello 53) before file bytes arrive; caching removes repeats.
df reports filesystem use in 1K-blocks with columns Filesystem/Used/Available/Use%; flags -a/-i/-h/-T/-t/-x reshape to dummy, inode, human (98G/9.4G/84G 11%), type (ext4/squashfs/tmpfs) views.
Memory ladder RAM/disk/tape/cache sets stage; ls -li /dev shows c (character stream, no kernel block buffering: RAM/printer/terminal) vs b (block structured 1K=1024 bytes, kernel buffered: disks/tapes) via first letter.
Major selects driver family (8 disk sda/sda1, 7 loop loop0..22) and minor selects instance within; lsblk shows MAJ:MIN RM RO TYPE MOUNTPOINT; fdisk shows 512-byte sectors, start 2048, 100GB DOS label, needing sudo.
Mid-sem exam covers files/directories up to this lecture; almost all questions are command/flag prediction (df/lsblk/fdisk/namei) with old paper on Taksila portal.
Real-world ties: everything-is-a-file to /dev, 16=2+14 to legacy bugs, namei to every open/cat, df types to live images/tmpfs, lsblk majors to fleet parsing, fdisk alignment to cloud images.
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 Directories — The Unix View
Must-know: Everything-is-a-file lets the same four calls handle regular files, directories and devices; a directory holds only name→inode maps, not file bytes.
⚠️ Top pitfall: Thinking a directory contains file bytes instead of just the name-to-inode link; mixing fixed-length classic limit with modern variable names.
Self-check: Why can you list /dev and /home with the same ls logic?
Connects to: 9.2, 9.3
Unix Directory Entry Structure
Must-know: 16 = 2 + 14 and offset_k = 16*k; 14-char truncation causes collision when first 14 chars match; . is current, .. is parent, / is root.
and
⚠️ Top pitfall: Thinking long names are stored fully; under old 16-byte layout only first 14 chars kept and two files with same prefix collide.
Self-check: What byte offset is entry k and what happens to a 20-char name in the classic table?
Connects to: 9.3, 9.4
The Link Between Name and Content — Inodes
Must-know: File name is a link (bridge) to a 2-byte inode number; the inode structure holds mode, owner, times, size, link count and block pointers to data blocks.
⚠️ Top pitfall: Confusing inode number (2-byte table entry) with inode structure (larger record with pointers); or mixing this minimal link with hard/soft link.
Self-check: What is stored in the 2-byte field vs what is in the inode structure?
Connects to: 9.2, 9.4
Path Name to Inode Translation — The namei Algorithm
Must-know: namei: W = root if p starts with / else wd; while c in p: read dir of W, match c to 14-byte name, get N, release W, W←N; symlink replaces prefix and reprocesses.
⚠️ Top pitfall: Treating file extension as separate component; thinking walk is right-to-left; forgetting release step order.
Self-check: For /home/ubuntu/sp/file_name.txt what is the last component and what is W after matching home?
Connects to: 9.5, 9.2
Disk Access Walkthrough — From cat Command to Data Blocks
Must-know: Each directory level costs inode + data block (1K=1024 bytes) fetches; cat /home/spiderman/hello.txt with root 2, home 13, hello 53 needs 8 transfers (4 levels ×2) including root and file.
and
⚠️ Top pitfall: Forgetting root's pair or the file's own inode+block when counting; thinking cached second cat costs same as cold first cat.
Self-check: How many inode and block fetches for cat /home/spiderman/hello.txt with no cache?
Connects to: 9.4, 9.6
Disk Usage Inspection — The df Family
Must-know: df shows 1K-blocks; -a adds dummy, -i shows Inodes, -h shows 98G/9.4G/84G 11% human, -T adds Type (ext4/squashfs/tmpfs), -t/-x filter by type, path narrows to one mount.
⚠️ Top pitfall: Mixing -t (filter include) with -T (show type column); thinking 100% squashfs means disk full by error rather than sized image.
Self-check: Which df flag shows human sizes and what does df -i show instead of blocks?
Connects to: 9.5, 9.8
Unix Input/Output — Character versus Block Devices
Must-know: ls -li /dev: first char c=character (stream, no kernel block buffering) vs b=block (structured blocks, kernel buffers); char examples RAM/printer/terminal, block examples disk/tape.
⚠️ Top pitfall: Claiming character has zero buffering anywhere or that block needs no buffering; tapes are block in this lecture's classification.
Self-check: How to tell block vs character with ls and what buffering rule applies to each?
Connects to: 9.8, 9.1
Device Identification — Major and Minor Numbers, lsblk and fdisk
Must-know: Major 8 = disk driver (sda 8:0, sda1 8:1), major 7 = loop (loop0 7:0 .. 22); lsblk RM/RO/TYPE; fdisk -l needs sudo, sector 512, start 2048, F free 0, L list types, p print.
⚠️ Top pitfall: Forgetting sudo for fdisk -l (permission denied); mixing fdisk -l vs inside L vs p/F; mistaking start 2048 as random not 1MB alignment.
Self-check: What do 8:1 and 7:5 mean in lsblk and why does fdisk without sudo fail?
Connects to: 9.7, 9.6
Exam Guidance Summary
Must-know: Syllabus is files/directories through this lecture; question style is command-output justification; study slides flags and Taksila old paper.
⚠️ Top pitfall: Learning every fdisk type instead of just L lists, p prints, F shows free 0.
Self-check: Which df flag combo gives human size plus type for ext4 only?
Connects to: 9.2, 9.4, 9.6
Key Industry Applications
Must-know: Each lecture concept maps to daily ops: /dev listing, ext4 vs squashfs vs tmpfs, namei for containers, lsblk major parsing, fdisk 1MB alignment.
⚠️ Top pitfall: Treating 100% squashfs as error vs sized image.
Self-check: Why does a second cat cost less than the first?
Connects to: 9.1, 9.5, 9.8
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.