Home | Search


2026-01-21

My notes of Operating Systems Course by IIT Delhi

Disclaimer

Notes

https://www.youtube.com/watch?v=WnTsKB1mI3s&list=PLsylUObW5M3CAGT6OdubyH6FztKfJCcFB&index=4 https://beej.us/guide/bgipc/html/

fork() is the primary way of creating new process in linux, Creates a child process with cloned program, execution flow, and the whole environment, but assigns a new pid, and both processes run at same time (they're "logically" running simultaneously even if not "physically", OS can share the same physical core for them by taking turns). after calling fork(), check if pid is non-zero to identify if you are parent or child now.

exec() also creates new process but it terminates the parent process

working with streams:

/proc/{pid} is pseudofilesystem in RAM that lets processes store information, or OS to provide information to process

threads share address space but have distinct execution flow

process leak - child process calls exit() but parent process has not called wait() to get the exit code, hence OS will keep the exit code of child in memory (potentially for months)

orphaned processes - if parent calls exit() before child calls exit(), child now has init process as parent and init will repeatedly call wait() to cleanup all child processes

At hardware level, hardware bus interfaces between CPU, disk, RAM, etc

RAM has a memory management unit (MMU) that does some memory accounting, address translation, access control etc

x86 8086 architecture 16-bit registers, but can also use half a register for 8-bit values Ax, BX, DX, CX - 4 standard 16-bit registers SP, BP, SI, DI - 4 standard 16-bit registers PC - program control 16-bit register - stores reference to next instruction to run FLAGS - register to track floating point overflows, positive/negative values etc, system-level information - cannot be written directly segment registers - add 4-bits more to PC, so that total 20-bits can be used to address 2^20 memory locations

later extended to 32-bit architecture with backwards compatibility (for existing OSes and compilers written on older hardware) EAX, EBX, EDX, ECX - 4 standard 32-bit registers

today 64-bit architectures are the norm as RAM grew bigger than 4 GB (2^32 bytes) eventually

backwards compatibility: 0x66 mov (%eax) %ebx => mov (%eax) %bx, 0x67 mov (%eax) %ebx => mov (%ax) %ebx

AT&T syntax => C-ish equivalent movl %eax, %edx => edx=eax; # store contents of eax movl $0x123, %edx => edx=123; # store 123 as a constant value movl 0x123, %edx => edx=(int)123; # treat 123 as an address, dereference it, store contents movl (%ebx), %edx => edx=(int)ebx # treat contents of ebx as an address, dereference it, store contents movl 4(%ebx), %edx => edx=(int)(ebx+4) # treat contents of ebx as address, add 4 bytes offset to get another address, dereference it, store contents

"l" indicates 32-bit or 64-bit, this gets converted to 0x66 or 0x67 or nothing as needed when written in binary

push/pop contents of stack. %esp is stack pointer. pushl %eax popl %eax => movl (%esp), %eax; add $4, %esp

call $0x12345 => pushl $eip; jmp 0x12345 # push incremented instruction pointer (/program control) to stack, jump to instruction stored at specified address ret => popl %eip; # function return i.e. pop previously stored instruction pointer from stack and use it to continue the program [These are fake instructions for explanation purpose, %eip is not accessible in real life]

GCC calling convention: at function entry (call), %eip points to 1st instruction in function, %esp points to return address, (%esp+4) points to first argument to function at function exit (ret), %eip points to return address, %esp points to arguments pushed by call, %eax contains return value

function can mutate or trash %eax, %edx, %ecx (caller-saved registers) function cannot mutate %ebx, %ebp, %esi, %edi (callee-saved registers)

ebp points to esp position at start of function call, esp is now free to be used for running the contens of the function ebp can be used to get function arguments anywhere in between the function run, also ebp is need after exiting the function function entry: push %ebp; mov %esp, %ebp function exit: mov %ebp %esp, popl %ebp, ret ebp is not strictly needed because compiler can remember how many items the function run has added to the stack, so it can calculate ebp from esp at any time. but it is convenient

GCC compiler workflow: .c files --compiler--> .s assembly files --assembler--> .o object files (binary) --linker--> one single binary file --loader--> starts execution of this binary

I/O space

Physical address space

memory translation

interrupt descriptor table

problems with just doing memory segmentation as discussed so far:

f maps virtual page nos to physical page nos

p_addr = f(v_addr // 2^p) >> p + v_addr % 2^p

typical page size = 2^12 bytes = 4 KB typical virtual memory size = 2^32 bytes = 4 GB (approx) => typical number of pages = 2^20 => depth=2 hierarchy to map virtual to physical pages => 2^10 entries in page directory, 2^10 entries in each page table => 2^12 bytes are covered by a single entry in page table

segmentation is no longer used in 64-bit architecture, paging is the primary method segmentation is hardware-based, paging is software-based

privilege levels

demand paging (optimisation)

copy-on-write (optimisation)

Translation lookaside buffer (TLB)

Large pages

Boot sector

xv6 boot sector code bootmain.s https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S

bootmain.c https://github.com/mit-pdos/xv6-public/blob/master/bootmain.c

$ objdump -p kernel # get elf program headers
LOAD: vaddr 0x80100000, paddr 0x100000, align 2^12, filesz 0xb596, memsz 0x126fc
# ignore first 1 MB of physical memory which is for console
# load these many bytes from disk to memory in 2^12 size page table
$ objdump -f kernel # get elf entry address from elf header
0x10000c
# first instruction to execute

entry.S (Kernel entry) https://github.com/mit-pdos/xv6-public/blob/master/entry.S

main.c (Kernel) https://github.com/mit-pdos/xv6-public/blob/master/main.c

Quickly skimming through next few lecture notes Possibly important points:

Concurrency notes

Disk

Example: increase throughput of static web server hosted on multicore machine

Useful concurrency abstractions

Conditional variables

Semaphore

Transactional approach

Reader-writer lock

xv6 implementation of concurrency and multiprocess communication

xv6 spinlock https://github.com/mit-pdos/xv6-public/blob/master/spinlock.c

xv6 implementation of sleep/wakeup discussed in detail, did not make detailed notes

xv6 parent and child process

xv6 kill implemented in proc.c https://github.com/mit-pdos/xv6-public/blob/master/proc.c example of a place where killed bit is checked (this example is inside trap) https://github.com/mit-pdos/xv6-public/blob/master/trap.c

IDE Disk driver https://github.com/mit-pdos/xv6-public/blob/master/ide.c

Demand paging

Thrashing

mmap (memory mapped files)

Samuel:

Storage devices

DIsk controller - order of serving I/O requests

Database versus filesystem

Filesystem

Durability semantics

Allocation of disk sectors to files

xv6 filesystem

Filesystem operations

Filesystem locking

Buffer cache replacement policy

xv6 filesystem optimisations that don't exist in it

Disk buffer cache size

Filesystem durability on system crash

ext3 filesystem

ext4 filesystem

fsync() syscall

Operating System Security

UNIX filesystem ACLs

Process scheduling

PAUSED AT END OF LECTURE 37

AI generated notes on some xv6 memory management lecture o BOOTING AND INITIAL PHYSICAL MAPPING


livelock

cache coherence protocol

Lock-free sync using compare-and-swap

Non-preemptibility in Linux

PAUSED AT LECTURE 39 16:52

Subscribe

Enter email or phone number to subscribe. You will receive atmost one update per month

Comment

Enter comment