Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

free vor dem exit erforderlich?

5 views
Skip to first unread message

Boris Nikolaev

unread,
Nov 20, 2003, 3:49:13 AM11/20/03
to
Hallo NG,

eine naive Frage: free auf allozierte Speicherbereiche macht man auch vor
dem Aussteigen aus dem Programm. Macht man es einfach anständigkeitshalber,
oder kann es auf irgendwelchen Platformen wirklich zu Problemen führen?

Gruß
Boris


Ralf Bartzke

unread,
Nov 20, 2003, 4:53:23 AM11/20/03
to
Boris Nikolaev schrieb:

Es kann durchaus. Zum Beispiel bei Betriebssystemem wie DOS, alte
Windows-Versionen ....

Ralf

Wolfgang Kaufmann

unread,
Nov 20, 2003, 5:29:16 AM11/20/03
to
* Thus spoke Boris Nikolaev <ier...@freenet.de>:

Hallo,

> eine naive Frage: free auf allozierte Speicherbereiche macht man auch vor
> dem Aussteigen aus dem Programm. Macht man es einfach anständigkeitshalber,
> oder kann es auf irgendwelchen Platformen wirklich zu Problemen führen?

Wenn Du Speicher anforderst, dann solltest Du ihn auch wieder freigeben;
nicht nur anstandshalber, sondern schon alleine deshalb, weil Du es so
gewohnt bist. ;-)

Du hast keine Garantie, dass auf jedwelcher Plattform der Speicher vom
Betriebssystem tatsächlich freigegeben wird.


Wolfgang.
--
"Es gibt Dinge, die man bereut, ehe man sie tut. Und man tut sie doch."
-- Christian Friedrich Hebbel

Jens Schweikhardt

unread,
Nov 20, 2003, 5:31:01 AM11/20/03
to
Ralf Bartzke <ralf.b...@t-online.de> wrote
in <bpi2uk$k6p$1...@piggy.rz.tu-ilmenau.de>:
# Boris Nikolaev schrieb:
#
#> Hallo NG,
#>
#> eine naive Frage: free auf allozierte Speicherbereiche macht man auch vor
#> dem Aussteigen aus dem Programm. Macht man es einfach anständigkeitshalber,
#> oder kann es auf irgendwelchen Platformen wirklich zu Problemen führen?
#>
#> Gruß
#> Boris
#>
# Es kann durchaus. Zum Beispiel bei Betriebssystemem wie DOS, alte
# Windows-Versionen ....

Das wird gern nachgeplapp^Werzählt, aber niemand kann es auf Nachfrage
nachweisen. Du etwa? (OS Version, Beispielprogramm). Es gab nach meiner
verschwommenen Erinnerung mal ein DOS, das ein Memory Leak im
Zusammenhang mit Umgebungsvariablen hatte. Mündliche Tradierung dieses
Bugs vermittels ahnungsloser Individuen führte dann zur Verwandlung in
den "DOS gibt nach exit() nicht allen Speicher frei" Pseudo-Bug.

Regards,

Jens
--
Jens Schweikhardt http://www.schweikhardt.net/
SIGSIG -- signature too long (core dumped)

Helmut Schellong

unread,
Nov 20, 2003, 6:32:20 AM11/20/03
to

Ich finde im Standard nichts, das sagt, Speicher werde automatisch
ge-freed bei exit.
Man sollte also pauschal selbst free-en.
Auch Signale sollte man idealerweise abfangen und dann in der
Catch-Funktion free-en.


--
Mit freundlichen Grüßen
Helmut Schellong v...@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm

Stefan Reuther

unread,
Nov 20, 2003, 6:50:23 AM11/20/03
to
Hallo,

Jens Schweikhardt <use...@schweikhardt.net> wrote:
> Ralf Bartzke <ralf.b...@t-online.de> wrote
> # Boris Nikolaev schrieb:


> #> eine naive Frage: free auf allozierte Speicherbereiche macht man auch vor
> #> dem Aussteigen aus dem Programm. Macht man es einfach anständigkeitshalber,
> #> oder kann es auf irgendwelchen Platformen wirklich zu Problemen führen?
> #

> # Es kann durchaus. Zum Beispiel bei Betriebssystemem wie DOS, alte
> # Windows-Versionen ....

> Das wird gern nachgeplapp^Werzählt, aber niemand kann es auf Nachfrage
> nachweisen. Du etwa? (OS Version, Beispielprogramm).

Es gibt unter DOS & Co. Systemaufrufe, die Speicher allokieren,
der beim Beenden des Programmes nicht freigegeben wird (z.B.
INT 31/AX=0501 mit dem 'PMode' DOS-Extender).

Allerdings ist das für ein C-Programm vollkommen irrelevant.
'malloc' wird normalerweise nicht 1:1 auf Systemaufrufe
abgebildet. Vielmehr besorgt sich 'malloc' mittels eines
Systemaufrufe einen Speicherblock und suballokiert darin.
Ebenso wird 'exit' üblicherweise nicht direkt auf System-
aufrufe abgebildet, sondern landet erstmal in der C-Runtime,
die zum einen 'atexit'-Routinen abarbeitet, und zum anderen
eben den von 'malloc' belegten Speicher wegräumen.

Daher kann man m.M.n. in 99% aller Fälle davon ausgehen, daß
gemallocter Speicher beim Programmende freigegeben wird. "Nur"
99% deswegen, da es unter DOS immer eine Methode gibt, das
Programm unter Umgehung der C-Runtime bzw. der DOS-Systemaufrufe
zu beenden. Der Durchschnittsprogrammierer dürfte mit solcherlei
Schweinereien aber nicht zu tun haben. Wer sich zutraut, sein
Programm durch rum-patchen in Systemdatenstrukturen zu beenden,
der wird auch wissen, wie sich das auf allokierten Speicher
auswirkt :-)

Es ist dennoch guter Stil, allen Speicher, den man irgendwo
allokiert, wieder freizugeben. Der Vorteil ist, daß das Programm
dadurch flexibler wird. Wenn dein Programm die Umgebung im selben
Zustand hinterläßt, wie es sie vorgefunden hat, kannst du z.B.
die 'main'-Funktion umbenennen in 'real_main' und aus einer
neuen 'main'-Funktion mit 35 verschiedenen Parametersets
aufrufen. Wenn jedes 'real_main' Speicherlecks hinterläßt,
wird das schwieriger.

Die C-Norm trifft darüber aber keine Aussage. 'exit' bzw. 'return
in main' beendet das Programm. Irgendeine umgebende Instanz kann
den Rückgabewert abfragen. Über die restliche Umgebung erfahren
wir nichts.


Stefan

Alexander Bartolich

unread,
Nov 20, 2003, 7:02:24 AM11/20/03
to
begin Jens Schweikhardt:
> [...] Es gab nach meiner verschwommenen Erinnerung mal ein DOS,

> das ein Memory Leak im Zusammenhang mit Umgebungsvariablen hatte.

Davon weiß ich nichts.

Allerdings gibt es zwei Design-Probleme in DOS.

a) File Handles (gibt es seit Version 2.0) werden nicht automatisch
bei Prozessende geschlossen.

b) Zum Start eines Programms wird ein Speicherblock angelegt, in den
die Programmdatei geladen wird. Dieser Block wird bei Prozessende
auch automatisch freigegeben. Fordert das Programm aber weitere
Blöcke an, so werden diese nicht automatisch freigegeben.

Beide Probleme lassen sich von der C-Runtime-Library abfangen,
in dem über diese Ressourcen Buch geführt wird, damit exit sie
wegräumen kann. IIRC wurde das von Borland und Microsoft auch so
gemacht.

--
Mögen die Schwingen des Koffeins in lichte Höhen tragen.

Helmut Schellong

unread,
Nov 20, 2003, 7:18:34 AM11/20/03
to
Stefan Reuther wrote:
[...]

> Die C-Norm trifft darüber aber keine Aussage. 'exit' bzw. 'return
> in main' beendet das Programm. Irgendeine umgebende Instanz kann
> den Rückgabewert abfragen. Über die restliche Umgebung erfahren
> wir nichts.

Richtig.
Der Standard beschreibt bei exit (1.main-return)
atexit-Funktionen, temporäre Dateien, Handle-Schließen
und Puffer-Flash.

Von Speicherfreigabe ist kein Wort vorhanden!
Auch nicht bei den Beschreibungen von free und malloc.

Horst Kraemer

unread,
Nov 20, 2003, 8:36:23 AM11/20/03
to
On 20 Nov 2003 13:02:24 +0100, Alexander Bartolich
<alexander...@gmx.at> wrote:

> begin Jens Schweikhardt:
> > [...] Es gab nach meiner verschwommenen Erinnerung mal ein DOS,
> > das ein Memory Leak im Zusammenhang mit Umgebungsvariablen hatte.
>
> Davon weiß ich nichts.
>
> Allerdings gibt es zwei Design-Probleme in DOS.
>
> a) File Handles (gibt es seit Version 2.0) werden nicht automatisch
> bei Prozessende geschlossen.

File Handles *werden* in DOS automatisch bei Prozessende durch die
Exit-Funktion von DOS geschlossen. Dies ist unproblematisch, denn die
vom Programm geoeffneten Handles sind in der Handle-Table des
Prozesses in dessen PSP (Program Segment Prefix) weithin sichtbar
explizt aufgefuehrt.


> b) Zum Start eines Programms wird ein Speicherblock angelegt, in den
> die Programmdatei geladen wird. Dieser Block wird bei Prozessende
> auch automatisch freigegeben. Fordert das Programm aber weitere
> Blöcke an, so werden diese nicht automatisch freigegeben.

Alle vom Programm per DOS-Funktion angeforderten Bloecke werden mit
dem Programm als "Owner" in die DOS-Allokationstabelle eingetragen und
automatisch mit dem Abgang des Owners durch die DOS-Exit-Funktion
abgeraeumt.

MfG
Horst

Alexander Bartolich

unread,
Nov 20, 2003, 9:39:46 AM11/20/03
to
begin Horst Kraemer:
> [...] File Handles *werden* [...] geschlossen.
> [...] Alle [...] angeforderten Bloecke werden [...] abgeraeumt.

Sagenhaft. Kann auf die schnelle nichts gegenteiliges finden.
Anscheinend hing ich da jahrzehntelang einem Irrglauben nach.
Erschütternd.

http://www.ctyme.com/intr/rb-2974.htm

Juergen Ilse

unread,
Nov 20, 2003, 10:24:41 AM11/20/03
to
Hallo,

Jens Schweikhardt <use...@schweikhardt.net> wrote:
> Ralf Bartzke <ralf.b...@t-online.de> wrote

> # Boris Nikolaev schrieb:


> #> eine naive Frage: free auf allozierte Speicherbereiche macht man auch vor
> #> dem Aussteigen aus dem Programm. Macht man es einfach anständigkeitshalber,
> #> oder kann es auf irgendwelchen Platformen wirklich zu Problemen führen?

> # Es kann durchaus. Zum Beispiel bei Betriebssystemem wie DOS, alte
> # Windows-Versionen ....
> Das wird gern nachgeplapp^Werzählt, aber niemand kann es auf Nachfrage
> nachweisen. Du etwa? (OS Version, Beispielprogramm). Es gab nach meiner
> verschwommenen Erinnerung mal ein DOS, das ein Memory Leak im
> Zusammenhang mit Umgebungsvariablen hatte. Mündliche Tradierung dieses
> Bugs vermittels ahnungsloser Individuen führte dann zur Verwandlung in
> den "DOS gibt nach exit() nicht allen Speicher frei" Pseudo-Bug.

Das mag zwar stimmen, aber der standard garantiert *nicht*, dass auch
aller reservierter Speicher bei der Terminierung des Programms frei
gegeben wird, also sollte man (auch wenn das auf den meisten heutigen
Systemen nicht zwingend notwendig sein sollte) das freigeben des selbst
allocierten Speichers *immer* selbst durchfuehren. References:
ISO C99: 7.20.4.3 The exit function

Tschuess,
Juergen Ilse (jue...@usenet-verwaltung.de)
--
Das Netz ist Freude. Es ist Ekstase, die jeden einzelnen Nerv erglühen
läßt. Es ist Duft, den man fühlt. Es ist ein Bild, das man riecht.
Es ist Erfüllung - ein Geschmack, neben dem alles andere schal ist.
("Netzreiter-Preisung" aus dem Buch "Der Netzparasit" von Andreas Brandhorst)

Ullrich von Bassewitz

unread,
Nov 20, 2003, 12:47:25 PM11/20/03
to
Helmut Schellong <r...@schellong.biz> wrote:
> Auch Signale sollte man idealerweise abfangen und dann in der
> Catch-Funktion free-en.

Genau das sollte man auf gar keinen Fall machen, da free nicht reentrant ist.
Ein portables Programm darf in einem Signalhandler nur abort() und _Exit()
aufrufen, sowie signal() mit dem ersten Parameter gleich der Signalnummer die
uebergeben wurde (ISO/IEC 9899:1999 (E), 7.14.1.1, Absatz 5). Alles andere
erzeugt undefined behaviour.

Gruss


Uz


P.S.: Es spricht natuerlich nichts dagegen, dass der Signalhandler ein Flag
setzt, und dass das Hauptprogramm nach Rueckkehr vom Signalhandler den
Speicher aufraeumt.

--
Ullrich von Bassewitz u...@spamtrap.musoftware.de
18:43:32 up 18 days, 2:08, 9 users, load average: 0.08, 0.05, 0.04

Helmut Schellong

unread,
Nov 20, 2003, 2:14:03 PM11/20/03
to
Ullrich von Bassewitz wrote:
> Helmut Schellong <r...@schellong.biz> wrote:
>
>>Auch Signale sollte man idealerweise abfangen und dann in der
>>Catch-Funktion free-en.
>
>
> Genau das sollte man auf gar keinen Fall machen, da free nicht reentrant ist.
> Ein portables Programm darf in einem Signalhandler nur abort() und _Exit()
> aufrufen, sowie signal() mit dem ersten Parameter gleich der Signalnummer die
> uebergeben wurde (ISO/IEC 9899:1999 (E), 7.14.1.1, Absatz 5). Alles andere
> erzeugt undefined behaviour.

> P.S.: Es spricht natuerlich nichts dagegen, dass der Signalhandler ein Flag


> setzt, und dass das Hauptprogramm nach Rueckkehr vom Signalhandler den
> Speicher aufraeumt.

Selbstverständlich meine ich das Ganze mit den Signalen vornehmlich
für Systeme, bei denen Signale vorzüglich funktionieren.
Das ist an erster Stelle Unix.
Es ist dort ausdrücklich erlaubt, in Signal-Catchern Funktionen
aufzurufen, die selbst Interrupts auslösen.

Desweiteren meinte ich das nur, wenn man tatsächlich danach
exit() macht.

Unter DOS z.B. funktionieren Signale an sich kaum bis gar nicht;
wenn, dann nur ^C.

Wenn man in free() ist und dann ein Signal kommt ... was soll schon
passieren?
Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
darin einfach ein static-char als Flag...
(Mach ich bei MCs dauernd.)

Philipp Janda

unread,
Nov 20, 2003, 6:38:41 PM11/20/03
to
Helmut Schellong schrieb:

>
> Selbstverständlich meine ich das Ganze mit den Signalen vornehmlich
> für Systeme, bei denen Signale vorzüglich funktionieren.
> Das ist an erster Stelle Unix.

Ich muss ganz ehrlich sagen, dass mir das Signal-Konzept grundsaetzlich
nicht gefaellt, weil es auf keinem mir bekannten OS vorzueglich
funktioniert.


> Es ist dort ausdrücklich erlaubt, in Signal-Catchern Funktionen
> aufzurufen, die selbst Interrupts auslösen.

Die Interrupts sind auch nicht das Problem - eher das was drumherum steht...

>
> Desweiteren meinte ich das nur, wenn man tatsächlich danach
> exit() macht.

Ich glaube, exit() ist auch nicht reentrant, weil es eine globale
atexit-handler Liste hat. Theoretisch koennte also Dein Computer
zerplatzen, wenn Du im Hauptprogramm gerade atexit() oder exit() (oder
eventuell sogar main-return?!) aufgerufen hast und dann im
Signal-Handler nochmal exit() aufrufst.

>
> Unter DOS z.B. funktionieren Signale an sich kaum bis gar nicht;
> wenn, dann nur ^C.
>
> Wenn man in free() ist und dann ein Signal kommt ... was soll schon
> passieren?

Nichts, wenn Du im Signal-Handler keine Funktion aufrufst, die dieselben
globalen Variablen manipuliert wie das unterbrochene free(), d.h. kein
malloc, realloc, calloc, free, und alle Funktionen, die diese eventuell
aufrufen (fopen?, fclose?, ...).
Andernfalls crasht das Programm hoffentlich, ohne allzuviel Schaden
anzurichten, und mit einer informativen Fehlermeldung.

> Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
> darin einfach ein static-char als Flag...
> (Mach ich bei MCs dauernd.)

Das geht auch nur, wenn char's atomar incrementiert, decrementiert und
ueberprueft werden koennen (volatile??).
Vermutlich wirds aber auch andernfalls bloss alle hundert Jahre mal
krachen...


Gruesse,
Philipp

Ullrich von Bassewitz

unread,
Nov 20, 2003, 7:17:08 PM11/20/03
to
Helmut Schellong <r...@schellong.biz> wrote:
> Selbstverständlich meine ich das Ganze mit den Signalen vornehmlich
> für Systeme, bei denen Signale vorzüglich funktionieren.
> Das ist an erster Stelle Unix.
> Es ist dort ausdrücklich erlaubt, in Signal-Catchern Funktionen
> aufzurufen, die selbst Interrupts auslösen.

Erstens interessiert Unix in dieser Gruppe nicht, sondern nur der C Standard.
Und der sagt ganz eindeutig etwas anderes als Du.

Zweitens, selbst wenn es interessieren wuerde: Auch unter gaengigen Unix
Systemen ist ein Aufruf free() nicht von einem Signalhandler aus erlaubt. Es
dreht sich naemlich nicht darum, ob die Funktion selber einen Interrupt
ausloest, sondern darum, dass sie unterbrochen worden sein kann, waehrend sie
gerade dabei war, globale Datenstrukturen zu aendern.

> Wenn man in free() ist und dann ein Signal kommt ... was soll schon
> passieren?

Der Standard sagt eindeutig, dass Du damit undefined behaviour produzierst.
Das alleine ist in dieser Gruppe schon genug.

Unabhaengig davon ist jede Funktion gefaehrdet, die globale Datenstrukturen
veraendert. Das ist im Fall der gaengigsten Implementationen von free()
ueblicherweise die freelist, die freigegebene Bloecke in irgendeiner Form
speichert, wenn sie nicht direkt am Ende des Heaps liegen. Und wenn free()
genau hier unterbrochen wird, dann ist die freelist in einem inkonsistenten
Zustand. Jeder dann folgende Aufruf von free() aus einem Signalhandler heraus,
benutzt eine inkonsistente freelist. Wenn Du ein bischen Ahnung von C hast,
dann kannst Du Dir vielleicht vorstellen, was das heisst.

> Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
> darin einfach ein static-char als Flag...
> (Mach ich bei MCs dauernd.)

Ein static char Flag ohne eine atomare "test and set" Operation reicht nicht
aus, um Dich gegen asynchrone Events abzusichern.

static flag;
if (flag == 0) {
/* <--- #1 */
/* Lock the critical section */
flag = 1;
/* Do some work */
...
/* Release the critical section */
flag = 0;
} else {
/* <--- #2 */
/* ???? */
}

Erstens kann die Funktion zwischen dem Test und dem Setzen ubterbrochen werden
(markiert mit #1). Dagegen gibt es keine Abhilfe ausser der genannten "test
and set" Operation, die aber in reinem C nur mit grossem Aufwand abbildbar ist
(Blocken aller Signale). Zweitens (markiert mit #2) ist ein simples Flag
non-blocking. Was willst Du im Fall von free() tun, wenn so ein Flag gesetzt
ist? Schliesslich ist es die Aufgabe von free(), den uebergebenen
Speicherblock freizugeben. Und das funktioniert nicht, wenn die freelist mit
so einem Flag verriegelt ist. Das Resultat waeren verlorene Speicherbereiche.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

00:51:03 up 18 days, 8:15, 9 users, load average: 0.17, 0.10, 0.07

Ullrich von Bassewitz

unread,
Nov 20, 2003, 7:22:32 PM11/20/03
to
Ullrich von Bassewitz <u...@spamtrap.musoftware.de> wrote:
> Zweitens, selbst wenn es interessieren wuerde: Auch unter gaengigen Unix
> Systemen ist ein Aufruf free() nicht von einem Signalhandler aus erlaubt.

Siehe

http://www.opengroup.org/onlinepubs/007904975/functions/xsh_chap02_04.html

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

01:21:05 up 18 days, 8:45, 9 users, load average: 0.14, 0.17, 0.09

Helmut Schellong

unread,
Nov 21, 2003, 1:54:36 AM11/21/03
to
Ullrich von Bassewitz wrote:
> Ullrich von Bassewitz <u...@spamtrap.musoftware.de> wrote:
>
>>Zweitens, selbst wenn es interessieren wuerde: Auch unter gaengigen Unix
>>Systemen ist ein Aufruf free() nicht von einem Signalhandler aus erlaubt.
>
>
> Siehe
>
> http://www.opengroup.org/onlinepubs/007904975/functions/xsh_chap02_04.html

Um es kurz zu machen:
In solchen Funktionen müssen dann eben Vorkehrungen getroffen werden,
die Ungemach verhindern.
Es wäre dumm von einem Lib-Entwickler, auf solche simplen Möglichkeiten
zu verzichten.

static char volatile Flag;
if (Flag) return 0;
Flag=1;
...
Flag=0;
return x;

In exit():
static char volatile Flag;
SigIgnore();
if (Flag) _exit();
Flag=1;
...
BackToSys();
return;
denn exit() ist so definiert, daß sie
niemals retourniert.
Ein Aufruf von _exit wäre hier das definierte undefinierte Verhalten.

Die Funktionen müssen doch selbst sicherstellen, daß sie ihre
bestimmungsgemäße Arbeit verrichten können.

Davon abgesehen verwende ich in eigenem Code eigene Flags, die einen
zweiten Aufruf von exit() etc. verhindern.
Desweiteren räume ich selbst auf, bevor ich exit aufrufe.
Das u.a. ist ohnehin empfehlenswert, wenn man mit Signalen rummacht.
Auch atexit verwende ich nie. Ich nehme eine eigene End();
In der Regel verwende ich longjmp() in Signal-Catch().

Wenn ich hier geschrieben habe, daß man 'in' Signal-Catch() free()
aufrufen soll, so war das mißverständlich.
So simpel meinte ich das nicht.

Claus Reibenstein

unread,
Nov 21, 2003, 2:43:14 AM11/21/03
to
Philipp Janda schrieb:

> Helmut Schellong schrieb:


>
>>Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
>>darin einfach ein static-char als Flag...

Auch das kann fürchterlich schiefgehen, wenn man nicht mit
systemspezifischen Mitteln dafür sorgt, dass zwischen Prüfen und Setzen
des Flags kein Interrupt ausgelöst wird.

>>(Mach ich bei MCs dauernd.)

Gut zu wissen. Dann weiß ich ja, worauf ich in Zukunft beim Kauf zu
achten habe.

> Das geht auch nur, wenn char's atomar incrementiert, decrementiert und
> ueberprueft werden koennen (volatile??).

Volatile nützt da nicht viel. Volatile ist nur dann interessant, wenn
die Variable zwischen zwei Zugriffen auch von anderer Stelle aus
verändert werden kann. Hier geht es aber eher darum, genau dies zu
verhindern.

> Vermutlich wirds aber auch andernfalls bloss alle hundert Jahre mal
> krachen...

Wenn von 1.000.000 MCs jeder 1x in 100 Jahren crasht, dann sind das
10.000 Crashes pro Jahr. Das macht dann pro Tag rund 27 Crashes.

Gruß. Claus

Helmut Schellong

unread,
Nov 21, 2003, 3:41:23 AM11/21/03
to
Claus Reibenstein wrote:
> Philipp Janda schrieb:
>
>
>>Helmut Schellong schrieb:
>>
>>
>>>Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
>>>darin einfach ein static-char als Flag...
>
>
> Auch das kann fürchterlich schiefgehen, wenn man nicht mit
> systemspezifischen Mitteln dafür sorgt, dass zwischen Prüfen und Setzen
> des Flags kein Interrupt ausgelöst wird.

Nein, setzen kann das Flag nur die betreffende Funktion:
void InitCAN1(void)
{
static char volatile f;
if (f) return;
f=1;
...
f=0;
return;
}
Wenn die Funktion gerade läuft, wird ein Init-Doppel aus einem
Interrupt vermieden.

>>>(Mach ich bei MCs dauernd.)
>
>
> Gut zu wissen. Dann weiß ich ja, worauf ich in Zukunft beim Kauf zu
> achten habe.

Was soll das heißen?
Obenstehende Maßnahme ist sicher, wenn die Funktion aus (einem)
Interrupt(s) aufgerufen wird, wobei *dabei* keine neuen Parameterwerte
zu berücksichtigen sind, und an mehreren Stellen aufgerufen wird
außerhalb von Interrupts, wobei hier der Aufruf meist erfolgt, weil
neue Parameterwerte einfließen sollen.

Wenn im Interrupt auch neue Parameterwerte gesetzt würden, müßte man
einen Request-Merker setzen.


>>Vermutlich wirds aber auch andernfalls bloss alle hundert Jahre mal
>>krachen...
>
>
> Wenn von 1.000.000 MCs jeder 1x in 100 Jahren crasht, dann sind das
> 10.000 Crashes pro Jahr. Das macht dann pro Tag rund 27 Crashes.

Das ist relativ.
Es gibt wohl ohnehin schon 10000 Crashes pro Tag,
wobei wohl 9950 davon schlicht folgenlos/harmlos sind
und nur 2 davon einen (schmerzlichen) Dateiverlust bewirken,
weltweit.
Damit kann man leben.
An einem Tag sterben vielleicht 5000 Menschen gewaltsam.
Damit *muß* man leben.

Helmut Schellong

unread,
Nov 21, 2003, 4:06:47 AM11/21/03
to
Ullrich von Bassewitz wrote:
> Helmut Schellong <r...@schellong.biz> wrote:
[...]

>>Wenn ich Funktionen habe, die nicht reentrant sind, verwende ich
>>darin einfach ein static-char als Flag...
>>(Mach ich bei MCs dauernd.)
>
>
> Ein static char Flag ohne eine atomare "test and set" Operation reicht nicht
> aus, um Dich gegen asynchrone Events abzusichern.
>
> static flag;
> if (flag == 0) {
> /* <--- #1 */
> /* Lock the critical section */
> flag = 1;
> /* Do some work */
> ...
> /* Release the critical section */
> flag = 0;
> } else {
> /* <--- #2 */
> /* ???? */
> }
[...]

Siehe meine Antwort mit 'InitCAN1()'.

Philipp Janda

unread,
Nov 21, 2003, 5:19:45 AM11/21/03
to
Helmut Schellong schrieb:

> Claus Reibenstein wrote:
>
> Nein, setzen kann das Flag nur die betreffende Funktion:
> void InitCAN1(void)
> {
> static char volatile f;
> if (f) return;
> f=1;
> ...
> f=0;
> return;
> }
> Wenn die Funktion gerade läuft, wird ein Init-Doppel aus einem
> Interrupt vermieden.

Nein, wirds nicht, da das Signal direkt nach der if-Abfrage zugestellt
werden koennte. Dann wird die Funktion einmal komplett im Signal-Handler
durchgefuehrt und beim Ruecksprung ins Hauptprogramm auch noch einmal
komplett.

Was vielleicht besser funktionieren koennte ist:

...
if( ++f > 1 ) {
--f;
return;
}
...

Aber warum willst Du diese Funktion denn ueberhaupt im Signal-Handler
aufrufen? Doch wohl, weil sie fuer den nachfolgenden Code im
Signal-Handler wichtig ist (Initialisierung von irgendeinem
Microcontroller?!). Da aber die Funktion im Hauptprogramm beliebig
zwischen f=1; und f=0; unterbrochen werden kann, koennte es sein, dass
dein Signal-Handler mit einem nicht oder nur teilweise initialisiertem
Microcontroller arbeitet! Peng!

>
>
> Was soll das heißen?
> Obenstehende Maßnahme ist sicher, wenn die Funktion aus (einem)
> Interrupt(s) aufgerufen wird, wobei *dabei* keine neuen Parameterwerte
> zu berücksichtigen sind, und an mehreren Stellen aufgerufen wird
> außerhalb von Interrupts, wobei hier der Aufruf meist erfolgt, weil
> neue Parameterwerte einfließen sollen.
> Wenn im Interrupt auch neue Parameterwerte gesetzt würden, müßte man
> einen Request-Merker setzen.

Haeh???

>
>>
>> Wenn von 1.000.000 MCs jeder 1x in 100 Jahren crasht, dann sind das
>> 10.000 Crashes pro Jahr. Das macht dann pro Tag rund 27 Crashes.
>
>
> Das ist relativ.
> Es gibt wohl ohnehin schon 10000 Crashes pro Tag,
> wobei wohl 9950 davon schlicht folgenlos/harmlos sind
> und nur 2 davon einen (schmerzlichen) Dateiverlust bewirken,
> weltweit.
> Damit kann man leben.

Jetzt weiss ich auch, *warum* wir ohnehin schon 10000 Crashes pro Tag
haben...

> An einem Tag sterben vielleicht 5000 Menschen gewaltsam.
> Damit *muß* man leben.

Und es gibt doch tatsaechlich so ein paar idealistische Spinner, die
meinen sie koennten daran etwas aendern ;-)

>
>

Gruesse,
Philipp

Philipp Janda

unread,
Nov 21, 2003, 5:34:13 AM11/21/03
to
Claus Reibenstein schrieb:

> Philipp Janda schrieb:


>
>>Das geht auch nur, wenn char's atomar incrementiert, decrementiert und
>>ueberprueft werden koennen (volatile??).
>
>
> Volatile nützt da nicht viel. Volatile ist nur dann interessant, wenn
> die Variable zwischen zwei Zugriffen auch von anderer Stelle aus
> verändert werden kann. Hier geht es aber eher darum, genau dies zu
> verhindern.

Ich habe da an folgendes gedacht:

void myfree( void* ptr ) {
static volatile int flag = 0;
++flag;
if( flag == 1 ) {
// begin critical section
free( ptr );
// end critical section
}
--flag;
}

Sobald flag auf (mindestens) eins ist duerfte kein Signal-Handler den
kritischen Code nochmal ausfuehren. Und da die Reihenfolge der
Funktionsaufrufe relativ festgelegt ist (bevor es mit dem Aufruf im
Hauptprogramm weitergeht, haben alle Signal-Handler das flag wieder auf
den urspruenglichen Wert 1 zurueckgesetzt), muesste der Kritische
Abschnitt genau einmal ausgefuehrt werden.

Das aendert natuerlich nichts daran, dass die Funktion im Signal-Handler
dann vermutlich nicht mehr das Gewuenschte tut, aber es schuetzt die
globalen Datenstrukturen.

>
>
>>Vermutlich wirds aber auch andernfalls bloss alle hundert Jahre mal
>>krachen...
>
>
> Wenn von 1.000.000 MCs jeder 1x in 100 Jahren crasht, dann sind das
> 10.000 Crashes pro Jahr. Das macht dann pro Tag rund 27 Crashes.

Ich wollte das keinesfalls verharmlosen!

>
> Gruß. Claus
>

Gruesse,
Philipp

Helmut Schellong

unread,
Nov 21, 2003, 6:28:52 AM11/21/03
to
Philipp Janda wrote:
> Helmut Schellong schrieb:
>
>> Claus Reibenstein wrote:
>>
>> Nein, setzen kann das Flag nur die betreffende Funktion:
>> void InitCAN1(void)
>> {
>> static char volatile f;
>> if (f) return;
>> f=1;
>> ...
>> f=0;
>> return;
>> }
>> Wenn die Funktion gerade läuft, wird ein Init-Doppel aus einem
>> Interrupt vermieden.
>
>
> Nein, wirds nicht, da das Signal direkt nach der if-Abfrage zugestellt
> werden koennte. Dann wird die Funktion einmal komplett im Signal-Handler
> durchgefuehrt und beim Ruecksprung ins Hauptprogramm auch noch einmal
> komplett.

Ja natürlich.
Aber das ist nicht schlimm!
Hauptsache, zwischen Flag=1 und Flag=0 kommt nichts dazwischen!

Zweimal Init jeweils komplett von a-z ist egal.
Nur wenn der eine Init bei k ist und der Interrupt will von
a neu beginnen - das gibt Probleme.

> Was vielleicht besser funktionieren koennte ist:
>
> ...
> if( ++f > 1 ) {
> --f;
> return;
> }
> ...
>
> Aber warum willst Du diese Funktion denn ueberhaupt im Signal-Handler
> aufrufen? Doch wohl, weil sie fuer den nachfolgenden Code im
> Signal-Handler wichtig ist (Initialisierung von irgendeinem
> Microcontroller?!).

Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen
der CAN-NODE-State geprüft.
Manchmal ist da ein Re-Init nötig.
Ich rede diesbezüglich von Anfang an von Mikrokontrollern.

> Da aber die Funktion im Hauptprogramm beliebig
> zwischen f=1; und f=0; unterbrochen werden kann, koennte es sein, dass
> dein Signal-Handler mit einem nicht oder nur teilweise initialisiertem
> Microcontroller arbeitet! Peng!

Ich rede hier von Industrieprodukten, die seit Jahren
teilweise jahrelang ohne Unterbrechung einwandfrei laufen.
Also nix 'Peng'.

>> Was soll das heißen?
>> Obenstehende Maßnahme ist sicher, wenn die Funktion aus (einem)
>> Interrupt(s) aufgerufen wird, wobei *dabei* keine neuen Parameterwerte
>> zu berücksichtigen sind, und an mehreren Stellen aufgerufen wird
>> außerhalb von Interrupts, wobei hier der Aufruf meist erfolgt, weil
>> neue Parameterwerte einfließen sollen.
>> Wenn im Interrupt auch neue Parameterwerte gesetzt würden, müßte man
>> einen Request-Merker setzen.
>
>
> Haeh???

Wenn neue Parameterwerte einfließen sollen, darf die InitCAN1()
*EBEN NICHT* an der Stelle if (Flag) return; VERLASSEN werden!
Zum Glück ist das bei dem Aufruf im Interrupt nicht der Fall.

Helmut Schellong

unread,
Nov 21, 2003, 6:48:23 AM11/21/03
to
Ullrich von Bassewitz wrote:
> Ullrich von Bassewitz <u...@spamtrap.musoftware.de> wrote:
>
>>Zweitens, selbst wenn es interessieren wuerde: Auch unter gaengigen Unix
>>Systemen ist ein Aufruf free() nicht von einem Signalhandler aus erlaubt.

Noch was:
Ich rufe free() nur so auf:
if (p=Ptr[2], p) Ptr[2]=0, free(p);

Desweiteren deaktiviere ich Signale, wenn ich in Code-Bereiche
eintrete, die diesbezüglich kritisch sind.
Das auslösende Signal setze ich eh auf SIG_IGN, während 'sein'
Handler läuft. Vor return gibt's dann wieder signal(SIGXXX, Catch);

Man kriegt das schon sicher hin.

Ullrich von Bassewitz

unread,
Nov 21, 2003, 7:20:52 AM11/21/03
to
Helmut Schellong <r...@schellong.biz> wrote:
> Um es kurz zu machen:
> In solchen Funktionen müssen dann eben Vorkehrungen getroffen werden,
> die Ungemach verhindern.

Du bist derjenige, der allgemein angeraten hat, aus einem Signalhandler heraus
free() aufzurufen. Das ist und bleibt falsch, sowohl was den C Standard
angeht, als auch was die von Dir als Entschuldigung herangezogenen Unix
Plattformen angeht.

> Noch was:
> Ich rufe free() nur so auf:
> if (p=Ptr[2], p) Ptr[2]=0, free(p);

Obiger Code ist sinnlos kompliziert, sowohl was die Zuweisung an die
Hilfsvariable, als auch was die unnoetige Verwendung des Komma Operators
angeht. Und er verbessert bezueglich eines Aufrufs von free() aus einem
Signalhandler heraus Deine Situation in keinster Weise. Auch mit diesem Code
ist das nicht zulaessig.

> Desweiteren deaktiviere ich Signale, wenn ich in Code-Bereiche
> eintrete, die diesbezüglich kritisch sind.

Das spielt beim Aufrufen von free() aus einem Signalhandler heraus keine
Rolle, weil gaengige Implementationen von free() das naemlich nicht tun - und
auch nicht tun muessen.

> Man kriegt das schon sicher hin.

Ich weiss nicht, wer "man" sein soll. Wenn Du Dich da nicht mit einbeziehst,
dann koennte ich Dir vielleicht recht geben:-)

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

13:10:10 up 18 days, 20:34, 9 users, load average: 0.06, 0.08, 0.03

Philipp Janda

unread,
Nov 21, 2003, 7:45:05 AM11/21/03
to
Helmut Schellong schrieb:

>>
>> Aber warum willst Du diese Funktion denn ueberhaupt im
>> Signal-Handler aufrufen? Doch wohl, weil sie fuer den nachfolgenden
>> Code im Signal-Handler wichtig ist (Initialisierung von irgendeinem
>> Microcontroller?!).
>
>
> Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen der
> CAN-NODE-State geprüft. Manchmal ist da ein Re-Init nötig.

Und genau dieser benoetigte Re-Init findet manchmal nicht vollstaendig
statt.

> Ich rede diesbezüglich von Anfang an von Mikrokontrollern.

Das gilt auch fuer Mikrokontroller.

>
>> Da aber die Funktion im Hauptprogramm beliebig zwischen f=1; und
>> f=0; unterbrochen werden kann, koennte es sein, dass dein
>> Signal-Handler mit einem nicht oder nur teilweise initialisiertem
>> Microcontroller arbeitet! Peng!
>
>
> Ich rede hier von Industrieprodukten, die seit Jahren teilweise
> jahrelang ohne Unterbrechung einwandfrei laufen.

Da kann man nur sagen: Schwein gehabt!
Ich glaube es ist in diesem Fall von Vorteil gewesen, die eine Sekunde
Verzoegerung einzubauen...

> Also nix 'Peng'.
>
>>> Was soll das heißen? Obenstehende Maßnahme ist sicher, wenn die
>>> Funktion aus (einem) Interrupt(s) aufgerufen wird, wobei *dabei*
>>> keine neuen Parameterwerte zu berücksichtigen sind, und an
>>> mehreren Stellen aufgerufen wird außerhalb von Interrupts, wobei
>>> hier der Aufruf meist erfolgt, weil neue Parameterwerte
>>> einfließen sollen. Wenn im Interrupt auch neue Parameterwerte
>>> gesetzt würden, müßte man einen Request-Merker setzen.
>>
>>
>>
>> Haeh???
>
>
> Wenn neue Parameterwerte einfließen sollen, darf die InitCAN1() *EBEN
> NICHT* an der Stelle if (Flag) return; VERLASSEN werden! Zum Glück
> ist das bei dem Aufruf im Interrupt nicht der Fall.

Du meinst, dass sich irgendwann ein paar globale Variablen aendern und
dann die InitCAN1() einmal vollstaendig aufgerufen werden muss, bevor
man gescheit weiterarbeiten kann?!

Aber genau das kann in die Hose gehen, da eine teilweise ausgefuehrte
InitCAN1() im Hauptprogramm eine vollstaendig ausgefuehrte InitCAN1() im
Signal-Handler verhindern kann. Und falls der Code im Signal-Handler von
einer korrekten Initialisierung ausgeht, dann: Peng!!


>
>

Gruesse,
Philipp


Helmut Schellong

unread,
Nov 21, 2003, 10:24:57 AM11/21/03
to
Ullrich von Bassewitz wrote:
> Helmut Schellong <r...@schellong.biz> wrote:
>
>>Um es kurz zu machen:
>>In solchen Funktionen müssen dann eben Vorkehrungen getroffen werden,
>>die Ungemach verhindern.
>
>
> Du bist derjenige, der allgemein angeraten hat, aus einem Signalhandler heraus
> free() aufzurufen. Das ist und bleibt falsch, sowohl was den C Standard
> angeht, als auch was die von Dir als Entschuldigung herangezogenen Unix
> Plattformen angeht.
>
>
>>Noch was:
>>Ich rufe free() nur so auf:
>> if (p=Ptr[2], p) Ptr[2]=0, free(p);
>
>
> Obiger Code ist sinnlos kompliziert, sowohl was die Zuweisung an die
> Hilfsvariable, als auch was die unnoetige Verwendung des Komma Operators
> angeht. Und er verbessert bezueglich eines Aufrufs von free() aus einem
> Signalhandler heraus Deine Situation in keinster Weise. Auch mit diesem Code
> ist das nicht zulaessig.

> Das spielt beim Aufrufen von free() aus einem Signalhandler heraus keine


> Rolle, weil gaengige Implementationen von free() das naemlich nicht tun - und
> auch nicht tun muessen.

Ich bestreite das einfach mal.
Nirgendwo steht geschrieben, daß free() Probleme machen kann,
nur *allein bewirkt* durch einen Aufruf in einem Signal-Handler.
Ein Signal-Handler ist eine stinknormale Funktion, wie jede andere.
Er wird lediglich asynchron von außen aufgerufen.
Im Assembler sieht man 'ret' und nicht etwa 'iret' - selbst wenn...
Das Problem besteht einfach indirekt darin, daß im Zusammenhang
mit Signal-Handlern die Möglichkeit besteht, daß free() aufgerufen wird
zu einem Zeitpunkt, wo der Programmablauf bereits innerhalb von free()
ist.
Genau das ist ein Problem, falls free() tatsächlich nicht reentrant ist.
Wenn man nun sicher verhindert, daß free() solchermaßen doppelt aufgerufen
wird, hat man das Problem von außen beseitigt.
Ich kann das und machte das schon oft.

Mein Code oben ist eine sinnvolle Pauschalmaßnahme.
Ich war schon oft dankbar über solche meine Maßnahmen.
Der Code verhindert sicher, daß free() mit einem Pointer unerwünscht
nochmals -also doppelt- aufgerufen wird.
Oder etwa nicht?!

Ullrich von Bassewitz

unread,
Nov 21, 2003, 10:36:24 AM11/21/03
to
Philipp Janda <siff...@gmx.net> wrote:
> Ich habe da an folgendes gedacht:
>
> void myfree( void* ptr ) {
> static volatile int flag = 0;
> ++flag;
> if( flag == 1 ) {
> // begin critical section
> free( ptr );
> // end critical section
> }
> --flag;
> }

Das funktioniert auch nicht, weil der ++ Operator nicht atomar sein muss -
auch dann nicht, wenn die Variable als volatile deklariert wurde.

> Das aendert natuerlich nichts daran, dass die Funktion im Signal-Handler
> dann vermutlich nicht mehr das Gewuenschte tut, aber es schuetzt die
> globalen Datenstrukturen.

Nein, leider nicht.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

16:32:35 up 18 days, 23:56, 10 users, load average: 0.00, 0.05, 0.02

Helmut Schellong

unread,
Nov 21, 2003, 10:57:34 AM11/21/03
to
Philipp Janda wrote:
> Helmut Schellong schrieb:
>
>>>
>>> Aber warum willst Du diese Funktion denn ueberhaupt im
>>> Signal-Handler aufrufen? Doch wohl, weil sie fuer den nachfolgenden
>>> Code im Signal-Handler wichtig ist (Initialisierung von irgendeinem
>>> Microcontroller?!).
>>
>>
>>
>> Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen der
>> CAN-NODE-State geprüft. Manchmal ist da ein Re-Init nötig.
>
>
> Und genau dieser benoetigte Re-Init findet manchmal nicht vollstaendig
> statt.

Doch.
Wenn Flag=1 ist, beim Aufruf aus dem IRPT, bedeutet das, daß gerade
ein Re-Init vollständig durchgeführt wird.
Der Aufruf aus dem IRPT ist also hier redundant.

Die Sendungen auf den CAN-Bus sind beispielsweise seit einer halben
Sekunde beendet. Dabei war eventuell ein NODE-error aufgetreten.
Die InitCAN1() braucht vielleicht 500us zum Durchlauf.
Wenn also der IRPT auf eine bereits betretene InitCAN1() trifft,
ist dieses Betreten garantiert lange nach der Feststellung des
NODE-errors.

Und wenn die Angelegenheit zeitlich eng wäre, würde eben der NODE-error
nochmals auftreten und dann im IRPT (2.Versuch) mit Flag=0 beseitigt.
Der NODE-error selbst hat eh ein TIMEOUT von 3 Sekunden.

>>> Da aber die Funktion im Hauptprogramm beliebig zwischen f=1; und
>>> f=0; unterbrochen werden kann, koennte es sein, dass dein
>>> Signal-Handler mit einem nicht oder nur teilweise initialisiertem
>>> Microcontroller arbeitet! Peng!
>>
>>
>>
>> Ich rede hier von Industrieprodukten, die seit Jahren teilweise
>> jahrelang ohne Unterbrechung einwandfrei laufen.
>
>
> Da kann man nur sagen: Schwein gehabt!
> Ich glaube es ist in diesem Fall von Vorteil gewesen, die eine Sekunde
> Verzoegerung einzubauen...

Das ist keine Verzögerung, sondern eine Art Maschine mit Zykluszeit
von 1 s.
Diese Maschine synchronisiert das ganze Geschehen.

>>>> Was soll das heißen? Obenstehende Maßnahme ist sicher, wenn die
>>>> Funktion aus (einem) Interrupt(s) aufgerufen wird, wobei *dabei*
>>>> keine neuen Parameterwerte zu berücksichtigen sind, und an
>>>> mehreren Stellen aufgerufen wird außerhalb von Interrupts, wobei
>>>> hier der Aufruf meist erfolgt, weil neue Parameterwerte
>>>> einfließen sollen. Wenn im Interrupt auch neue Parameterwerte
>>>> gesetzt würden, müßte man einen Request-Merker setzen.
>>>
>>>
>>>
>>>
>>> Haeh???
>>
>>
>>
>> Wenn neue Parameterwerte einfließen sollen, darf die InitCAN1() *EBEN
>> NICHT* an der Stelle if (Flag) return; VERLASSEN werden! Zum Glück
>> ist das bei dem Aufruf im Interrupt nicht der Fall.
>
>
> Du meinst, dass sich irgendwann ein paar globale Variablen aendern und
> dann die InitCAN1() einmal vollstaendig aufgerufen werden muss, bevor
> man gescheit weiterarbeiten kann?!

Wenn die Einheit neu parametriert wird und dadurch z.B. ein weiterer
CAN-Puffer eingerichtet werden muß, muß InitCAN1() danach unbedingt
durchlaufen werden.
Du kannst Dir denken, daß ein Aufruf von InitCAN1() wegen eines
NODE-errors KEINE neue Parametrierung des CAN-Controllers ist.
Nach NODE-error muß InitCAN1() *unabhängig* von Parameterwerten einmal
aufgerufen werden, damit ein CAN-Controller-RESET durchgeführt wird.

Ullrich von Bassewitz

unread,
Nov 21, 2003, 11:07:17 AM11/21/03
to
Helmut Schellong <r...@schellong.biz> wrote:
>> Das spielt beim Aufrufen von free() aus einem Signalhandler heraus keine
>> Rolle, weil gaengige Implementationen von free() das naemlich nicht tun - und
>> auch nicht tun muessen.
>
> Ich bestreite das einfach mal.

Es waere schoen, wenn Du das nicht nur bestreiten, sondern Belege aus dem C
Standard fuer Deine Meinung bringen wuerdest. Ich habe die Stelle genannt, wo
steht, dass ein Aufruf von free() aus einem Signalhandler heraus *nicht*
zulaessig ist. Ich habe Dir auch eine Liste von Funktionen genannt, die unter
gaengigen Unix-System explizit von einem Signalhandler aus aufgerufen werden
duerfen - und free() steht *nicht* in dieser Liste.

Insofern kannst Du eine Menge bestreiten, wenn Du das aber nicht belegst, ist
es nur Geschwafel.

> Nirgendwo steht geschrieben, daß free() Probleme machen kann,
> nur *allein bewirkt* durch einen Aufruf in einem Signal-Handler.

In ISO/IEC 9899:1999 (E), 7.14.1.1, Absatz 5 steht explizit, dass Du undefined
behaviour produzierst, wenn Du aus einem Signalhandler heraus free() aufrufst.
Wenn das fuer Dich nicht gleichbedeutend mit Problemen ist, dann brauchen wir
ueberhaupt nicht weiter zu diskutieren.

> Das Problem besteht einfach indirekt darin, daß im Zusammenhang
> mit Signal-Handlern die Möglichkeit besteht, daß free() aufgerufen wird
> zu einem Zeitpunkt, wo der Programmablauf bereits innerhalb von free()
> ist.
> Genau das ist ein Problem, falls free() tatsächlich nicht reentrant ist.
> Wenn man nun sicher verhindert, daß free() solchermaßen doppelt aufgerufen
> wird, hat man das Problem von außen beseitigt.

Nein. Es steht einer Implementation frei, intern beliebig oft malloc/free
aufzurufen. Da diese Aufrufe ausserhalb Deines Codes passieren kannst Du Dich
auch nicht davor schuetzen - es sei denn, Du haeltst Dich an das, was der
Standard Dir garantiert. Und der sagt nun mal, dass free() nicht aus einem
Signalhandler heraus aufgerufen werden darf.

Ausserdem beruht obige Aussage von Dir auf einer Annahme der Arbeitsweise von
free() die wohl oft richtig ist, aber nicht so sein muss. free() koennte zum
Beispiel auch globale temporaere Variablen gemeinsam mit anderen Funktionen
benutzen. In diesem Fall wuerde free auch dann crashen, wenn eine andere
Funktion unterbrochen worden waere.

> Ich kann das und machte das schon oft.

Die bisherige Diskussion deutet darauf hin, dass Du keine Ahnung hast, was Du
tust.

> Der Code verhindert sicher, daß free() mit einem Pointer unerwünscht
> nochmals -also doppelt- aufgerufen wird.
> Oder etwa nicht?!

Nein. Er verhindert das nicht, weil die Abfrage nicht atomar ist. Und er
verhindert auch nicht, dass free() mit einem anderen Pointer zur selben Zeit
aufgerufen wird. Insofern ist Dein Code komplett unnuetz.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

16:37:18 up 19 days, 1 min, 9 users, load average: 0.07, 0.04, 0.01

Rainer Weikusat

unread,
Nov 21, 2003, 11:11:22 AM11/21/03
to
Helmut Schellong <r...@schellong.biz> writes:
>>>Ich rufe free() nur so auf:
>>> if (p=Ptr[2], p) Ptr[2]=0, free(p);
>> Obiger Code ist sinnlos kompliziert, sowohl was die Zuweisung an die
>> Hilfsvariable, als auch was die unnoetige Verwendung des Komma Operators
>> angeht. Und er verbessert bezueglich eines Aufrufs von free() aus einem
>> Signalhandler heraus Deine Situation in keinster Weise. Auch mit diesem Code
>> ist das nicht zulaessig.
>
>> Das spielt beim Aufrufen von free() aus einem Signalhandler heraus keine
>> Rolle, weil gaengige Implementationen von free() das naemlich nicht tun - und
>> auch nicht tun muessen.
>
> Ich bestreite das einfach mal.
> Nirgendwo steht geschrieben, daß free() Probleme machen kann,
> nur *allein bewirkt* durch einen Aufruf in einem Signal-Handler.
> Ein Signal-Handler ist eine stinknormale Funktion, wie jede andere.
> Er wird lediglich asynchron von außen aufgerufen.

Das heißt zu einem beliebigen Zeitpunkt, an dem sich ein Programm
eventuell innerhalb einer Bibliotheksfunktion befinden könnte, die
einen globalen Zustand modifiziert und im Gegensatz zu multithreading
kann sich die Bibliothek nicht gegen asynchrone Aufrufe durch den
Kernel absichern, außer sie blockiert immer alle Signale, für die ein
Handler definiert wurde, bevor eine solche Modifkation durchgeführt
wird.

> Mein Code oben ist eine sinnvolle Pauschalmaßnahme.
> Ich war schon oft dankbar über solche meine Maßnahmen.
> Der Code verhindert sicher, daß free() mit einem Pointer unerwünscht
> nochmals -also doppelt- aufgerufen wird.
>
> Oder etwa nicht?!

Er dürfte das auf jedem System tun, auf dem eine load-Operation für
pointer atomic ist und wird andernfalls zu spektakulären und
unerklärlichen scheinendem Fehlverhalten führen.

Philipp Janda

unread,
Nov 21, 2003, 11:46:38 AM11/21/03
to
Helmut Schellong schrieb:

> Philipp Janda wrote:
>
>> Helmut Schellong schrieb:
>>
>>>>
>>>> Aber warum willst Du diese Funktion denn ueberhaupt im
>>>> Signal-Handler aufrufen? Doch wohl, weil sie fuer den nachfolgenden
>>>> Code im Signal-Handler wichtig ist (Initialisierung von irgendeinem
>>>> Microcontroller?!).
>>>
>>>
>>>
>>>
>>> Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen der
>>> CAN-NODE-State geprüft. Manchmal ist da ein Re-Init nötig.
>>
>>
>>
>> Und genau dieser benoetigte Re-Init findet manchmal nicht vollstaendig
>> statt.
>
>
> Doch.
> Wenn Flag=1 ist, beim Aufruf aus dem IRPT, bedeutet das, daß gerade
> ein Re-Init vollständig durchgeführt wird.

Aber noch nicht fertig ist!

> Der Aufruf aus dem IRPT ist also hier redundant.

Eventuell reden wir hier aneinander vorbei. Falls der einzige Sinn und
Zweck des Signal-Handlers darin besteht, die InitCAN1()-Funktion
aufzurufen, dann ist die angesprochene Loesung ausreichend sicher
(allerdings besteht dann eigentlich ueberhaupt keine Notwendigkeit, die
InitCAN1()-Funktion manuell im Hauptprogramm aufzurufen).
Probleme gibts dann, wenn man danach im selben Signal-Handler noch Code
ausfuehren will, der vom neuen Zustand abhaengt, da dieser neue Zustand
moeglicherweise noch nicht komplett erreicht ist (naemlich genau dann,
wenn Flag==1).

>
> Die Sendungen auf den CAN-Bus sind beispielsweise seit einer halben
> Sekunde beendet. Dabei war eventuell ein NODE-error aufgetreten.
> Die InitCAN1() braucht vielleicht 500us zum Durchlauf.
> Wenn also der IRPT auf eine bereits betretene InitCAN1() trifft,
> ist dieses Betreten garantiert lange nach der Feststellung des
> NODE-errors.
>

Ich verlasse mich normalerweise nicht auf "garantierte" Laufzeiten als
Ordnungskriterium.


Gruesse,
Philipp

Philipp Janda

unread,
Nov 21, 2003, 11:56:11 AM11/21/03
to
Ullrich von Bassewitz schrieb:

> Philipp Janda <siff...@gmx.net> wrote:
>
>>Ich habe da an folgendes gedacht:
>>
>>void myfree( void* ptr ) {
>> static volatile int flag = 0;
>> ++flag;
>> if( flag == 1 ) {
>> // begin critical section
>> free( ptr );
>> // end critical section
>> }
>> --flag;
>>}
>
>
> Das funktioniert auch nicht, weil der ++ Operator nicht atomar sein muss -
> auch dann nicht, wenn die Variable als volatile deklariert wurde.

Das mit dem atomar hatte ich urspruenglich ja auch gefordert, aber
mittlerweile bin ich eigentlich der Ansicht, dass es gar nicht noetig ist.

Das volatile garantiert doch, dass die Variable flag immer einen
gueltigen Wert enthaelt (in diesem Fall 1 oder 0), oder?
Selbst wenn die Funktion im Hauptprogramm direkt im ++flag; unterbrochen
wird, dann steht in der Variablen flag halt noch die 0 drin, und der
Signal-Handler greift auf die globalen Daten zu. Da die Funktion im
Hauptprogramm noch nicht so weit ist, gibt es da auch keine Probleme.
Die Funktion im Hauptprogramm faehrt dann nachdem der/die Signal-Handler
komplett abgearbeitet wurden (und demnach die Variable flag wieder auf 0
ist) dort fort wo sie aufgehoert hat (beim Setzen von flag auf 1).

Oder uebersehe ich da etwas?

>
>
>>Das aendert natuerlich nichts daran, dass die Funktion im Signal-Handler
>> dann vermutlich nicht mehr das Gewuenschte tut, aber es schuetzt die
>>globalen Datenstrukturen.
>
>
> Nein, leider nicht.
>
> Gruss
>
>
> Uz
>

Gruesse,
Philipp


Helmut Schellong

unread,
Nov 21, 2003, 11:55:44 AM11/21/03
to
Philipp Janda wrote:

> Ich habe da an folgendes gedacht:
>
> void myfree( void* ptr ) {
> static volatile int flag = 0;
> ++flag;
> if( flag == 1 ) {
> // begin critical section
> free( ptr );
> // end critical section
> }
> --flag;
> }

Ich verwende bei allen System-Funktionen ohnehin Wrapper.
Das ist bei größeren Programmen empfehlenswert.

Dabei muß man aber im Falle von free() mehr Aufwand treiben
als hier oben zu sehen.

void freeE(void *p)
{
static char volatile f;
static void *prev, *pa[32];
static int i;
if (!p || f && p==prev) return;
if (f) { if (i<32) pa[i++]= p; return; }
f= 1;
prev= p;
...
prev= 0;
f= 0;
return;
}

Das ist auch nur ein Ansatz, aber wohl besser.
Man kann noch unterscheiden, ob freeE im Zusammenhang
mit exit-Absicht aufgerufen wird oder nicht.
Man kann auch prev als f verwenden, und pa[0] wiederum als prev...

Man vermeidet so das Konzept des Signal-Suspendierens...
Die notwendige Größe von pa[] kennt man ja.

Ullrich von Bassewitz

unread,
Nov 21, 2003, 12:46:24 PM11/21/03
to
Philipp Janda <siff...@gmx.net> wrote:
> Das mit dem atomar hatte ich urspruenglich ja auch gefordert, aber
> mittlerweile bin ich eigentlich der Ansicht, dass es gar nicht noetig ist.
>
> Das volatile garantiert doch, dass die Variable flag immer einen
> gueltigen Wert enthaelt (in diesem Fall 1 oder 0), oder?

Nein. Der Standard sagt, dass bei einem Sequence Point jeweils ein gueltiger
Wert vorliegen muss. Der Standard macht keine Aussage darueber, was zwischen
zwei Sequence Points in einer Variable steht, im Gegenteil:

ISO/IEC 9899:1999 (E) 5.1.2.3 Program execution, Absatz 4

When the processing of the abstract machine is interrupted by receipt of a
signal, only the values of objects as of the previous sequence point may
be relied on. Objects that may be modified between the previous sequence
point and the next sequence point need not have received their correct
values yet.

Die Deklaration als volatile sorgt nur dafuer, dass bei einem Sequence Point
der tatsaechliche Wert auch in der Variable abgespeichert wird.

Ein Beispiel, wo ohne weiteres Zwischenwerte auftreten koennen ist der ++
Operator auf einer Maschine, deren Registerbreite kleiner ist als die
Variablenbreite. Auf einer 8 Bit Maschine koennte Dein Codeschnippsel so
uebersetzt werden:

;
; ++flag;
;
inc _flag
bne L0005
inc _flag+1
L0005:

Da der Wert nicht als ganzes angesprochen wird entstehen Zwischenwerte, d.h.
der tatsaechliche Wert im Speicher kann andere Werte annehmen als "flag" und
"flag+1". Da der Standard nur Aussagen zum Wert der Variable bei einem
Sequence Point macht ist die Uebersetzung korrekt.

Das ist natuerlich nur ein Beispiel, es lohnt also nicht, darueber zu
diskutieren, ob man das Problem vielleicht durch Deklarieren von flag als
sig_atomic_t umgehen koennte. Der Standard garantiert korrekte Werte nur fuer
Sequence Points, und es gibt keine Garantie, dass ein Signal ein Programm nur
bei einem Sequence Point unterbricht.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

18:13:00 up 19 days, 1:37, 9 users, load average: 0.06, 0.06, 0.06

Helmut Schellong

unread,
Nov 21, 2003, 1:18:10 PM11/21/03
to
Ullrich von Bassewitz wrote:
> Helmut Schellong <r...@schellong.biz> wrote:
>
>>>Das spielt beim Aufrufen von free() aus einem Signalhandler heraus keine
>>>Rolle, weil gaengige Implementationen von free() das naemlich nicht tun - und
>>>auch nicht tun muessen.
>>
>>Ich bestreite das einfach mal.
>
>
> Es waere schoen, wenn Du das nicht nur bestreiten, sondern Belege aus dem C
> Standard fuer Deine Meinung bringen wuerdest. Ich habe die Stelle genannt, wo
> steht, dass ein Aufruf von free() aus einem Signalhandler heraus *nicht*
> zulaessig ist. Ich habe Dir auch eine Liste von Funktionen genannt, die unter
> gaengigen Unix-System explizit von einem Signalhandler aus aufgerufen werden
> duerfen - und free() steht *nicht* in dieser Liste.

All functions not in the above table are considered to be unsafe with respect to signals.
In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001
shall behave as defined when called from or interrupted by a signal-catching function,
with a single exception: when a signal interrupts an unsafe function and the
signal-catching function calls an unsafe function, the behavior is undefined.

Das liest sich aber *wesentlich* harmloser als Du es hier ausdrückst.

Ich lese lediglich daraus, daß man diejenigen Funktionen, die nicht in der Liste stehen,
nicht vollkommen sorglos in Verbindung mit Signalen bringen darf.

> Insofern kannst Du eine Menge bestreiten, wenn Du das aber nicht belegst, ist
> es nur Geschwafel.

Erstens, ich behauptete nie, daß meine Vorschläge hier zu free im Handler
strikt konform sind.
Zweitens, was der Standard dort schreibt, kann man in der Praxis kaum
ernst nehmen, denn der Standard sagt, daß JEGLICHER Aufruf von Std-Funktionen
außer abort() und _Exit() UB sei.
Das bezieht sich wieder mal auf DOS oder noch viel kleiner, also den
kleinsten gemeinsamen Nenner.
Sowas ignoriere ich einfach geflissentlich.
Ich rufe glatt strcmp() im Handler auf, und ich bin sicher, daß ich bis an
mein Lebensende dadurch kein UB erleben werde.
Und wenn der Standard sagt, das wäre UB, so glaube ich das einfach nicht
- in der Praxis.
Nämlich wenn der Standard von UB schreibt, so heißt das lediglich, daß er
eine Entwicklung von Funktionen in dieser Weise erlaubt, was noch lange
nicht heißt, daß das in der Praxis tatsächlich vorkommt.

Sogar auf den 16Bit-Mikrokontrollern, mit denen ich arbeite, kann ich JEDE
beliebige Lib-Funktion in Interrupts aufrufen - ohne UB.

>>Nirgendwo steht geschrieben, daß free() Probleme machen kann,
>>nur *allein bewirkt* durch einen Aufruf in einem Signal-Handler.
>
>
> In ISO/IEC 9899:1999 (E), 7.14.1.1, Absatz 5 steht explizit, dass Du undefined
> behaviour produzierst, wenn Du aus einem Signalhandler heraus free() aufrufst.
> Wenn das fuer Dich nicht gleichbedeutend mit Problemen ist, dann brauchen wir
> ueberhaupt nicht weiter zu diskutieren.

Siehe oben.

>>Der Code verhindert sicher, daß free() mit einem Pointer unerwünscht
>>nochmals -also doppelt- aufgerufen wird.
>>Oder etwa nicht?!
>
>
> Nein. Er verhindert das nicht, weil die Abfrage nicht atomar ist. Und er
> verhindert auch nicht, dass free() mit einem anderen Pointer zur selben Zeit
> aufgerufen wird. Insofern ist Dein Code komplett unnuetz.

if (p=Ptr[2], p) Ptr[2]=0, free(p);

Ich sagte, daß hier ein Aufruf von free durch einen Handler
verhindert wird, wenn hier free bereits angelaufen wurde (doppelt), auf
diesen Pointer bezogen.
Das ist eine kleine Vorstufe von Sicherheitsmaßnahmen und bezieht
sich nur auf die mangelnde Reentranz von free.
Besonders toll ist dieser Code tatsächlich nicht.
Ich verwende sowieso freeE() als Wrapper.

Rainer Weikusat

unread,
Nov 21, 2003, 1:37:28 PM11/21/03
to
Helmut Schellong <r...@schellong.biz> writes:
> Ich rufe glatt strcmp() im Handler auf, und ich bin sicher, daß ich
> bis an mein Lebensende dadurch kein UB erleben werde.

Insofern es C angeht, tust Du das *fortwährend*. Was passiert, wenn Du
strcmp in einem signal handler aufrufst, ist durch die C-Norm nicht
definiert. Das bedeutet natürlich nicht, daß dann willkürlicher Unsinn
passieren wird, sondern lediglich, daß in Abhängigkeit von der
Implementierung eventuell ein anderer Effekt auftritt, als der in
diesem Dokument beschriebene.

Helmut Schellong

unread,
Nov 21, 2003, 1:47:37 PM11/21/03
to
Philipp Janda wrote:
> Helmut Schellong schrieb:
>
>> Philipp Janda wrote:
>>
>>> Helmut Schellong schrieb:
>>>
>>>>>
>>>>> Aber warum willst Du diese Funktion denn ueberhaupt im
>>>>> Signal-Handler aufrufen? Doch wohl, weil sie fuer den nachfolgenden
>>>>> Code im Signal-Handler wichtig ist (Initialisierung von irgendeinem
>>>>> Microcontroller?!).
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen der
>>>> CAN-NODE-State geprüft. Manchmal ist da ein Re-Init nötig.
>>>
>>>
>>>
>>>
>>> Und genau dieser benoetigte Re-Init findet manchmal nicht vollstaendig
>>> statt.
>>
>>
>>
>> Doch.
>> Wenn Flag=1 ist, beim Aufruf aus dem IRPT, bedeutet das, daß gerade
>> ein Re-Init vollständig durchgeführt wird.
>
>
> Aber noch nicht fertig ist!

Das macht nichts.
Der Interrupt ruft auf, weil er einen CAN-Reset haben will.
Und dieser CAN-Reset findet gerade statt, wenn f==1.

/* 244 * 4.096ms = 0.999424s ~ 1s */
switch (TBT._1s) {
default :
if (!TBT.can_end) {
/* CAN communication with connected devices */
TBT_CANcomm();
}
Mess_read_buffs();
break;
case 229:
if (Can.bus_off_timer > 0) Can.bus_off_timer--;
else if (Can.bus_off_timer_start) {
Can.bus_off_timer_start= 0;
CSR1= BUS_IRPT_EN;
InitCAN1();
}
if (DP.flg[0]&DP_CAN0EN) {
if (Can0.bus_off_timer > 0) Can0.bus_off_timer--;
else if (Can0.bus_off_timer_start) {
Can0.bus_off_timer_start= 0;
CSR0= BUS_IRPT_EN; // node transition IRPT enabled
InitCAN0();
}
}
break;
case 230: if (Modem[0].start>1) TBTmodem(1);
if (Modem[1].start>1) TBTmodem(2);
break;
case 231:

Helmut Schellong

unread,
Nov 21, 2003, 1:56:54 PM11/21/03
to

Im Ernst, ich habe schon lange eine C-Lib entwickelt
mit strcmp_F(), usw. usw. usw., weil ich pauschal
dieser irren Scheise aus dem Weg gehen will.

--------------------------------------------------------------------------
#if defined(F_strcmp_F) && !defined(DF_strcmp_F) && !defined(strcmp_F)
# define DF_strcmp_F
fSTATIC int strcmp_F(const byte *d0, const byte *s0)
{
register const byte *d=d0, *s=s0;
if (d!=s) {
for (; 1; ++d,++s) {
if (*d!=*s) return ( (int)*d - (int)*s );
if (!*s) break;
}
}
return (0);
}
#endif
--------------------------------------------------------------------------

Es ist zum Wiehern, daß sowas (im übertragenen Sinne) UB auslösen soll.

Ullrich von Bassewitz

unread,
Nov 21, 2003, 2:07:08 PM11/21/03
to
Helmut Schellong <r...@schellong.biz> wrote:
> All functions not in the above table are considered to be unsafe with respect to signals.
> In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001
> shall behave as defined when called from or interrupted by a signal-catching function,
> with a single exception: when a signal interrupts an unsafe function and the
> signal-catching function calls an unsafe function, the behavior is undefined.
>
> Das liest sich aber *wesentlich* harmloser als Du es hier ausdrückst.

"Undefined behaviour" ist so ziemlich das schlimmste, was der Standard
aussagen kann. Es bedeutet, dass alles passieren kann, und dass Dein Programm
keinerlei vom Standard zugesicherte Eigenschaften mehr hat. Wenn das fuer Dich
harmlos ist, dann kann Dir keiner mehr helfen.

Zumal obiges ja noch nicht mal aus dem C Standard, sondern aus der Open Group
Base Specification, die in vielen Punkten wesentlich mehr erlaubt als der C
Standard (und die ich nur wegen Deinen Unix Ausfluechten in's Gespraech
gebracht habe, eigentlich interessiert sie in dieser Gruppe ueberhaupt nicht).

> Erstens, ich behauptete nie, daß meine Vorschläge hier zu free im Handler
> strikt konform sind.

Was sollen sie dann in dieser Gruppe? Es waere mir neu, dass man sich hier mit
"C nach Helmut Schellong" beschaeftigt.

> Zweitens, was der Standard dort schreibt, kann man in der Praxis kaum
> ernst nehmen, denn der Standard sagt, daß JEGLICHER Aufruf von Std-Funktionen
> außer abort() und _Exit() UB sei.

Dann bist Du in der falschen Newsgroup. Vielleicht solltest Du Deine
Abneigungen woanders einbringen?

> if (p=Ptr[2], p) Ptr[2]=0, free(p);
>
> Ich sagte, daß hier ein Aufruf von free durch einen Handler
> verhindert wird, wenn hier free bereits angelaufen wurde (doppelt), auf
> diesen Pointer bezogen.

Und ich habe Dir schon mehrfach gesagt, dass das nicht funktioniert. Wenn das
Signal den Code zwischen Zuweisung und Test unterbricht, dann hast Du
einen mehrfachen Aufruf von free() mit demselben Zeiger, wobei beim zweiten
Aufruf der Block bereits freigegeben ist. Wurde free() unterbrochen, dann ist
ein Absturz mehr oder weniger vorprogrammiert.

Aber egal, wenn Dich der Standard nicht interessiert, dann lohnt es sich auch
nicht, weiter zu diskutieren.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

19:49:11 up 19 days, 3:13, 9 users, load average: 0.24, 0.42, 0.56

Helmut Schellong

unread,
Nov 21, 2003, 2:44:50 PM11/21/03
to
Ullrich von Bassewitz wrote:
> Helmut Schellong <r...@schellong.biz> wrote:
>
>>All functions not in the above table are considered to be unsafe with respect to signals.
>>In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001
>>shall behave as defined when called from or interrupted by a signal-catching function,
>>with a single exception: when a signal interrupts an unsafe function and the
>>signal-catching function calls an unsafe function, the behavior is undefined.
>>
>>Das liest sich aber *wesentlich* harmloser als Du es hier ausdrückst.
>
>
> "Undefined behaviour" ist so ziemlich das schlimmste, was der Standard
> aussagen kann. Es bedeutet, dass alles passieren kann, und dass Dein Programm
> keinerlei vom Standard zugesicherte Eigenschaften mehr hat. Wenn das fuer Dich
> harmlos ist, dann kann Dir keiner mehr helfen.

Der erste Satz oben sagt aus, daß ungenannte Funktionen als 'unsicher' betrachtet
werden müssen im Zusammenhang mit Signalen,
also hier nicht arglos verwendet werden können.
Dann wird gesagt, die Funktionen sollen sich alle gemäß ihrer Definition verhalten,
wenn sie in Handlern aufgerufen oder von Handlern unterbrochen werden,
mit einer Ausnahme: Wenn ein Signal eine 'unsichere' Funktion unterbricht und dieser
Handler gleichzeitig eine 'unsichere' Funktion aufruft, liegt UB vor.

Das Letztere kann man verhindern!
Man darf also free im Handler aufrufen, wenn keine unsichere Funktion
unterbrochen wurde.
Anders herum darf free von einem Handler unterbrochen werden, wenn in ihm
keine unsichere Funktion aufgerufen wird.

Wenn man also unsichere Funktionen durch Wrapper sicher macht, ist das Problem
beseitigt.

>>Zweitens, was der Standard dort schreibt, kann man in der Praxis kaum
>>ernst nehmen, denn der Standard sagt, daß JEGLICHER Aufruf von Std-Funktionen
>>außer abort() und _Exit() UB sei.
>
>
> Dann bist Du in der falschen Newsgroup. Vielleicht solltest Du Deine
> Abneigungen woanders einbringen?

Es gibt ein paar Dinge im Standard, die mich aufregen;
es haben schon oft hier Praktiker gesagt, daß man *gemäß Standard*
eigentlich gar kein 'richtiges' größeres, aktuell angemessenes Programm
schreiben kann.
Man könne nur dem Standard so weit wie möglich entsprechen.

>>if (p=Ptr[2], p) Ptr[2]=0, free(p);
>>
>>Ich sagte, daß hier ein Aufruf von free durch einen Handler
>>verhindert wird, wenn hier free bereits angelaufen wurde (doppelt), auf
>>diesen Pointer bezogen.
>
>
> Und ich habe Dir schon mehrfach gesagt, dass das nicht funktioniert. Wenn das
> Signal den Code zwischen Zuweisung und Test unterbricht, dann hast Du
> einen mehrfachen Aufruf von free() mit demselben Zeiger, wobei beim zweiten
> Aufruf der Block bereits freigegeben ist. Wurde free() unterbrochen, dann ist
> ein Absturz mehr oder weniger vorprogrammiert.

Ich sagte schon, daß dieser Code nicht besonders toll ist.

Aber, jetzt ist zum zweiten Mal das Mißverständnis aufgekommen:
Ich meinte, wenn HIER der Code IN DER free() DRIN IST, kann ein
Handler free NICHT AUCH NOCH (doppelt) mit DIESEM POINTER aufrufen,
weil der ja dann auf NULL ist.

*Nacheinander* geht das schon.
Deshalb ist dieser Code nur viertelgar.
Vergessen wir doch einfach *diese* doofe Code-Zeile.

Rainer Weikusat

unread,
Nov 21, 2003, 2:50:25 PM11/21/03
to
u...@spamtrap.musoftware.de (Ullrich von Bassewitz) writes:
> Helmut Schellong <r...@schellong.biz> wrote:
>> All functions not in the above table are considered to be unsafe with respect to signals.
>> In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001
>> shall behave as defined when called from or interrupted by a signal-catching function,
>> with a single exception: when a signal interrupts an unsafe function and the
>> signal-catching function calls an unsafe function, the behavior is undefined.
>>
>> Das liest sich aber *wesentlich* harmloser als Du es hier ausdrückst.
>
> "Undefined behaviour" ist so ziemlich das schlimmste, was der Standard
> aussagen kann. Es bedeutet, dass alles passieren kann, und dass Dein Programm
> keinerlei vom Standard zugesicherte Eigenschaften mehr hat.

Das ist eine sehr freie Interpretation des englischen Verbs 'use' und
des Kontextes, in dem es dort gebraucht wird. Wodurch wird die
gestützt?

Philipp Janda

unread,
Nov 21, 2003, 4:09:45 PM11/21/03
to
Ullrich von Bassewitz schrieb:

Naja, dann duerfen die Signale halt bloss nach Sequence Points kommen... ;-)

Ok, ok, ich nehm alles zurueck und behaupte das Gegenteil.

>
> Das ist natuerlich nur ein Beispiel, es lohnt also nicht, darueber zu
> diskutieren, ob man das Problem vielleicht durch Deklarieren von flag als
> sig_atomic_t umgehen koennte. Der Standard garantiert korrekte Werte nur fuer
> Sequence Points, und es gibt keine Garantie, dass ein Signal ein Programm nur
> bei einem Sequence Point unterbricht.
>
> Gruss
>
>
> Uz
>

Gruesse,
Philipp


Philipp Janda

unread,
Nov 21, 2003, 4:25:19 PM11/21/03
to
Helmut Schellong schrieb:

> Philipp Janda wrote:
>
>> Helmut Schellong schrieb:
>>
>>> Philipp Janda wrote:
>>>
>>>> Helmut Schellong schrieb:
>>>>
>>>>>>
>>>>>> Aber warum willst Du diese Funktion denn ueberhaupt im
>>>>>> Signal-Handler aufrufen? Doch wohl, weil sie fuer den nachfolgenden
>>>>>> Code im Signal-Handler wichtig ist (Initialisierung von irgendeinem
>>>>>> Microcontroller?!).
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Nein, im Interrupt wird jede Sekunde nach CAN-Sendungen der
>>>>> CAN-NODE-State geprüft. Manchmal ist da ein Re-Init nötig.
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> Und genau dieser benoetigte Re-Init findet manchmal nicht vollstaendig
>>>> statt.
>>>
>>>
>>>
>>>
>>> Doch.
>>> Wenn Flag=1 ist, beim Aufruf aus dem IRPT, bedeutet das, daß gerade
>>> ein Re-Init vollständig durchgeführt wird.
>>
>>
>>
>> Aber noch nicht fertig ist!
>
>
> Das macht nichts.

Sag Du es mir, da ich es aus dem unteren Code-Fragment nicht entnehmen
kann (selbst wenn ich einen zusammenhanglosen Haufen C-Code verstehen
wuerde).

> Der Interrupt ruft auf, weil er einen CAN-Reset haben will.

Wir reden aber immer noch ueber einen Signal-Handler (einziger Parameter
ist die Signal-Nummer als int), oder? Falls ja, dann sehe ich im Code
einige globale Variablen, die im Fall f==1 eventuell in einem
inkonsistenten Zustand sind.

Der interessante Teil (naemlich der Code *nach* dem InitCAN1() ist
leider nicht dabei.

Falls kein Code mehr kommt oder er keine Annahmen ueber den
Initialisierungszustand Deiner Mikrokontroller macht, dann darf ich mich
mal selbst zitieren:

<zitat>


Eventuell reden wir hier aneinander vorbei. Falls der einzige Sinn und
Zweck des Signal-Handlers darin besteht, die InitCAN1()-Funktion
aufzurufen, dann ist die angesprochene Loesung ausreichend sicher
(allerdings besteht dann eigentlich ueberhaupt keine Notwendigkeit, die
InitCAN1()-Funktion manuell im Hauptprogramm aufzurufen).
Probleme gibts dann, wenn man danach im selben Signal-Handler noch Code
ausfuehren will, der vom neuen Zustand abhaengt, da dieser neue Zustand
moeglicherweise noch nicht komplett erreicht ist (naemlich genau dann,
wenn Flag==1).

</zitat>

Wobei sich allerdings weiter unten in diesem Thread mittlerweile ergeben
hat, dass die angesprochene Loesung doch nicht ausreichend sicher ist.

Gruesse,
Philipp

Helmut Schellong

unread,
Nov 21, 2003, 6:22:52 PM11/21/03
to

Nein. Es gibt dort keine Signal-Handler.
Es gibt nur echte Interrupt-Routinen.

Da unten ist ein Ausschnitt aus einem Interrupt, der mit allen direkt
zugehörigen Funktionen, die daraus aufgerufen werden, ~3000 Zeilen hat.

__interrupt void TBTinterrupt(void) { ; }

Der wird alle 4ms durch Hardware aufgerufen und hat eine
Zykluszeit von 1 s, wie man unten sieht.

> Falls ja, dann sehe ich im Code
> einige globale Variablen, die im Fall f==1 eventuell in einem
> inkonsistenten Zustand sind.

Sind sie nie, können sie gar nicht sein.
Wie kommst Du darauf?

Es gibt keinen.
Dieser Code-Ausschnitt ist vollständig.
Nur ein paar Zeilen nach dem switch, die nix damit zu tun haben.

> Wobei sich allerdings weiter unten in diesem Thread mittlerweile ergeben
> hat, dass die angesprochene Loesung doch nicht ausreichend sicher ist.

Doch, InitCAN1() ist sicher durch das Flag darin.

Jens Schweikhardt

unread,
Nov 24, 2003, 7:59:50 AM11/24/03
to
Helmut Schellong <r...@schellong.biz> wrote
in <bplkt3$cko$07$1...@news.t-online.com>:
[deletia (Die wunderbare Welt des Helmut S.)]
# Zweitens, was der Standard dort schreibt, kann man in der Praxis kaum
# ernst nehmen,

s/Standard/Schellong

Du redest hier in dclc der Ignoranz technischer Spezifikationen das
Wort. Es dürfte kaum schlechtere Ingenieure geben.

Regards,

Jens
--
Jens Schweikhardt http://www.schweikhardt.net/
SIGSIG -- signature too long (core dumped)

Helmut Schellong

unread,
Nov 24, 2003, 9:25:58 AM11/24/03
to
Jens Schweikhardt wrote:
> Helmut Schellong <r...@schellong.biz> wrote
> in <bplkt3$cko$07$1...@news.t-online.com>:
> [deletia (Die wunderbare Welt des Helmut S.)]
> # Zweitens, was der Standard dort schreibt, kann man in der Praxis kaum
> # ernst nehmen,
>
> s/Standard/Schellong
>
> Du redest hier in dclc der Ignoranz technischer Spezifikationen das
> Wort. Es dürfte kaum schlechtere Ingenieure geben.

Kommentaria:
Im Hardware-Bereich sind die Standards auch vernünftiger abgefaßt.

Bodo Thiesen

unread,
Nov 24, 2003, 5:05:50 PM11/24/03
to

Sollte trotzdem egal sein. Da direkt auf flag == 1 getestet wird, stimmt es
entweder, oder es stimmt nicht. Wichtig war doch nur, daß die zu schützende
Funktion nicht paralell vom Hauptprogramm und vom Sighandler aufgerufen wird.

Also: ++flag;

Wenn der Befehl noch nicht ausgeführt wurde, ist flag = 0.
Wenn der Befehl komplett ausgeführt wurde, ist flag = 1.
Wenn der Befehl unterbrochen wurde, ist flag entweder 0, oder 1, oder
undefiniert. Die ersten beiden Fälle kennen wir schon, interessant ist also
undefiniert:

1. ++flag ergibt wieder 0 (dann wird der Sighandler nix tun, und der
Unterbrochene wird seine Aktion fortsetzen.
2. ++flag ergibt immernoch 1 (dann wird die Aktion im Sighandler ausgeführt,
und _danach_ im Hauptprogramm, dagegen müsste man sich dann noch absichern)
3. ++flag ergibt irgendetwas ganz anderes. Dann wird der Sighandler ebenfalls
nichts machen, und die Aktion wird im Hauptprogramm durchgeführt.

Nächste überlegung wäre es, int in unsigned char zu ändern, damit bei "flag ist
undefiniert" keine Trap representation entstehen kann. Da dieses flag dann auch
weder padding bits noch ähnliche Spielereien enthalten darf, sollte auch
++flag[SIGNAL]++flag;--flag;[SIGRET];[aktion]--flag; funktionieren, selbst wenn
nach dem ersten ++flag irgendetwas ganz obskures in flag drin steht. Dieses
Obskure wird nämlich einfach um eins erhöht, und hinterher um eins vermindert -
der Unterbrochene Inkrement bekommt davon nix mit.

Ich meine, das bleibt sicher.

Gruß, Bodo
--
MS Outlook Express?->[DE: http://piology.org/ILOVEYOU-Signature-FAQ.html]

@@@@@ GEGEN TCG aka. TCPA: @@@@@ [DE: http://www.againsttcpa.com]

Helmut Schellong

unread,
Nov 25, 2003, 5:37:40 AM11/25/03
to

Natürlich. Zwischen =1 und ++ besteht diesbezüglich auch kein Unterschied,
weil bei flag=1 der Compiler in aller Regel ohnehin
MOVN A, #1
MOV flag, A
macht, wobei dazwischen ja ein IRPT kommen kann.

Es soll ja hier verhindert werden, daß der Funktionskörper
zwischen den beiden Flag-Zugriffen zweimal gleichzeitig betreten werden kann.
Genau das wird zuverlässig durch meine Maßnahme verhindert.
Eine Abarbeitung zweimal komplett hintereinander ist unschädlich.

Wenn es darum geht, einen Inhalt stets korrekt zu verändern, der global
an mehreren Stellen verändert wird und auch in Interrupts verändert wird,
muß man mitunter
__DI(); ........ __EI();
anwenden.
In einem hochpriorisierten Interrupt muß man das beispielsweise nicht.
Zwischen gleich priorisierten Interrupts auch nicht.
Das entspricht dem Setzen auf SIG_IGN der im Programm abgefangenen
Signale.

Dirk Clemens

unread,
Nov 25, 2003, 7:24:57 AM11/25/03
to
Philipp Janda wrote:
> Claus Reibenstein schrieb:
>
>> Philipp Janda schrieb:
>>

>
> Ich habe da an folgendes gedacht:
>
> void myfree( void* ptr ) {
> static volatile int flag = 0;
> ++flag;
> if( flag == 1 ) {
> // begin critical section
> free( ptr );
> // end critical section
> }
> --flag;
> }

Und was ist wenn ++flag so implementiert ist:
1.) copy memory 'flag' to register
2.) increment register
3.) copy register to memory 'flag'

... und der Interruppt nach Schritt 1) erfolgt:

Also:

Thread A:
hole flag (Wert 0)
Thread B:
hole flag (Wert 0)
++flag (Wert 1)
schreib flag (Wert 1)
... tue was

Thread B:
schreib flag (Wert 1)
--> hier glauben beider threads, dass flag den Wert 1 ha !!!
... tue was

Thread B:
hole flag (Wert 1)
--flag (Wert 0)
schreib flag (Wert 0)

Thread A:
hole flag (Wert 0)
--flag (Wert -1)
schreib flag (Wert -1)

Und hier hat flag einen ganz unerwarteten Wert.

Für obiges Beispiel muss man auf ein unteilbares Inkrement zurückgreifen,
welches sich unter Win INTERLOCKED nennt.
Auf mc kann man häufig die Interrupts sperren.

Lemmi

--
mailto: d...@18xx.de
homepage: http://www.18xx.de


Dirk Clemens

unread,
Nov 25, 2003, 7:33:49 AM11/25/03
to
Bodo Thiesen wrote:
> u...@spamtrap.musoftware.de (Ullrich von Bassewitz) wrote:

> Also: ++flag;
>
> Wenn der Befehl noch nicht ausgeführt wurde, ist flag = 0.
> Wenn der Befehl komplett ausgeführt wurde, ist flag = 1.
> Wenn der Befehl unterbrochen wurde, ist flag entweder 0, oder 1, oder
> undefiniert. Die ersten beiden Fälle kennen wir schon, interessant ist also
> undefiniert:

Und wenn der Befehl ausgeführt und der Wert noch nicht zurückgeschrieben
wurde ist flag im Speicher 0 und im ausführenden Thread 1,
hat also gleichzeitig 2 unterschiedliche Werte!

Siehe hierzu auch mein Beipiel in einem andren Zweig dieser Diskussion.

Philipp Janda

unread,
Nov 25, 2003, 8:09:30 AM11/25/03
to
Dirk Clemens schrieb:

> Philipp Janda wrote:
>
>>Claus Reibenstein schrieb:
>>
>>
>>>Philipp Janda schrieb:
>>>
>
>
>>Ich habe da an folgendes gedacht:
>>
>>void myfree( void* ptr ) {
>> static volatile int flag = 0;
>> ++flag;
>> if( flag == 1 ) {
>> // begin critical section
>> free( ptr );
>> // end critical section
>> }
>> --flag;
>>}
>
>
> Und was ist wenn ++flag so implementiert ist:
> 1.) copy memory 'flag' to register
> 2.) increment register
> 3.) copy register to memory 'flag'

Ja genau, so hab ich mir das ungefaehr vorgestellt.

>
> ... und der Interruppt nach Schritt 1) erfolgt:
>
> Also:
>
> Thread A:
> hole flag (Wert 0)
> Thread B:
> hole flag (Wert 0)
> ++flag (Wert 1)
> schreib flag (Wert 1)
> ... tue was
>
> Thread B:

(war hier vielleicht Thread A gemeint?!)

> schreib flag (Wert 1)
> --> hier glauben beider threads, dass flag den Wert 1 ha !!!
> ... tue was
>
> Thread B:
> hole flag (Wert 1)
> --flag (Wert 0)
> schreib flag (Wert 0)
>
> Thread A:
> hole flag (Wert 0)
> --flag (Wert -1)
> schreib flag (Wert -1)
>
> Und hier hat flag einen ganz unerwarteten Wert.

Ich nehme mal an, dass wir von verschiedenen Sachen reden: Ich war mir
von Anfang an im Klaren darueber, dass der Code nicht threadsicher ist.
Ich bin von Signal-Handlern ausgegangen, und ein wichtiger Unterschied
ist, dass sich die Funktionen nicht gegenseitig unterbrechen koennen
(der zuletzt aufgerufene Signal-Handler unterbricht die davor gerade
ausgefuehrte Funktion aber niemals umgekehrt). Daher wird der
Signal-Handler komplett abgearbeitet bevor zur vorherigen Funktion
zurueckgekehrt wird.

Also ungefaehr so:

Hauptfunktion:
hole flag (Wert 0)

Signalhandler:
hole flag (Wert 0)
++flag;


schreib flag (Wert 1)
... tue was

hole flag (Wert 1)
--flag;
schreib flag (Wert 0);

Hauptfunktion:
++flag;
schreib flag (Wert 1)
... usw.

Der kritische Fall ist meiner Meinung nach:

Hauptfunktion:
hole flag (Wert 0)
++flag;
schreib flag *teilweise* (Wert xyz)

Signalhandler:
hole flag (Wert xyz)
++flag;
schreib flag (Wert xyz+1)

Und hier kommts dann darauf an, was xyz+1 nun wirklich ist. Falls es ==1
ist, dann wird der Signal-Handler normal abgearbeitet (macht aber nix,
weil wir im Hauptprogramm noch nicht im kritischen Abschnitt waren).
Ansonsten wird der Signal-Handler nicht ausgefuehrt, obwohl er
eigentlich noch ausgefuehrt werden koennte. Die einzigen wirklichen
Probleme treten mit ungueltigen Werten fuer xyz (Trap-Repraesentationen)
oder mit Ueberlaeufen (oder Trap-Repraesentationen) bei xyz+1 auf.
Falls diese Sonderfaelle nicht eintreten, gehts ganz normal weiter...

Signalhandler:
... tue was oder auch nicht
hole flag (Wert xyz+1)
--flag;
schreib flag (Wert xyz, es sei denn, wir hatten einen Ueberlauf)

Hauptfunktion:
schreib Rest von flag (Wert 1, es sei denn, wir hatten einen Ueberlauf)
... usw.

>
> Für obiges Beispiel muss man auf ein unteilbares Inkrement zurückgreifen,

Das waere sicherlich eine bessere Loesung.

> welches sich unter Win INTERLOCKED nennt.
> Auf mc kann man häufig die Interrupts sperren.
>
> Lemmi
>

Gruesse,
Philipp

Dirk Clemens

unread,
Nov 25, 2003, 9:44:18 AM11/25/03
to
Philipp Janda wrote:

> Ich nehme mal an, dass wir von verschiedenen Sachen reden: Ich war mir
> von Anfang an im Klaren darueber, dass der Code nicht threadsicher ist.
> Ich bin von Signal-Handlern ausgegangen, und ein wichtiger Unterschied
> ist, dass sich die Funktionen nicht gegenseitig unterbrechen koennen
> (der zuletzt aufgerufene Signal-Handler unterbricht die davor gerade
> ausgefuehrte Funktion aber niemals umgekehrt). Daher wird der
> Signal-Handler komplett abgearbeitet bevor zur vorherigen Funktion
> zurueckgekehrt wird.


Da habe ich wohl nicht ganz aufgepasst :-(
Für einen Signal-Handle nach deiner Beschreibung ,sollte' es wohl
reichen (Kurz überdacht)

Ullrich von Bassewitz

unread,
Nov 25, 2003, 3:34:45 PM11/25/03
to
Bodo Thiesen <bot...@gmx.de> wrote:
> Ich meine, das bleibt sicher.

Und ich meine, dass die Diskussion nicht lohnt. Ausgangspunkt war ja, ob man
free() gegenueber einem Signalhandler so absichern kann, und das kann man
definitiv nicht, weil man nicht weiss, von wo aus free() aufgerufen wird, ob
es globale Variablen mit anderen Funktionen shared usw.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

21:08:55 up 23 days, 4:32, 9 users, load average: 0.01, 0.04, 0.03

Bodo Thiesen

unread,
Nov 28, 2003, 2:02:48 PM11/28/03
to
Dirk Clemens <d...@18xx.de> wrote:

Wir waren bei Signalen, nicht bei Threads.

Bodo Thiesen

unread,
Nov 28, 2003, 2:02:49 PM11/28/03
to
Philipp Janda <siff...@gmx.net> wrote:

> Der kritische Fall ist meiner Meinung nach:
>
> Hauptfunktion:
> hole flag (Wert 0)
> ++flag;
> schreib flag *teilweise* (Wert xyz)
>
> Signalhandler:
> hole flag (Wert xyz)
> ++flag;
> schreib flag (Wert xyz+1)
>
> Und hier kommts dann darauf an, was xyz+1 nun wirklich ist. Falls es ==1
> ist, dann wird der Signal-Handler normal abgearbeitet (macht aber nix,
> weil wir im Hauptprogramm noch nicht im kritischen Abschnitt waren).
> Ansonsten wird der Signal-Handler nicht ausgefuehrt, obwohl er
> eigentlich noch ausgefuehrt werden koennte. Die einzigen wirklichen
> Probleme treten mit ungueltigen Werten fuer xyz (Trap-Repraesentationen)
> oder mit Ueberlaeufen (oder Trap-Repraesentationen) bei xyz+1 auf.

Und die Trap-Representations kann man mit unsigned char verhindern.

Philipp Janda

unread,
Nov 28, 2003, 2:04:18 PM11/28/03
to
Bodo Thiesen schrieb:

> Philipp Janda <siff...@gmx.net> wrote:
>
>
>>Der kritische Fall ist meiner Meinung nach:
>>
>>Hauptfunktion:
>> hole flag (Wert 0)
>> ++flag;
>> schreib flag *teilweise* (Wert xyz)
>>
>>Signalhandler:
>> hole flag (Wert xyz)
>> ++flag;
>> schreib flag (Wert xyz+1)
>>
>>Und hier kommts dann darauf an, was xyz+1 nun wirklich ist. Falls es ==1
>>ist, dann wird der Signal-Handler normal abgearbeitet (macht aber nix,
>>weil wir im Hauptprogramm noch nicht im kritischen Abschnitt waren).
>>Ansonsten wird der Signal-Handler nicht ausgefuehrt, obwohl er
>>eigentlich noch ausgefuehrt werden koennte. Die einzigen wirklichen
>>Probleme treten mit ungueltigen Werten fuer xyz (Trap-Repraesentationen)
>>oder mit Ueberlaeufen (oder Trap-Repraesentationen) bei xyz+1 auf.
>
>
> Und die Trap-Representations kann man mit unsigned char verhindern.

Andererseits erhoeht man dadurch normalerweise die Wahrscheinlichkeit
von Ueberlaeufen drastisch (im Vergleich zu einem int). Die sauberste
Variante (sofern man bei diesem Hack ueberhaupt von sauber sprechen
kann) ist vermutlich mit sigatomic_t.

>
> Gruß, Bodo

Gruesse,
Philipp

Bodo Thiesen

unread,
Nov 28, 2003, 2:17:14 PM11/28/03
to
u...@spamtrap.musoftware.de (Ullrich von Bassewitz) wrote:

>Bodo Thiesen <bot...@gmx.de> wrote:
>> Ich meine, das bleibt sicher.
>
>Und ich meine, dass die Diskussion nicht lohnt. Ausgangspunkt war ja, ob man
>free() gegenueber einem Signalhandler so absichern kann, und das kann man
>definitiv nicht, weil man nicht weiss, von wo aus free() aufgerufen wird, ob
>es globale Variablen mit anderen Funktionen shared usw.

Gut, die Diskussion lohnt trotzdem, denn wenn ich nicht weiß, ob ich in
meinem Signal Handler free() aufrufen darf, dann kann ich mir immernoch eine
Liste anlegen, in der ich den freizugebenden Speicherbereich eintrage (wobei
diese Liste natürlich nicht via malloc im Signal Handler angelegt werden
darf, der Speicher muß schon da sein). Abgesehen davon frage ich mich im
Moment, wieso hier soviel über ++flag diskutiert wird. Ich würde - nachdem
ich mir die Passage im Standard durchgelesen habe - einfach mal sig_atomic_t
benutzen, und hätte das ganze Problem überhaupt garnicht. (Ok, das mit dem
free() bleibt - das ist scheinbar erst mit POSIX möglich - das schwirrte mir
die ganze Zeit im Kopf rum, seit Helmut das hier zitiert hatte - aber es
interessiert ja nicht, schließlich sind wir hier nicht in der NG
de.comp.lang.posix ;-)

Ullrich von Bassewitz

unread,
Nov 28, 2003, 4:36:45 PM11/28/03
to
Bodo Thiesen <bot...@gmx.de> wrote:
> Gut, die Diskussion lohnt trotzdem, denn wenn ich nicht weiß, ob ich in
> meinem Signal Handler free() aufrufen darf, dann kann ich mir immernoch eine
> Liste anlegen, in der ich den freizugebenden Speicherbereich eintrage (wobei
> diese Liste natürlich nicht via malloc im Signal Handler angelegt werden
> darf, der Speicher muß schon da sein).

Erstens sagt der Standard eindeutig, dass Du free() *nicht* aufrufen darfst,
d.h. die Situation stellt sich ueberhaupt nicht so dar, dass Du es nicht weiss
(zumindest nicht in dieser Gruppe). Zweitens kannst Du im Signalhandler nichts
in so eine Liste eintragen oder auslesen, weil der Standard explizit sagt,
dass ein Signalhandler nur Variablen von Type sig_atomic_t beschreiben darf.

> Abgesehen davon frage ich mich im
> Moment, wieso hier soviel über ++flag diskutiert wird.

Der Punkt wurde (genau wie der Unix Nebenschauplatz) von Helmut eingebracht,
um davon abzulenken, welchen Mist er erzaehlt hat:-)

> Ich würde - nachdem
> ich mir die Passage im Standard durchgelesen habe - einfach mal sig_atomic_t
> benutzen, und hätte das ganze Problem überhaupt garnicht.

Falsch. Der Standard sagt, dass Du in einem Signalhandler einer Variable vom
Typ sig_atomic_t etwas *zuweisen* darfst. Er sagt nicht, dass Du sie
inkrementieren darfst.

> (Ok, das mit dem
> free() bleibt - das ist scheinbar erst mit POSIX möglich - das schwirrte mir
> die ganze Zeit im Kopf rum, seit Helmut das hier zitiert hatte - aber es
> interessiert ja nicht, schließlich sind wir hier nicht in der NG
> de.comp.lang.posix ;-)

Der Aufruf von free() aus einem Signalhandler heraus ist auch unter POSIX
nicht moeglich. Der Abschnitt, den Helmut aus dem von mir genannten Dokument
zitiert, sagt genau das. Wo Du gegenteilige Information her haben willst ist
mir nicht klar.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

20:42:01 up 26 days, 4:05, 10 users, load average: 0.10, 0.06, 0.10

Helmut Schellong

unread,
Nov 28, 2003, 5:22:37 PM11/28/03
to
Ullrich von Bassewitz wrote:
> Bodo Thiesen <bot...@gmx.de> wrote:
>
>>Gut, die Diskussion lohnt trotzdem, denn wenn ich nicht weiß, ob ich in
>>meinem Signal Handler free() aufrufen darf, dann kann ich mir immernoch eine
>>Liste anlegen, in der ich den freizugebenden Speicherbereich eintrage (wobei
>>diese Liste natürlich nicht via malloc im Signal Handler angelegt werden
>>darf, der Speicher muß schon da sein).
>
>
> Erstens sagt der Standard eindeutig, dass Du free() *nicht* aufrufen darfst,
> d.h. die Situation stellt sich ueberhaupt nicht so dar, dass Du es nicht weiss
> (zumindest nicht in dieser Gruppe). Zweitens kannst Du im Signalhandler nichts
> in so eine Liste eintragen oder auslesen, weil der Standard explizit sagt,
> dass ein Signalhandler nur Variablen von Type sig_atomic_t beschreiben darf.

Der Standard ist in diesem Punkt leichtem Schwachsinn anheim gefallen.
Es weist darauf hin, daß Funktionen so entwickelt werden sollen, daß sie jederzeit
von Signalen unterbrochen werden können als auch in Handlern beliebig aufgerufen
werden können, ohne dadurch die geringsten Probleme zu verursachen.

Gleichzeitig gibt er praktisch alle ANSI-C-Funktionen frei, daß diese beliebige
Probleme verursachen dürfen, in solchen Fällen.
Hier so, dort so.
Weiterhin sind Aufrufe von signal in Handlern UB.
In der Praxis MUSS man aber UNBEDINGT signal in Handlern aufrufen, da z.B.
vor dem Aufruf von signal das betreffende Signal stets auf SIG_DFL gesetzt wird.
In Unix-System-C-Handbüchern, die ich in den 80ern las, wurde sogar GEFORDERT,
daß man typischerweise im Handler zunächst sig,SIG_IGN, dann sig,SigCatch
aufrufen soll.

Außerdem gibt es doch Signale, wie jeder weiß.
Und diese können ohnehin jedes Programm beliebig unterbrechen, auch wenn das
Programm gerade in free oder malloc verweilt, und auch (oder gerade dann),
wenn man im Programm überhaupt gar nichts mit signal() usw. programmiert hat.
Irgendwas paßt hier doch nicht.

>>Abgesehen davon frage ich mich im
>>Moment, wieso hier soviel über ++flag diskutiert wird.
>
>
> Der Punkt wurde (genau wie der Unix Nebenschauplatz) von Helmut eingebracht,
> um davon abzulenken, welchen Mist er erzaehlt hat:-)

Nein. Der Punkt ++flag wurde *gar nicht* von mir eingebracht.

Von mir wurde Flag=1; ... Flag=0; eingebracht, um einen Bereich vor
doppeltem Betreten zu schützen - was auch sicher funktioniert.

>>Ich würde - nachdem
>>ich mir die Passage im Standard durchgelesen habe - einfach mal sig_atomic_t
>>benutzen, und hätte das ganze Problem überhaupt garnicht.
>
>
> Falsch. Der Standard sagt, dass Du in einem Signalhandler einer Variable vom
> Typ sig_atomic_t etwas *zuweisen* darfst. Er sagt nicht, dass Du sie
> inkrementieren darfst.

Der Standard ist in diesem Punkt ein Vollidiot.
In jedem beliebigem MC für 3 EUR kann ich in Interrupts
GRENZENLOS jede beliebige Aktion ausführen, wie es mir beliebt
und solange Ressourcen nicht völlig aufgebraucht werden.

Der Standard hätte hier etwa das Hundertfache fordern müssen,
um praxisgerecht zu sein.

Ein solcher Handler:
void Catcher(int sig) { sig_atom2=sig; return; }
nützt niemandem etwas, weil Polling außerhalb
gar nicht sichergestellt werden kann.
Da könnte man gleich die gesamte Signal-Einrichtung wegwerfen.

Juergen Ilse

unread,
Nov 28, 2003, 6:10:43 PM11/28/03
to
Hallo,

Bodo Thiesen <bot...@gmx.de> wrote:
> u...@spamtrap.musoftware.de (Ullrich von Bassewitz) wrote:
>>Bodo Thiesen <bot...@gmx.de> wrote:
>>> Ich meine, das bleibt sicher.
>>Und ich meine, dass die Diskussion nicht lohnt. Ausgangspunkt war ja, ob man
>>free() gegenueber einem Signalhandler so absichern kann, und das kann man
>>definitiv nicht, weil man nicht weiss, von wo aus free() aufgerufen wird, ob
>>es globale Variablen mit anderen Funktionen shared usw.
> Gut, die Diskussion lohnt trotzdem, denn wenn ich nicht weiß, ob ich in
> meinem Signal Handler free() aufrufen darf, dann kann ich mir immernoch eine
> Liste anlegen, in der ich den freizugebenden Speicherbereich eintrage (wobei
> diese Liste natürlich nicht via malloc im Signal Handler angelegt werden
> darf, der Speicher muß schon da sein).

Das nuetzt dir nichts, solange free() selbst nicht reentrent ist (und
das wird dir im allegemeinen eher nicht garantiert). Es ist moeglich
(und sogar wahrscheinlich), dass free() an globalen Datenstrukturen
des laufenden Prozesses herumdoktert, um die Speicherverwaltung zu
aendern. Wenn free zum falschen Zeitpunkt unterbrochen wird, hat es
einige der notwnedigen Aenderungen bereits durchgefuehrt, andere aber
evt. noch nicht. Das zweite aufgerufene free() arbeitet dann (unab-
haengig davon, ob es nun mit legalen oder "wilden" Pointerwerten auf-
gerufen wurde) auf inkonsistenten Datenstrukturen, und nach Rueckkehr
aus dem signal-handler macht das unterbrochene free() dort weiter, wo
es aufgehoert hatte, ohne jedoch mitzubekommen, dass da ein weiterer
Aufruf von free() an den Datenstrukturen herumgekodelt hat ...
Aerger bis hin zu crashes sind unter solchen Umstaenden nur eine Frage
der Zeit (bis es tatsaechlich mal den "falschen Zeitpunkt" getroffen
hat). Ich denke, genau das wollte Ullrich mit seinen Ausfuehrungen
sagen, und deine eigene "malloc-Liste" hilft dagegen nicht das geringste.
Aus einem Signal-Handler darf nur das aufgerufen werden, was vom Standard
(oder wenn man auf portierbarkeit nicht SOOO viel Wert legt von der ge-
nutzten Implementierung) als unproblematisch fuer den Einsatz in Signal-
handlern angegeben wird. Zumindest was den Standard betrifft gehoert
free() eindeutig *nicht* in diese Kathegorie und ein naives "was soll
denn bei einem free() schon schief gehen" hilft nicht wirklich weiter.

Tschuess,
Juergen Ilse (jue...@usenet-verwaltung.de)
--
Das Netz ist Freude. Es ist Ekstase, die jeden einzelnen Nerv erglühen
läßt. Es ist Duft, den man fühlt. Es ist ein Bild, das man riecht.
Es ist Erfüllung - ein Geschmack, neben dem alles andere schal ist.
("Netzreiter-Preisung" aus dem Buch "Der Netzparasit" von Andreas Brandhorst)

Helmut Schellong

unread,
Nov 28, 2003, 6:44:11 PM11/28/03
to
Juergen Ilse wrote:
> Hallo,
>
> Bodo Thiesen <bot...@gmx.de> wrote:
>
>>u...@spamtrap.musoftware.de (Ullrich von Bassewitz) wrote:
>>
>>>Bodo Thiesen <bot...@gmx.de> wrote:

> Das nuetzt dir nichts, solange free() selbst nicht reentrent ist (und
> das wird dir im allegemeinen eher nicht garantiert). Es ist moeglich
> (und sogar wahrscheinlich), dass free() an globalen Datenstrukturen
> des laufenden Prozesses herumdoktert, um die Speicherverwaltung zu
> aendern. Wenn free zum falschen Zeitpunkt unterbrochen wird, hat es
> einige der notwnedigen Aenderungen bereits durchgefuehrt, andere aber
> evt. noch nicht. Das zweite aufgerufene free() arbeitet dann (unab-
> haengig davon, ob es nun mit legalen oder "wilden" Pointerwerten auf-
> gerufen wurde) auf inkonsistenten Datenstrukturen, und nach Rueckkehr
> aus dem signal-handler macht das unterbrochene free() dort weiter, wo
> es aufgehoert hatte, ohne jedoch mitzubekommen, dass da ein weiterer
> Aufruf von free() an den Datenstrukturen herumgekodelt hat ...

Die Nichtreentranz ist sicherlich DAS Problem hier.

Andererseits kann man ein Programm schreiben, ganz ohne Handler
und ohne signal() zu verwenden. Jedoch werden malloc, realloc und free
verwendet.
In diesem Fall kann das Programm beliebig durch Signale unterbrochen
werden, auch wenn es gerade in free arbeitet.
Das Kreuz an einem Fenster anzuklicken löst ja z.B.
SIGTERM/SIGABORT/SIGINT aus.
Und das Programm wird dann von außen abgeräumt und beendet.
Es wird also von außen quasi ein exit() durchgeführt.

Ich meine, die Systeme sichern das schon alles irgendwie ab.
Beispiel: free() selbst suspendiert Signale...
(Es liegt ja eh kein REALtime-System vor...)
Das hat jetzt allerdings nichts mit dem Standard zu tun, der
UB nahezu grenzenlos freigibt.

Bodo Thiesen

unread,
Dec 8, 2003, 12:41:43 AM12/8/03
to
Philipp Janda <siff...@gmx.net> wrote:

> Bodo Thiesen schrieb:
>> Philipp Janda <siff...@gmx.net> wrote:
>>
>>> eigentlich noch ausgefuehrt werden koennte. Die einzigen wirklichen
>>> Probleme treten mit ungueltigen Werten fuer xyz (Trap-Repraesentationen)
>>> oder mit Ueberlaeufen (oder Trap-Repraesentationen) bei xyz+1 auf.
>>
>>
>> Und die Trap-Representations kann man mit unsigned char verhindern.
>
> Andererseits erhoeht man dadurch normalerweise die Wahrscheinlichkeit
> von Ueberlaeufen drastisch (im Vergleich zu einem int).

Wieso ist bei Dir die Gefahr eines Überlaufes bei unsigned char höher als
bei einem int?

> Die sauberste
> Variante (sofern man bei diesem Hack ueberhaupt von sauber sprechen
> kann) ist vermutlich mit sigatomic_t.

Wohl wahr.

Bodo Thiesen

unread,
Dec 8, 2003, 1:01:20 AM12/8/03
to
Juergen Ilse <jue...@usenet-verwaltung.de> wrote:

> Bodo Thiesen <bot...@gmx.de> wrote:
>
>> eine
>> Liste anlegen, in der ich den freizugebenden Speicherbereich eintrage (wobei
>> diese Liste natürlich nicht via malloc im Signal Handler angelegt werden
>> darf, der Speicher muß schon da sein).
>
>Das nuetzt dir nichts, solange free() selbst nicht reentrent ist (und
>das wird dir im allegemeinen eher nicht garantiert).

free() wird in diesem Szenario garnicht aus dem Signalhandler heraus
aufgerufen.

Bodo Thiesen

unread,
Dec 8, 2003, 1:01:22 AM12/8/03
to
u...@spamtrap.musoftware.de (Ullrich von Bassewitz) wrote:

> Bodo Thiesen <bot...@gmx.de> wrote:
>
> Zweitens kannst Du im Signalhandler nichts
> in so eine Liste eintragen oder auslesen, weil der Standard explizit sagt,

Wo?

> dass ein Signalhandler nur Variablen von Type sig_atomic_t beschreiben darf.

In der mir vorliegenden Version des Standards steht:

| 7.14 Signal handling <signal.h>
|
| 2 The type defined is
|
| sig_atomic_t
|
| which is the (possibly volatile-qualified) integer type of an
| object that can be accessed as an atomic entity, even in the
| presence of asynchronous interrupts.

>> Ich würde - nachdem
>> ich mir die Passage im Standard durchgelesen habe - einfach mal sig_atomic_t
>> benutzen, und hätte das ganze Problem überhaupt garnicht.
>
> Falsch. Der Standard sagt, dass Du in einem Signalhandler einer Variable vom
> Typ sig_atomic_t etwas *zuweisen* darfst. Er sagt nicht, dass Du sie
> inkrementieren darfst.

Wie gefordert: Wo?

>> (Ok, das mit dem
>> free() bleibt - das ist scheinbar erst mit POSIX möglich - das schwirrte mir
>> die ganze Zeit im Kopf rum, seit Helmut das hier zitiert hatte - aber es
>> interessiert ja nicht, schließlich sind wir hier nicht in der NG
>> de.comp.lang.posix ;-)
>
> Der Aufruf von free() aus einem Signalhandler heraus ist auch unter POSIX
> nicht moeglich. Der Abschnitt, den Helmut aus dem von mir genannten Dokument
> zitiert, sagt genau das. Wo Du gegenteilige Information her haben willst ist
> mir nicht klar.

Gruß, Bodo

Bodo Thiesen

unread,
Dec 8, 2003, 1:01:25 AM12/8/03
to
Helmut Schellong <r...@schellong.biz> wrote:

> Ullrich von Bassewitz wrote:
>> Bodo Thiesen <bot...@gmx.de> wrote:
>
> Außerdem gibt es doch Signale, wie jeder weiß.
> Und diese können ohnehin jedes Programm beliebig unterbrechen, auch wenn das
> Programm gerade in free oder malloc verweilt, und auch (oder gerade dann),
> wenn man im Programm überhaupt gar nichts mit signal() usw. programmiert hat.
> Irgendwas paßt hier doch nicht.

Ach ja: Wenn man ein SIGILL, SIGFPE oder SIGSEGV erhält, muß man das
Programm im signal handler beenden, da ein Rücksprung UB auslöst.
Andererseits muß man jeden via malloc() angeforderten Speicherblock
freigeben, da von der Implementation die automatische Freigabe bei einem
Exit nicht garantiert ist.

Claus Reibenstein

unread,
Dec 8, 2003, 1:48:54 AM12/8/03
to
Bodo Thiesen schrieb:

> Wieso ist bei Dir die Gefahr eines Überlaufes bei unsigned char höher als
> bei einem int?

Weil unsigned char kleiner sein kann als int.

Gruß. Claus

Ullrich von Bassewitz

unread,
Dec 8, 2003, 5:38:36 AM12/8/03
to
Bodo Thiesen <bot...@gmx.de> wrote:
> In der mir vorliegenden Version des Standards steht:
[...]
> Wie gefordert: Wo?

Warum liest Du den von Dir zitierten Abschnitt nicht ganz? :-)

ISO/IEC 9899:1999 (E) 7.14.1.1 "The signal function", Absatz 5:

If the signal occurs other than as the result of calling the abort or raise
function, the behavior is undefined if the signal handler refers to any
object with static storage duration other than by assigning a value to an
object declared as volatile sig_atomic_t, or the signal handler calls any
function in the standard library other than the abort function, the _Exit
function, or the signal function with the first argument equal to the signal
number corresponding to the signal that caused the invocation of the
handler. Furthermore, if such a call to the signal function results in a
SIG_ERR return, the value of errno is indeterminate.

Gruss


Uz


--
Ullrich von Bassewitz u...@spamtrap.musoftware.de

11:35:49 up 5 days, 16:58, 9 users, load average: 0.06, 0.03, 0.00

Philipp Janda

unread,
Dec 8, 2003, 7:03:20 AM12/8/03
to
Bodo Thiesen schrieb:
> Philipp Janda <siff...@gmx.net> wrote:
>
>
>>Bodo Thiesen schrieb:
>>
>>>Philipp Janda <siff...@gmx.net> wrote:
>>>
>>>
>>>>eigentlich noch ausgefuehrt werden koennte. Die einzigen wirklichen
>>>>Probleme treten mit ungueltigen Werten fuer xyz (Trap-Repraesentationen)
>>>>oder mit Ueberlaeufen (oder Trap-Repraesentationen) bei xyz+1 auf.
>>>
>>>
>>>Und die Trap-Representations kann man mit unsigned char verhindern.
>>
>>Andererseits erhoeht man dadurch normalerweise die Wahrscheinlichkeit
>>von Ueberlaeufen drastisch (im Vergleich zu einem int).
>
>
> Wieso ist bei Dir die Gefahr eines Überlaufes bei unsigned char höher als
> bei einem int?

Weil bei mir sizeof( int ) > sizeof( unsigned char) ist, und das sogar
recht drastisch.
Wenn man z.B. 8 Bit uchars und 32 Bit ints hat, dann hat man beim int
vereinfacht gerechnet
eine Ueberlaufwahrscheinlichkeit von 1/(2^32), beim unsigned char 1/256.
Oder willst Du etwa darauf hinaus, dass ein Ueberlauf bei unsigned
Datentypen eventuell nix ausmacht?! (Das muss ich mir erst nochmal genau
durch den Kopf gehen lassen...)

>
>
>>Die sauberste
>>Variante (sofern man bei diesem Hack ueberhaupt von sauber sprechen
>>kann) ist vermutlich mit sigatomic_t.
>
>
> Wohl wahr.
>
> Gruß, Bodo

Gruesse,
Philipp

Bodo Thiesen

unread,
Dec 11, 2003, 11:00:57 PM12/11/03
to
Claus Reibenstein <c.reib...@pop-hannover.de> wrote:

Ok, ja, moment, stopp. Es ging um ++flag, was nur ein "paar" mal aufgerufen
wird. Und für diesen Fall braucht man die Situation flag=255 normalerweise
nicht berücksichtigen, denn wenn Signalhandler 255 mal rekursiv aufgerufen
werden, läuft meiner Meinung nach irgendetwas ganz gehörig schief.

Aber natürlich ist überlaufwahrscheinlichkeit bei char größer als bei int,
ich hatte aber eher an den Überlauf von Byte1 nach Byte2 bei flag++ gedacht,
wodurch die ganze Aktion dann nicht mehr atomisch abläuft. Und die Gefahr
hielt ich halt bei char für geringer ...

Philipp Janda

unread,
Dec 12, 2003, 5:12:44 AM12/12/03
to
Bodo Thiesen schrieb:

> Claus Reibenstein <c.reib...@pop-hannover.de> wrote:
>
>
>>Bodo Thiesen schrieb:
>>
>>
>>>Wieso ist bei Dir die Gefahr eines Überlaufes bei unsigned char höher als
>>>bei einem int?
>>
>>Weil unsigned char kleiner sein kann als int.
>>
>>Gruß. Claus
>
>
> Ok, ja, moment, stopp. Es ging um ++flag, was nur ein "paar" mal aufgerufen
> wird. Und für diesen Fall braucht man die Situation flag=255 normalerweise
> nicht berücksichtigen, denn wenn Signalhandler 255 mal rekursiv aufgerufen
> werden, läuft meiner Meinung nach irgendetwas ganz gehörig schief.

Es geht nicht um "normales" Inkrementieren von flag, sondern um die
abnormen "halben" Schreibvorgaenge, die passieren koennten wenn ++flag
unguenstig unterbrochen wird. Ich bin davon ausgegangen, dass dann in
flag beliebiger (statistisch normalverteilter) Stuss drinstehen kann und
da hab ich bei char normalerweise groessere Chancen, dass da CHAR_MAX
drinsteht, als bei int mit INT_MAX.

>
> Aber natürlich ist überlaufwahrscheinlichkeit bei char größer als bei int,
> ich hatte aber eher an den Überlauf von Byte1 nach Byte2 bei flag++ gedacht,
> wodurch die ganze Aktion dann nicht mehr atomisch abläuft. Und die Gefahr
> hielt ich halt bei char für geringer ...

Vermutlich ist es in der Praxis wahrscheinlicher, dass char atomar
geschrieben wird, als dass int atomar geschrieben wird, insofern hast Du
vermutlich (auch) recht.

>
> Gruß, Bodo

Gruesse,
Philipp

Falk Hueffner

unread,
Dec 12, 2003, 5:36:11 AM12/12/03
to
Philipp Janda <siff...@gmx.net> writes:

> Vermutlich ist es in der Praxis wahrscheinlicher, dass char atomar
> geschrieben wird, als dass int atomar geschrieben wird,

Im Gegenteil. Fuer int wird typischerweise der "schnellste" Typ
genommen; hingegen haben einige Platformen (z. B. Alpha EV4), um das
Busdesign zu vereinfachen, keine Moeglichkeit, Bytes direkt zu
adressieren.

--
Falk

Jens Schweikhardt

unread,
Dec 12, 2003, 6:50:52 AM12/12/03
to
Philipp Janda <siff...@gmx.net> wrote
in <brc49j$1nck4$1...@uni-berlin.de>:
...
# Vermutlich ist es in der Praxis wahrscheinlicher, dass char atomar
# geschrieben wird, als dass int atomar geschrieben wird

Das stimmt nur für Hobbyisten-CPUs :-) Auf Big Iron CPUs (z.B. von Cray
u.a.) müssen einzelne char Zugriffe ausmaskiert werden und sind daher
aufwendiger.

Regards,

Jens
--
Jens Schweikhardt http://www.schweikhardt.net/
SIGSIG -- signature too long (core dumped)

0 new messages