Reading — step 1 of 5
Read
~1 min readInodes & Files
Read & Write Operations
read(fd, buf, n):
- Look up open-file table entry (per-process FD → kernel struct file).
- Get inode + current offset.
- Compute starting (logical_block, byte_offset).
- For each block of data needed:
- Resolve logical_block → physical_block via extents.
- Read into kernel buffer (page cache).
- Copy to user buffer.
- Update offset.
- Return bytes read.
ssize_t do_read(struct file *f, char *buf, size_t n) {
struct inode *ip = f->inode;
size_t total = 0;
while (n > 0 && f->offset < ip->size) {
size_t in_block = f->offset % BSIZE;
size_t to_read = min(min(n, BSIZE - in_block), ip->size - f->offset);
struct buffer *b = bread(ip, f->offset / BSIZE);
memcpy(buf + total, b->data + in_block, to_read);
brelse(b);
f->offset += to_read;
total += to_read;
n -= to_read;
}
return total;
}
write(fd, buf, n):
- Similar to read.
- May need to ALLOCATE new blocks if file grows.
- Update inode's size, mtime, blocks count.
Page cache:
- All reads/writes go through it.
breadreturns cached buffer; if not present, reads from disk.- Writes mark buffer dirty; flushed later.
- Saves disk I/O dramatically.
Direct I/O (O_DIRECT):
- Bypass page cache.
- App provides aligned buffer.
- Used by databases (already cache themselves).
Read-ahead:
- After sequential read, prefetch next blocks asynchronously.
- Adaptive: detects sequential vs random pattern.
mmap:
- Map file into process address space.
- Page faults trigger reads.
- Writes mark page dirty; flushed.
- Good for random access; bad for streaming.
fsync(fd):
- Force flush all dirty data + metadata for this file to stable storage.
- Critical for durability (databases call after every commit).
- Slow (waits for disk).
fdatasync: flush data only, not metadata (size unchanged).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…