Linux operatsioonisüsteemis töötab võrguühendus: erinevus redaktsioonide vahel

Allikas: Imre kasutab arvutit
Mine navigeerimisribaleMine otsikasti
42. rida: 42. rida:
 
* listen()
 
* listen()
   
tulemusena paistab
+
tulemusena paistab (reaalset grep väljundis accept4 ei ole paista)
   
 
<pre>
 
<pre>
terminal 1 - server-01# netcat -l 6001
+
terminal 1 - server-01# netcat -l 6001 2>&1 | egrep "socket|setsockopt|bind|listen|accept"
  +
..
  +
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
  +
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
  +
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
  +
bind(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
  +
listen(3, 1) = 0
  +
accept4(...
  +
...
   
 
terminal 2 - server-02# netstat -anpt | grep netcat
 
terminal 2 - server-02# netstat -anpt | grep netcat

Redaktsioon: 28. mai 2026, kell 15:37

Sissejuhatus

TODO

Tööpõhimõte

Teenuse pakkumise poolelt vaadates toimub seoses võrguühenduse teenindamisega selline sündmuste käik

SOCKET() -> BIND() -> LISTEN() -> ACCEPT() -> READ/WRITE() -> CLOSE()

kus erinevad tarkvaralised lahendused saavad jagada seda järgnevust erinevalt lõikudeks, nt

  • üks netcat protsess tegeleb kõigi teemadega ise
  • postgres isand protsess tegeleb kuni accept juurde, edasi moodustatakse uus protsess ning see töötab lõpuks 'active connected socket' olukorraga
  • sshd/init (ubuntu 24.04) - esialgu kuulab võrgus init protsess port 22/tcp, st on listen olekus, kui saabub esimene pöördumine tuuakse juurde sshd protsess, listen antakse üle (sd_listen_fds tehnika abil)

Väited

  • toodud nähtusi saab omavahel kombineerida (nt nginx-reuseport režiimis kasutab lisaks 'sshd/init' lähenemise elemente)
  • kui toimub võrguühendus, siis serveri poolelt osaleb kaks erinevat socketit: 1. listening socket, 2. active connected socket
  • socket esineb kerneli mälu struktuuri üksusena (sinna on üles kirjutatud metaandmed, nt port number, aga ka andmetega seotud queue'd)

Listening-socket ja active-connected-socket objektidega toimub on sarnane protsessiga toimuvale forkimise olukorras

  • pidavalt on süsteemis olemas nö parent nähtus: vastavalt 1. listen socket, 2. parent protsess
  • teatud olukorras toimub uue objekti tekkimine: 1. saabuva võrguühenduse teenindamisega moodustatakse listen socket alusel koopia; 2. xxx

TODO

Väited

  • haproxy - kasutatakse vaikimisi
  • nginx - vaikimisi ei kasutata, aga soovitatakse kasutada jõudluse suurendamise eesmärgil
  • zabbix-agent2 - ei ole võimalik kasutada

Võrguühendus vaatlemine

Serveri poolel on lähtepunktiks ainult nn 'listening socket', peale netcat protsessi käivitamist ütleb protsess kernelile kolm syscall'i

  • socket()
  • bind()
  • listen()

tulemusena paistab (reaalset grep väljundis accept4 ei ole paista)

terminal 1 - server-01# netcat -l 6001 2>&1 | egrep "socket|setsockopt|bind|listen|accept"
..
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                          = 0
accept4(...
...

terminal 2 - server-02# netstat -anpt | grep netcat
tcp        0      0 0.0.0.0:6001            0.0.0.0:*               LISTEN      21473/netcat

root@zabbix-pub-01:~# lsof -n -P -p 21473
COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF  NODE NAME
netcat  21473 root  cwd    DIR   252,2     4096  8193 /root
netcat  21473 root  rtd    DIR   252,2     4096     2 /
netcat  21473 root  txt    REG   252,2    39560   841 /usr/bin/nc.openbsd
netcat  21473 root  mem    REG   252,2  2125328  7621 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat  21473 root  mem    REG   252,2    55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0
netcat  21473 root  mem    REG   252,2    68104  8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2
netcat  21473 root  mem    REG   252,2    80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1
netcat  21473 root  mem    REG   252,2   236616  7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat  21473 root    0u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    1u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    2u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    3u  IPv4 1514112      0t0   TCP *:6001 (LISTEN)

kus

  • lsof käsk esitab userspace protsessiga seotud avatud failide nimekirja (muu hulgas kuulub nimekirja võrgusoketiga seotud võrguühenduse inode 1514112
  • 1514112 on operatsioonisüsteemis unikaalne globaalne võrgusoketi inode number (ino)
  • 3u on protsessi kontekstis unikaalne file descriptor väärus (3r või 4w oleksid põhimõtteliselt read ja write, u on universal st mõlemad)
  • tegu on listening soketiga, talle vastab mälueraldis jms
  • soket asub ise kernel space'is, aga userspace'i protsessil on temaga kontakt fd abil (selle suhtes teeb protess nt read() ja write() syscall'isid

Peale kliendi ühenduse alustamist st kui ühendus toimub, paistab kliendi arvutis

terminal 1 - klient-01# netstat 192.168.10.193 6001

terminal 2 - klient-01# netstat -anpt | grep netcat
tcp        0      0 192.168.10.196:58384    192.168.10.193:6001     ESTABLISHED 345883/netcat

# lsof -n -P -p 345883
COMMAND    PID USER  FD   TYPE   DEVICE SIZE/OFF   NODE NAME
netcat  345883 root cwd    DIR    253,2     4096     14 /root
netcat  345883 root rtd    DIR    253,2     4096      2 /
netcat  345883 root txt    REG    253,2    35032 419036 /usr/bin/nc.traditional
netcat  345883 root mem    REG    253,2  1999312 403434 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat  345883 root mem    REG    253,2   225600 403431 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat  345883 root   0u   CHR    136,2      0t0      5 /dev/pts/2
netcat  345883 root   1u   CHR    136,2      0t0      5 /dev/pts/2
netcat  345883 root   2u   CHR    136,2      0t0      5 /dev/pts/2
netcat  345883 root   3u  IPv4 29390854      0t0    TCP 192.168.10.196:58384->192.168.10.193:6001 (ESTABLISHED)

kus

  • üks active connected socket oma mälueraldisega kernelis
  • 29390854 on operatsioonisüsteemi jaoks globaalselt unikaalne inode väärtus (ino)

ja serveris kaks võrgusoketit

root@zabbix-pub-01:~# netstat -anpt | grep netcat
tcp        0      0 0.0.0.0:6001            0.0.0.0:*               LISTEN      21473/netcat
tcp        0      0 192.168.10.193:6001     192.168.10.196:58384    ESTABLISHED 21473/netcat

root@zabbix-pub-01:~# lsof -n -P -p 21473
COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF  NODE NAME
netcat  21473 root  cwd    DIR   252,2     4096  8193 /root
netcat  21473 root  rtd    DIR   252,2     4096     2 /
netcat  21473 root  txt    REG   252,2    39560   841 /usr/bin/nc.openbsd
netcat  21473 root  mem    REG   252,2  2125328  7621 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat  21473 root  mem    REG   252,2    55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0
netcat  21473 root  mem    REG   252,2    68104  8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2
netcat  21473 root  mem    REG   252,2    80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1
netcat  21473 root  mem    REG   252,2   236616  7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat  21473 root    0u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    1u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    2u   CHR   136,7      0t0    10 /dev/pts/7
netcat  21473 root    3u  IPv4 1514112      0t0   TCP *:6001 (LISTEN)
netcat  21473 root    4u  IPv4 1514113      0t0   TCP 192.168.10.193:6001->192.168.10.196:58384 (ESTABLISHED)
  • listening socket oma mälueraldisega jms
  • active connected socket oma mälueraldisega
  • võrgusoketi inode numbrid on soketitel erinevad - 1514112 ja 1514113

Reuseport

Osutub, et tcp/ip network stack Linux, Windows, BSD jt implementatsioonid võimaldavad teatud tingimustel mitmel protsessil korraga sama porti kasutada. Seda võimalust kontrollib 'so_reuseport' socket option ('so_').

Python skript

# cat socket-01-server.py
import socket
import os
import sys

PORT = 8083
# Fetch the Process ID so we can easily tell which instance answers the client
PID = os.getpid()

# 1. Create a standard TCP IPv4 Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2. THE MAGIC OPTIONS: Enable Reuse Address and Reuse Port
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

# 3. Bind and Listen (Standard blocking mode)
server_socket.bind(('0.0.0.0', PORT))
server_socket.listen(10)

print(f"[Worker PID: {PID}] Multi-core listening gate opened on port {PORT}...")

try:
    while True:
        # This call BLOCKS completely. The script sleeps right here in the kernel
        # until the Linux 4-tuple hash decides to route a client to THIS specific socket instance.
        client_socket, client_address = server_socket.accept()

        # Read the raw incoming HTTP request payload (up to 4KB)
        request = client_socket.recv(4096)

        if request:
            # Construct a basic HTTP response containing the answering process ID
            body = f"Hello from Worker Process ID: {PID}\n"
            response = (
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/plain\r\n"
                f"Content-Length: {len(body)}\r\n"
                "Connection: close\r\n"
                "\r\n"
                f"{body}"
            ).encode('utf-8')

            # Fire the response back to the client
            client_socket.sendall(response)

        # Close the socket immediately to return the client line
        client_socket.close()

except KeyboardInterrupt:
    print(f"\n[Worker {PID}] Shutting down down gracefully.")
finally:
    server_socket.close()

Nginx

TODO

haproxy

TODO


Kasulikud lisamaterjalid

  • TODO