Hermann Riemann <
nosp...@hermann-riemann.de>:
>Am 03.04.21 um 03:32 schrieb Helmut Waitzmann:
>> Hast Du dabei sichergestellt, dass das C‐Programm im zweiten Fall
>> nicht mehr Speicher angefordert hatte wie im ersten Fall?
>
>Das C- Programm ist von der Art:
>
>
>int64_t l=4096;
>void *p;
>while(1){
> p=malloc(l);
> if (p==NULL) break;
> memset(p,0,l);
> free(p);
> printf("%llX\n"l);
> l*=2;}
>
>Da ist der Unterschied ob swap abgeschaltet ist,
>deutlich sichtbar.
Das C‐Programm fordert also in geometrischer Folge, mit 4 KiB
beginnend, in jedem Schleifendurchlauf die doppelte Menge Speicher
an, so lang, bis das Betriebssystem den Wunsch nicht mehr erfüllen
kann.
=> Mit Swapspace tritt der Fall, dass das C‐Programm zu Ende kommt,
erst viel später ein, als ohne. Daher sind beide Fälle nicht
vergleichbar.
Der Versuch taugt also auf den ersten Blick allenfalls für die
Frage, ob mehr oder weniger Swapspace im Fall, dass ein
wildgewordenes Programm amoklaufend allen Speicher frisst, den es
kriegen kann, besser ist oder nicht, aber nicht für die Frage, ob
ein System ohne wildgewordene Programme besser ohne oder mit
Swapspace läuft.
Wenn ich Dich richtig verstanden habe, möchtest Du durch Verzicht
auf Swapspace um den Preis, dass der Betriebssystemkern den
Sensenmann (OoM‐Killer) losschickt und im System möglicherweise
Schaden anrichtet, Swappen verhindern.
Da hätte ich Dir einen besseren Vorschlag, der ohne den Sensenmann
auskommt: Verbiete dem Programm, sich allen Speicher zu krallen.
Dann kann man trotzdem noch, falls man will, Swapspace
bereitstellen.
Schau Dir dazu das Handbuch «prlimit(2)» für den Systemaufruf
«prlimit» an, mit dem man Prozesse im Ressourcen‐Verbrauch
beschränken kann. Dort findest Du u. a. folgende Abschnitte, hier
mit «|» am Zeilenanfang markiert, durchbrochen von Kommentaren:
| The resource argument must be one of:
|
| RLIMIT_AS
| This is the maximum size of the process's virtual
| memory (address space). The limit is specified in
| bytes, and is rounded down to the system page size.
| This limit affects calls to brk(2), mmap(2), and
| mremap(2), which fail with the error ENOMEM upon
| exceeding this limit. In addition, automatic stack
| expansion fails (and generates a SIGSEGV that kills
| the process if no alternate stack has been made
| available via sigaltstack(2)). Since the value is a
| long, on machines with a 32-bit long either this
| limit is at most 2 GiB, or this resource is
| unlimited.
Das hört sich doch schon mal sehr gut an, um einen Prozess, der mit
«malloc» allzu gierig umgeht, in Speicherschranken zu weisen. Aber
es gibt sogar noch spezifischeres:
|
| RLIMIT_DATA
| This is the maximum size of the process's data
| segment (initialized data, uninitialized data, and
| heap). The limit is specified in bytes, and is
| rounded down to the system page size. This limit
| affects calls to brk(2), sbrk(2), and (since Linux
| 4.7) mmap(2), which fail with the error ENOMEM upon
| encountering the soft limit of this resource.
Diese Schranke ist genau das, was man hier brauchen kann, um einen
Prozess, der mit «malloc» allzu gierig umgeht, in Schranken zu
weisen.
Und dann gibt es auch noch eine Begrenzung für den Stack:
| RLIMIT_STACK
| This is the maximum size of the process stack, in
| bytes. Upon reaching this limit, a SIGSEGV signal
| is generated. To handle this signal, a process must
| employ an alternate signal stack (sigaltstack(2)).
|
| Since Linux 2.6.23, this limit also determines the
| amount of space used for the process's command-line
| arguments and environment variables; for details,
| see execve(2).
Das wäre für amoklaufende Programme, die etwa eine Endlosrekursion
in Funktions‐ oder Prozeduraufrufe machen und deshalb den Stack
unbeschränkt wachsen lassen, einerseits interessant. Die Folge ist
dann, dass sie ein SEGV‐Signal erhalten. Andererseits kann man
davon ausgehen, dass die meisten Programme sich keinen alternativen
Signal‐Stack einrichten. Dann läuft der Stacküberlauf auf ein
KILL‐Signal hinaus, das es dem Prozess nicht erlaubt, noch
irgendwelche Aufräumarbeiten zu tun.
Für das Programm «prlimit» (das natürlich den Systemaufruf «prlimit»
benutzt) steht im Handbuch «prlimit(1)», wie es aufzurufen ist.
Eine weitere Hilfe, das System flüssig zu halten, ist, Programme,
die viel Speicher anfordern, mit einem schlechten (also großen)
«ionice»‐Wert oder «nice»‐Wert zu starten. «nice» ist eigentlich
immer eine gute Idee beim Start von nicht‐zeitkritischen Programmen,
die das System ziemlich beanspruchen könnten.
Dann kannst Du folgenden Aufruf des C‐Programms probieren:
# Starte das Programm mit in Bytes gemessener
# (beispielsweise auf 1 GiB) begrenzter Speichergroesze
# und gesenkter Prioritaet:
nice -n 19 -- prlimit --data="$(( 1 << 30 ))": -- \
das_C-Programm
Gib als Speichergrößengrenze die Menge von Speicher an, die das
C‐Programm beim Versuch ohne Swapspace erhalten konnte.
Nimm den Swapspace in Betrieb und wiederhole den Versuch. Hast Du
jetzt immer noch große Wartezeiten? Meine Vermutung: Man erhält
ein stabiles System, bei dem das amoklaufende C‐Programm scheitert,
ohne das ganze System auszubremsen und – vor allem – ohne vom
Sensenmann daran gehindert zu werden, vor dem Ende Aufräumarbeiten
zu tun.