How does a segmentation fault work under the hood?
unix.stackexchange.com
unix.stackexchange.com
There is no mention of the words "page table", and it's very light on use of the word "interrupt".
The answer is a good stackexchange answer in that it's about a page long and covers all the key points. It gives a link to "page" for those who want to understand that subsystem. It's not necessary to include a whole computer architecture course.
And of course this is all architecture dependent.
Edit:
x86 does have a register for defining the location of page tables that it will walk, the it would trap on a miss if the PTE is missing. Idk, if Linux uses this register or not or uses a software defined TLB, or some kind of hybrid approach.
TLB management is done in hardware on x86. TLB misses don't generate exceptions, they just trigger page table walks in hardware, and then if the page table walk fails (e.g. because of a non-present page table entry) it triggers a CPU exception (page fault) that the OS can then handle.
Things like MIPS use a software-managed TLB that works roughly as you describe, though.
The CPU doesn't really know about processes. (Some CPUs have features to manage "tasks", but they are not commonly used 1-to-1 with actual processes, largely for efficiency/performance reasons as I understand it.) The OS tells the CPU that any time it hits a page fault, it should start running code at this address with kernel privilege; this configuration itself also requires kernel privilege. Then it drops privileges and jumps to user code. When the user code makes an invalid memory access, the CPU follows the previous configuration about what to do in the case of an error. That runs a kernel function that delivers the signal (which is an entirely UNIX-specific concept).
If your program doesn't explicitly choose to do anything with a signal, there's a "default disposition" for each signal, either to kill the process (segmentation faults, Ctrl-C, etc.), do nothing (window resized, child process exited), or suspend the process (Ctrl-Z). (Or unsuspend it, in the case of SIGCONT, but SIGCONT is weird.) If you do want to do something with a signal, you can specify a signal handler, which runs asynchronously when a signal is received: in the middle of some code, your program randomly jumps to that handler to deal with the problem. This is the only way to deal with certain conditions like segmentation faults, since there's no way for the program to keep running. But since it could be in the middle of some code, writing signal handlers is extremely tricky because you don't know what the state of the process is. For instance, malloc() usually takes a per-thread lock on the heap: if you were signalled in the middle of an allocation, calling malloc() will deadlock. There are a few other methods of dealing with signals, like sigwaitinfo() or signalfd / kqueue, that don't have this limitation but also can't respond to things like segmentation faults.
If you don't have a signal handler, and the default disposition of the signal is to terminate your process, your exit code will contain the signal that killed you. Your parent process can look at this (usually, it's paying attention for the SIGCHLD signal) and print out a message. That's where the "Segmentation fault" message in the shell comes from: it's after the process is already dead, and the shell, the parent process, is looking at whether it exited cleanly or not.
By the way, note that if a process hasn't overridden the default signal dispositions, there's no difference between terminating it with Ctrl-C (SIGINT), Ctrl-\ (SIGQUIT), kill (SIGTERM), or kill -9 (SIGKILL). It's just that it's impossible to change the handling of SIGKILL, so that's your option if the process has a termination handler that's not working right. But otherwise, all four of them cause the process to immediately exit—and make the kernel do the same cleanup. (It is this cleanup that can get stuck and cause a process to be unresponsive even to kill -9: it means the process is already as dead as it can be, but the kernel is trying to close file descriptors or unmap memory and got stuck doing that.)