orm@doc-tcpip.org

Erstellt: Februar 1999 - Letzte Modifikation: Mai 2003

[ Main | Local ]


Der "thewall" Parameter

Was man so wissen sollte.

Thewall und alles was dazugehört

Was es ist:
Der Rechner hat eine gewisse Menge an RAM. Diesen RAM müssen sich alle Dienste und Applikationen auf dem System teilen - wobei es einige Dienste gibt, die für das Funktionieren des Systems essentiell sind, und die daher bevorzugt behandelt werden. Das Stichwort hier ist reservierter RAM und gepinnter RAM.
Gepinnt ist Speicher dann, wenn er nicht ausgelagert (gepaged oder geswapped) werden darf.
Reserviert ist RAM, wenn ein Dienst bestimmte Speichermengen bevorzugt belegen und eventuelle andere Nutzer vertreiben darf.

Die Netzwerkdienste (dazu zählen alle Verbindungen, auch über den Loopback, sowie zB. auch Terminals und Logins) haben so ein Vorrecht. Sie haben also einen Speicherbereich, indem sie andere Nutzer rauswerfen dürfen. Und "thewall" ist die Grenze, die diesen reservierten Bereich definiert. Das heißt nicht, das dieser Speicher für den normalen Betrieb verloren ist. Die Netzwerkdienste nehmen nur das, was sie im Moment brauchen - solange, bis sie an eine Wand, Thewall, stossen.

Damit habe ich den Rest des Systems vor einem amoklaufenden Netzwerkstack geschützt. Jetzt muß ich noch die einzelnen Prozesse des Netzwerkstacks gegeneinander schützen, dh. ich muß verhindern, das einer all das Memory auffrißt.

Dazu als Hintergrund: Eine TCP-IP Verbindung, eine UDP Verbindung, ein Terminal, ein Login etc. öffnen einen Socket. Unter Unix ist alles ein File, also auch ein Socket. Im Fall einer TCP-Verbindung wird der Socket über Port und IP-Adresse definiert, und die Applikation macht dann nichts anderes, als auf den Socket wie auf ein File zu schreiben. Dabei werden die Daten in einer Kette von "mbufs" abgelegt - bei größeren Mengen auch in "mclustern". Ein Applikation, die schlecht läuft, könnte nun den ganzen Speicher der Netzwerkservices vollschreiben. Deshalb gibt man ein Limit pro Socket vor, also quasi einen "Thewall" im kleinen - sb_max (Socket Buffer Maximum).

Was kann jetzt noch passieren?

Es können viele, neue Verbindungen alles Memory aufessen. Damit wäre niemandem gedient, den alle hätten zwar einen Socket, könnten aber nichts damit anfangen, weil der Speicher ja schon weg ist. Dafür gibt es den sockthresh (Socket Treshhold) Parameter. Das ist ein Anteil in Prozent des von Thewall begrenzten Speichers. Diesen Anteil dürfen die Prozesse bzw. Sockets, die schon offen sind und arbeiten, weiter benutzen. Will man aber einen neuen Socket aufmachen, bekommt man eine Fehlermeldung und es geht nicht.

Es geht also darum, bestehenden Sockets den Rücken für das problemlose Arbeiten frei zu halten - und zwar dadurch, das man, obwohl noch Speicher da ist, das Öffnen von Sockets verhindert.

Parameter und Tuning

Hier das ganze, um es klarer zu machen, im Bild

Der Kasten ist das gesamte Real Memory des Rechners. Auf dieser Maschine ist der Defaultwert für Thewall eingestellt: die großen X zeigen diese Grenze. Die kleinen s zeigen den Socket Threshold, also bis wohin es möglich ist, Sockets zu öffnen. Die @ sind effektiv belegter Speicher, die * zeigen freien aber noch für die Netwerkservices gepinnten Speicher an. Die kleinen x zeigen die Grenze dieses wirklich allokierten Bereiches an. Der Rest ist freier Speicher.

Ausgangssituation:
-----------------------s---X-----------------------------
|@@@@**x               s   X                            |
|@@@@**x               s   X                            |
-----------------------s---X-----------------------------

Auf diesem System ist wenig Aktivität im Netzwerkbereich (@). Der Memory Manager hat sich aber trotzdem einen Speicher als Puffer gesichert (*). Der Rest des Bereiches unterhalb von Thewall (X) steht dem System zur freien Verfügung. Der Kernel weiss allerdings, das Daten unterhalb von thewall sofort ausziehen müssen, wenn die Netzwerkservices diesen Platz haben wollen. Das geht natürlich nur, wenn im entsprechenden Clock Cycle der Memory Manager seinen Bedarf geprüft hat.

-----------------------s---X-----------------------------
|@@@@@@***x            s   X                            |
|@@@@@@***x            s   X                            |
-----------------------s---X-----------------------------

Das ist ein Paar Clock Cycles später: der Netzverkehr hat zugenommen, der Memory Manager hat sich neuen Speicher besorgt, und hat seinen Puffer erweitert.

-----------------------s---X-----------------------------
|@@@*****x             s   X                            |
|@@@*****x             s   X                            |
-----------------------s---X-----------------------------

Wieder ein paar Cycles weiter: Die Pakete sind raus, aber der Speicher vom Memory Manager noch nicht wieder freigegeben.

-----------------------s---X-----------------------------
|@@@*x                 s   X                            |
|@@@*x                 s   X                            |
-----------------------s---X-----------------------------

Der Memory Manager ist gelaufen und hat den effektiv freien Speicher auch wieder freigegeben.

Wie kommt es jetzt zu den "mbuf low" oder "out of mbuf" bzw. "mbuf denied" Nachrichten?

Das kommt stark auf die Applikation an, also wie sie programmiert ist. Es gibt Applikationen, die sich nicht für Low Mbuf interesieren und es einfach weiter probieren, Andere geben diesen Fehler sofort weiter. Der Zähler im netstat läuft immer hoch.

In jedem Fall ist der Memory Manager, als er einem Prozess/Socket Speicher geben wollte, entweder gegen Thewall gestossen oder sein Puffer war voll - also sein eigener, kleiner Thewall (kleine x). Das kann bei einem plötzlichen Burst auf dem Netzwerk passieren, denn der Memory Manager braucht ja einige Clock Cycles, um sein Memory entsprechend zu verwalten:

-----------------------s---X-----------------------------
|@@@@x                 s   X                            |
|@@@@x                 s   X                            |
-----------------------s---X-----------------------------

Wenn in diesem Moment jemand mbufs haben möchte, dann würde es ein "denied" geben. Das ist nicht der normale Fall, da das System eigentlich schnell genug ist, um auf Änderungen entsprechend und zeitnah zu reagieren.
Es gibt also auch weit vor einem Zusammenstoß mit thewall Fälle von Mbuf-Fehlern - immer dann, wenn der Memory Manager erst einige "normale" Applikationen aus dem Speicher werfen muss und den dann freien Speicher stehlen kann. Das kostet einige Zyklen.

-----------------------s---X-----------------------------
|@@@@@@@@@@@@@@@@@@@@@@s   X                            |
|@@@@@@@@@@@@@@@@@@@@@@s   X                            |
-----------------------s---X-----------------------------

Und das wäre der klassische Fall - Der Speicher ist bis zum Socket Treshold voll. Es gibt keine Möglichkeit mehr, neue Sockets aufzumachen. Eine Verbindung von aussen wird einfach fallengelassen, bei einem Versuch, auf dem System einen Socket zu öffnen, bekommt man ENOBUFS, mbuf denied. Bestehende Sockets können diesen Teil des Memory noch nutzen, sie würden einen Fehler bekommen, wenn sie an Thewall stossen.

Wie kann ich das am lebenden System sehen?

Mit netstat -m oder mit dem crash Kommando.


#netstat -m
93 mbufs in use:
64 mbuf cluster pages in use
279 Kbytes allocated to mbufs
0 requests for mbufs denied
0 calls to protocol drain routines
0 sockets not created because sockthresh was reached
 

Das erklärt sich eigentlich selbst. Das System nutzt 93 mbufs und 64 cluster. Das sind insgesamt 279 KB. Das ist leicht zu rechnen:
93 * 256 Byte + 64 * 4096 Byte (die Cluster) = 285952 Byte.
Das durch 1024 gibt 279.25. Schaut man mit dem crash Kommando (wie unten) nach dem gesamten alloziierten Speicher (1499136 Byte) so sieht man, das der Puffer 1213184 Byte sind (also 1184.75 KB).

Man bekommt dann noch pro CPU eine ausführliche Statistik, einmal nach mbuf/mcluster Größe geordnet (alles unter 256 ist ein mBuffer, alles darüber sind Cluster). Man sieht die High- und Low-Water Marks, und man kann sehen, wieviele Anforderungen pro Klasse erfolgten.

Etwas genauer in Sachen Highwater mark:
hiwat: Hochwassermarke für freie Stücke mbuf in einem Bereich. Teile < 4096: wenn die freien mbufs > als hiwat werden, fasst man die Teile zu einer Page zusammen und schlägt diese Stücke dann dem 4096 Byte Bereich zu. Teile >= 4096: wird hier hiwat erreicht, geht der Speicher an das System zurück ==> unpinned.

 
Kernel malloc statistics:

******* CPU 0 *******
By size       inuse     calls failed    free   hiwat   freed
32              637    428721      0     131     640       0
64              139     13172      0      53     320       0
128             166     20079      0      58     160       1
256             470  53473153      0     202     384       0
512             408   1345018      0      32      40      33
1024             72    175153      0      36     100       0
2048              1   1340834      0      53     100       0
4096             67     24248      0       6     120       0
8192              7     12233      0       6      10       0
16384             1      2231      0      22      24      52
32768             1         1      0       0     511       0
 

Dann ist das ganze noch nach Art des Service geordnet, was oft auch sehr Hilfreich sein kann:

 
By type       inuse     calls failed  memuse  memmax  mapb
mbuf             93  52929700      0   23808   74496     0
mcluster         64   3344152      0  262144  394496     0
socket          419    113548      0  149408  186848     0
pcb             149     27060      0   30656   47488     0
routetbl        459       704      0   49664   49664     0
fragtbl           0    371383      0       0     832     0
ifaddr           27        27      0    3104    3104     0
mblk            177     18582      0   37120   44416     0
mblkdata          9     11358      0   57472  182400     0
strhead          40       510      0   11360   13920     0
strqueue         57      1433      0   29184   35840     0
strmodsw         25        25      0    1600    1600     0
strpoll           0         1      0       0      32     0
strosr            0       514      0       0     512     0
strsyncq         66      4251      0    7776    9504     0
streams         186      1245      0   27200   31360     0
file              1         1      0     128     128     0
devbuf            1         1      0     256     256     0
kernel table    176     10316      0  132960  133472     0
locking           3         3      0     384     384     0
temp             13        19      0   11840   12352     0
mcast opts        2         7      0     256     384     0
mcast addrs       2         3      0     128     128     0
 

Unter AIX432 und später muss man den no-Parameter "extendednetstat" auf 1 setzen. Das wird in der rc.net defaultmässig auf 0 gesetzt, um bei Mehrprozessor-Maschinen Zeit zu sparen. Man muss danach rebooten, sonst gelten die Werte nicht. Den es ist ein Kernel-Tabelle, die da über eine Kernel Extension ausgelesen wird!!

Mit dem crash-Kommando geht das so:
od thewall 1 dec ==> thewall (also der Netzwerk-Puffer) in kB
od allocated 1 dec ==> tatsächlich alloziiert (pinned) in Byte
od kememsize 1 D ==> momentan belegter Speicher (pinned und swapable)
Man startet dazu crash und tippt die Befehle ein. Das D bzw. dec rechnet in brauchbare Zahlen um.

Unter AIX 5L geht es mit dem kdb:
Man öffnet den kdb und fragt das Symbol thewall ab. Man erhält die Adresse in Hex, sowie die Adresse des Eintrages im TOC der Symbole. Interessant ist Adresse des Symboles, denn damit kann man es abfragen (dump Befehl):

 
(0)> nm thewall
Symbol Address : 00209820
   TOC Address : 002093C8
(0)> d 00209820 
thewall+000000: 0000 FDE8  0000 0000  0000 7FFC  0000 0000   ................
(0)> cal 40000
Value hexa: 0000FDE8         Value decimal: 65000
(0)>
Der Wert für thewall sind die ersten beiden Wörter (0000 FDE8). Das kann man mit dem cal-Befehl umrechnen und kommt so auf 65000 Byte.

Für Leute, die es ganz genau wissen wollen

Der ganze mbuf Kram findet sich in mbuf.h und machparam.h. Und selbstverständlich in TCP/IP Illustrated Vol.2 von Meister STEVENS.

Zu sb_max muß man noch folgendes sagen:
Der entsprechende Systemcall zum öffnen eines Sockets ist setsockopt(). Da fällt man manchmal drüber, wenn man versucht, Iptrace zu starten und sb_max unter 1 MB hat.

Wenn setsockopt() versucht, einen Socket Buffer (entweder Send oder Receive) größer als sb_max zu öffnen, dann gibt es den Error 74 (No buffer space available - siehe errno.h).

Mit diesem Systemcall ist es möglich, das eine Applikation ihren eigenen tcp_sendspace / tcp_recvspace - unabhängig von den Einstellungen via no-Kommando, aber immer im Rahmen von sb_max - einstellt. (setsockopt(SO_SNDBUF) und (SO_RCVBUF)).

Wenn einer von beiden Sockets versucht, über sb_max zu gehen, dann gibt es den Error ( setsockopt -: There is not enough buffer space for the requested socket operation.)

Um es nochmal klar zu sagen: Alle Sockets in /usr/include/sys/socket.h können die eingestellten no-Parameter durch setsockopt() überspielen.

How this relates to thewall: OID Once the network options tcp_sendspace, and tcp_recvspace RAL are set, then inetd is stopped/restarted (so it picks them up, and passe them on to every subserver it starts) - Every telnet session into that machine will inherit those size send/recv buffers. PAGE This is visible via crash/ndb/tcb output as the number after "hiwat:" 4 The actual "mbufs in use" shown by netstat -m output will not begin OF to visibly climb until that much memory is actually needed to do the 86 buffering on that connection, so an mbuf failure is not likely to occur until long after the connection is established, and is well underway, and the mbuf pool starts to near exhaustion. (sockthresh defaults to 85% of thewall). Even though telnet sessions are not likely to fill those buffers, (because of telnet's character-at-a-time oriented nature). -the potential IS THERE to consume the entire 1 Megabyte of mbufs if the throughput went up. Spoke to Lori Thomas: found the systems are set like this: /> no -a | grep sb_max PAGE sb_max = 102400 15 /> no -a | grep space OF tcp_sendspace = 102400 86 tcp_recvspace = 102400 udp_sendspace = 65536 udp_recvspace = 588000 /> no -a | grep thewall thewall = 65536 /> bootinfo -r 524288 Having her change them to: /> no -a | grep sb_max OID sb_max = (was set WAY too low -basically, it was set RAL to the same amount as a transmit buffer- leaving nothing for recv) recommended setting: the LARGER of the three below: 1.) tcp_sendspace + tcp_recvspace PAGE 2.) udp_sendspace + udp_recvspace 16 3.) find the application which does the largest setsockopt(SO_RECVBUF) OF and setsockopt(SO_SNDBUF) - add these together. 86 some applications will allow you to set these up in it's config file. In any case, sb_max must be larger than the largest of the above 3 choic es. Now: the real toughie: thewall = this will be directly affected by the above sb_max setting, but basically take sb_max and multiply times the n umber of sockets of that size you expect to have open concurrently, add even more for basic O/S requirements, and routing sockets, and transient connections, etc... An imaginary example would be: and app that opens 20 connections to each OID client, and does 20 clients concurrently, and takes the system defaults RAL for tcp_send/recv spaces, where sb_max is 204800(bytes) could require at peak throughput on all connections: (20 x 20 x 204800) = 81,920,000 bytes for socket buffers. (about 78 megs PAGE ) 17 Set it to 1/2 of real system memory as indicated by "bootinfo -r" output OF (in her case bootinfo -r shows 512 megs, so thewall should be 256 initia 86 lly).

[ Main | Local ]

[ Allgemein | UNIX | AIX | TCP-IP | TCP | ROUTING | DNS | NTP | NFS | FreeBSD | Linux | RPi | SMTP | Tracing | GPS | LW ]

Copyright 2001-2021 by Orm Hager - Es gilt die GPL
Feedback bitte an: Orm Hager (orm@doc-tcpip.org )