The one thing to understand first
A B-tree index is constantly being modified by many sessions at once, yet a reader almost never has to wait for a writer. PostgreSQL pulls this off with a 1981 idea called the Lehman-Yao B-link tree: every page carries a high key (the largest key it is allowed to hold) and a right-link to its sibling. Those two things let a search that lands on a page just after a concurrent split simply walk right to find its key, instead of locking the whole path. The whole concurrency story is one sentence: readers hold one page lock at a time and recover from races by following right-links.
The shape of a page
In src/backend/access/nbtree/, every B-tree page ends with a BTPageOpaqueData struct (see nbtree.h) holding btpo_prev and btpo_next — the left and right sibling pointers at the same level. On every page except the rightmost on its level, the first item is the high key: an upper bound such that every key on the page is <= the high key. Internal pages additionally hold downlinks (block numbers) to children. Leaf pages hold the actual indexed values plus a TID (heap ctid) pointing at the table row.
A search that never lock-couples
Descent lives in nbtsearch.c. _bt_search() walks from the root down, and at each level _bt_binsrch() binary-searches the page for the child to follow. The crucial trick is _bt_moveright(): after locking a page, the code checks the high key. If the key it is looking for is greater than the high key, that means a split moved the target onto the right sibling after we read the parent’s downlink — so the code follows btpo_next to the right and retries, rather than restarting from the top. Because a reader only ever holds one page lock at a time (no lock-coupling), writers are rarely blocked.