> For the complete documentation index, see [llms.txt](https://docs.wehost.co.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wehost.co.in/blog/from-c++-to-the-linux-kernel-connecting-the-layers.md).

# From C++ to the Linux Kernel: Connecting the Layers

I’ve been learning C++, but somewhere between pointers, streams and sockets, I realised the more interesting lesson was understanding how the layers underneath Linux connect.

Consider a seemingly simple line:

```cpp
int fd = socket(AF_INET, SOCK_STREAM, 0);
```

It looks simple:

```
AF_INET      -> IPv4
SOCK_STREAM  -> byte-stream socket
socket()     -> create a socket
fd           -> returned file descriptor
```

But what does `fd` actually represent?

### Following the Socket

I created a socket and kept the process alive:

```bash
python3 -c 'import socket,time,os; s=socket.socket(); print("PID:",os.getpid(),flush=True); time.sleep(300)' &
PID=$!
```

Then inspected its file descriptors:

```bash
ls -l /proc/$PID/fd
```

And there it was:

```
0 -> /dev/pts/1
1 -> /dev/pts/1
2 -> /dev/pts/1
3 -> socket:[15292]
```

FD `3` is not the socket itself. It is the process’s handle to a kernel-managed socket object.

The picture starts looking like this:

```
Application
    |
    | socket()
    v
System-call interface
    |
    v
--------------------
   Kernel boundary
--------------------
    |
    v
Process FD table
    |
    | FD 3
    v
Kernel socket object
    |
    v
TCP/IP stack
```

### Watching the Boundary

Then I ran the same experiment through `strace`:

```bash
strace -e trace=socket,close,write,clock_nanosleep \
python3 -c 'import socket,time,os; s=socket.socket(); print("PID:",os.getpid()); time.sleep(5)'
```

<figure><img src="https://556493038-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxMLCiQqJJztvsDM2LNoE%2Fuploads%2FcnN0WDjepSOjh11s6PLC%2FScreenshot%202026-08-17%20at%2015.32.15.png?alt=media&amp;token=10013a29-b06a-49dc-bac8-27abf20cc582" alt=""><figcaption></figcaption></figure>

The interesting part:

```
close(3) // python calling multiple liabires 
socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 3 
write(1, "PID: 1729\n", 10) = 10 // the print stantment
clock_nanosleep(...)  // the sleep statement
close(3) = 0 // program closes
```

Now the layers connect.

```
C++:
int fd = socket(...);

        |
        v

strace:
socket(...) = 3

        |
        v

/proc/<PID>/fd:
3 -> socket:[15292]
```

`strace` shows the interaction with the kernel.

`/proc/<PID>/fd` shows the resulting process state.

Even something as ordinary as `print()` eventually becomes:

```
print(...)
   |
   v
stdout
   |
   v
write(1, ...)
      ^
      |
    FD 1
```

And suddenly pipes and redirection make more sense too.

```bash
program > output.txt
program1 | program2
```

Underneath, the shell is manipulating file descriptors and connecting processes together.

### The Bigger Lesson

None of these concepts are particularly obscure individually:

```
C++
POSIX
syscalls
file descriptors
/proc
sockets
pipes
strace
networking
```

The interesting part is understanding how they connect.

```
Code
 -> library/runtime
 -> syscall
 -> kernel
 -> process state
 -> kernel object
 -> network/filesystem/device
```

That is what I’m finding valuable about learning C++.

Not simply learning another programming language, but developing the ability to follow an operation through the system.

Linux starts becoming less of a collection of commands to memorize and more of a system whose behaviour you can trace, observe and reason about.
