Disks & Partitions
fdisk, lsblk
/proc/partitions
Key Terms:
How a Hard Disk is Built Physically
A traditional hard disk drive (HDD) is a mechanical device. Data is stored magnetically on spinning platters. Understanding this helps you understand why disk I/O is slow compared to RAM.
| Hard Disk Drive | ||||
| Platter 1 Magnetic disk |
Platter 2 Magnetic disk |
Read/Write Head Moves radially |
Spindle Motor 7200 RPM typical |
Controller Talks to OS |
| On each platter surface:
Tracks (concentric circles)
→
Sectors (track slices)
→
Physical Blocks (512 bytes each)
|
||||
Three Steps to Read Data from Disk:
Seek Time
Head moves to correct track
(~5-10ms)
Rotational Latency
Wait for sector to rotate under head
(~4ms avg)
Transfer Time
Read/write the actual data
(fastest part)
A single physical disk is divided into partitions — each treated by the kernel as a separate device. The partition table (stored at the start of the disk) tells the OS where each partition begins and ends.
| Physical Disk (/dev/sda) | |||||
| Partition Table /dev/sda (MBR) |
/dev/sda1 ext4 filesystem (Linux root /) |
/dev/sda2 ext4 filesystem (/home) |
/dev/sda3 swap area (virtual RAM) |
/dev/sda4 NTFS (Windows) |
|
Three Uses for a Partition:
Contains regular files and directories. Created with
mkfs. Mounted with mount.Used directly by databases (e.g., Oracle raw devices) bypassing the OS file system for performance.
Used by the kernel as overflow for RAM. Created with
mkswap, activated with swapon./proc/partitions lists all disk partitions the kernel knows about with their major/minor IDs and sizes.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp;
int major, minor;
unsigned long blocks;
char name[64];
fp = fopen("/proc/partitions", "r");
if (fp == NULL) {
perror("fopen /proc/partitions");
return 1;
}
/* Skip header lines */
char line[256];
fgets(line, sizeof(line), fp); /* "major minor #blocks name" */
fgets(line, sizeof(line), fp); /* blank line */
printf("%-8s %-8s %-12s %s\n",
"Major", "Minor", "Blocks(1K)", "Name");
printf("%-8s %-8s %-12s %s\n",
"-----", "-----", "----------", "----");
while (fscanf(fp, "%d %d %lu %63s",
&major, &minor, &blocks, name) == 4) {
printf("%-8d %-8d %-12lu %s\n",
major, minor, blocks, name);
}
fclose(fp);
return 0;
}
/* Sample output:
Major Minor Blocks(1K) Name
----- ----- ---------- ----
8 0 976762584 sda
8 1 524288 sda1
8 2 976236544 sda2
*/
Use BLKGETSIZE64 ioctl to get the exact size of a block device in bytes.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h> /* BLKGETSIZE64 */
#include <stdint.h>
int main(int argc, char *argv[]) {
int fd;
uint64_t size_bytes;
const char *device;
device = (argc > 1) ? argv[1] : "/dev/sda";
fd = open(device, O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
/* BLKGETSIZE64: fills size_bytes with total bytes on device */
if (ioctl(fd, BLKGETSIZE64, &size_bytes) == -1) {
perror("ioctl BLKGETSIZE64");
close(fd);
return 1;
}
printf("Device: %s\n", device);
printf("Size (bytes): %llu\n", (unsigned long long)size_bytes);
printf("Size (MB): %.2f\n", size_bytes / (1024.0 * 1024.0));
printf("Size (GB): %.2f\n", size_bytes / (1024.0 * 1024.0 * 1024.0));
/* Calculate number of 512-byte sectors */
printf("512-byte sectors: %llu\n",
(unsigned long long)(size_bytes / 512));
close(fd);
return 0;
}
/* Run as root: ./disk_size /dev/sda
Device: /dev/sda
Size (bytes): 1000204886016
Size (MB): 953869.39
Size (GB): 931.51
512-byte sectors: 1953525168
*/
The swapon() and swapoff() system calls add/remove swap partitions at runtime. Usually called by the init system at boot.
#include <stdio.h>
#include <unistd.h>
/* These syscall wrappers may not be in all libc versions,
so we show how to use the shell commands too */
/* Check current swap usage: /proc/swaps */
void read_swap_info(void) {
FILE *fp;
char line[256];
fp = fopen("/proc/swaps", "r");
if (!fp) { perror("fopen"); return; }
printf("=== Current Swap Areas (/proc/swaps) ===\n");
while (fgets(line, sizeof(line), fp))
printf("%s", line);
fclose(fp);
}
int main(void) {
read_swap_info();
/* Shell equivalent commands:
To create a swap file:
dd if=/dev/zero of=/swapfile bs=1M count=1024
chmod 600 /swapfile
mkswap /swapfile -- format as swap
swapon /swapfile -- activate swap
To deactivate:
swapoff /swapfile
To see all active swap:
cat /proc/swaps
free -h -- shows Swap: row
*/
return 0;
}
/* /proc/swaps sample output:
Filename Type Size Used Priority
/dev/sda3 partition 4194300 0 -2
/swapfile file 1048572 0 -3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
/* MBR (Master Boot Record) is 512 bytes at offset 0 of disk.
Partition table starts at offset 446.
Each entry is 16 bytes, there are 4 entries. */
typedef struct {
uint8_t status; /* 0x80 = bootable */
uint8_t chs_first[3]; /* CHS of first sector */
uint8_t type; /* Partition type code */
uint8_t chs_last[3]; /* CHS of last sector */
uint32_t lba_start; /* LBA start sector */
uint32_t sector_count; /* Number of sectors */
} __attribute__((packed)) PartEntry;
void decode_type(uint8_t type) {
switch (type) {
case 0x00: printf("Empty"); break;
case 0x82: printf("Linux swap"); break;
case 0x83: printf("Linux ext"); break;
case 0x07: printf("NTFS/exFAT"); break;
case 0x0B: printf("FAT32 CHS"); break;
case 0x0C: printf("FAT32 LBA"); break;
case 0x05: printf("Extended"); break;
default: printf("Unknown(0x%02x)", type); break;
}
}
int main(int argc, char *argv[]) {
int fd;
uint8_t mbr[512];
PartEntry *pt;
int i;
const char *device = (argc > 1) ? argv[1] : "/dev/sda";
fd = open(device, O_RDONLY);
if (fd == -1) { perror("open"); return 1; }
if (read(fd, mbr, 512) != 512) {
perror("read MBR"); close(fd); return 1;
}
close(fd);
/* Verify MBR signature */
if (mbr[510] != 0x55 || mbr[511] != 0xAA) {
printf("No valid MBR found!\n");
return 1;
}
printf("Device: %s\n", device);
printf("MBR signature: 0x%02x%02x (valid)\n\n",
mbr[510], mbr[511]);
printf("%-6s %-12s %-12s %-12s %s\n",
"Part", "Start(LBA)", "Sectors", "Size(MB)", "Type");
/* Partition table entries start at offset 446 */
pt = (PartEntry *)(mbr + 446);
for (i = 0; i < 4; i++) {
if (pt[i].type == 0) continue; /* Skip empty */
double size_mb = (double)pt[i].sector_count * 512.0
/ (1024.0 * 1024.0);
printf("%-6d %-12u %-12u %-12.1f ",
i + 1,
pt[i].lba_start,
pt[i].sector_count,
size_mb);
decode_type(pt[i].type);
if (pt[i].status == 0x80) printf(" [BOOT]");
printf("\n");
}
return 0;
}
/* Run as root: ./read_mbr /dev/sda */
🎯 Interview Questions — Disks and Partitions
Q1. What are the three latency components when reading from a hard disk?
Seek time (head moves to the right track), rotational latency (waiting for the sector to rotate under the head), and transfer time (actual data transfer). Seek + rotational latency dominates and is typically 10–20ms — millions of CPU cycles.
Q2. What is a disk partition? How does the kernel see it?
A partition is a contiguous region of a disk defined in the partition table (MBR or GPT). The kernel treats each partition as an independent block device — e.g., /dev/sda1, /dev/sda2. The fdisk command manages partitions; /proc/partitions lists them.
Q3. What are the three uses of a disk partition in Linux?
1) As a filesystem (containing files/directories, created with mkfs, mounted with mount). 2) As a raw device for direct database access bypassing the OS filesystem. 3) As a swap area (virtual memory extension, created with mkswap, activated with swapon).
Q4. What is the physical block size on a hard disk?
Typically 512 bytes (or 4096 bytes on modern Advanced Format drives). This is the smallest unit the disk hardware can read or write atomically. Above this, the OS filesystem defines its own “logical block” which is a multiple of the physical block size.
Q5. How do you check active swap areas from the command line?
cat /proc/swaps shows all active swap areas with their type (partition or file), size, and usage. The free -h command also shows total swap in a summary. swapon –show is another option.
Q6. What command shows partition information and how does it work internally?
fdisk -l reads the MBR (first 512 bytes) of the disk, validates the 0x55AA signature, and parses the four 16-byte partition table entries at offset 446. Each entry has start LBA, sector count, and partition type byte. Modern tools also handle GPT (GUID Partition Table) for disks over 2TB.
