Forum: PC-Programmierung C unerklärliches Programmverhalten


von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Ich wollte unechtes multitasking in c mithilfe eines Timers und einem 
Signalhandler unter Linux Implementieren, um es später auf einen uC mit 
Timerinterrupt zu portieren.

Aber das Programm tut etwas, das da einfach nicht steht:
1
    if(num_running_threads>=MAX_THREADS)
2
      goto add_thread_failture_maxthreads_exceeded;
3
    for(int current_thread=MAX_THREADS;current_thread--;){
4
      printf("create_thread %d %s\n", current_thread, threads[current_thread].active?"active":"inactive");
5
      if(!threads[current_thread].active)
6
        break;
7
    }
8
    num_running_threads++;
9
    printf("create_thread %d %d\n",num_running_threads, current_thread);

Führt irgendwie zur Ausgabe von:
1
save_current_thread 0
2
create_thread 7 inactive
3
create_thread 1 0
4
save_current_thread 0
5
continue_current_thread 0
6
save_current_thread 0
7
continue_current_thread 0
8
save_current_thread 0
9
continue_current_thread 0
10
save_current_thread 0

Und das verstehe ich nicht, diese Zeile:
1
create_thread 7 inactive
2
create_thread 1 0
Sollte so Aussehen:
1
create_thread 7 inactive
2
create_thread 1 7

Was könnte hier Passiert sein?

Komplettes Programm ist im Anhang.

Ausfgeführt hab ich das so:
1
gcc -std=c99 -Wall -Wextra -pedantic isr_timerbased_task_sheduler.c -o isr_timerbased_task_sheduler
2
./isr_timerbased_task_sheduler>log.txt&sleep 0.1;kill $!;head log.txt;
System:
1
Linux Server1-hp 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Compiler:
1
gcc (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
2
Copyright (C) 2014 Free Software Foundation, Inc.
3
This is free software; see the source for copying conditions.  There is NO
4
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

von Peter II (Gast)


Lesenswert?

was soll der mist mit den gotos?

von Daniel A. (daniel-a)


Lesenswert?

Peter II schrieb:
> was soll der mist mit den gotos?

Ich fand es schöner die Einzelnen Programmteile mit labeln zu 
beschriften, als grosse unübersichtliche verschachtelte if und switch 
strukturen zu verwenden. Die gotos erfüllen ihren zweck.

Ich hab diesen Fehler gerade gefunden:
1
for(int current_thread=MAX_THREADS;current_thread--;){
Muss natürlich so sein:
1
for(current_thread=MAX_THREADS;current_thread--;){

Jetzt bekomme ich aber einen SIGSEGV, hoffentlich sind setjmp und 
longjmp überhaupt auf diese weise Anwendbar...

von TriHexagon (Gast)


Lesenswert?

Daniel A. schrieb:
> Peter II schrieb:
>> was soll der mist mit den gotos?
>
> Ich fand es schöner die Einzelnen Programmteile mit labeln zu
> beschriften, als grosse unübersichtliche verschachtelte if und switch
> strukturen zu verwenden. Die gotos erfüllen ihren zweck.

Wenn du den Programmfluss verschleiern willst nur zu.

von radiostar (Gast)


Lesenswert?

TriHexagon schrieb:
> Wenn du den Programmfluss verschleiern willst nur zu.

Wer keine Ahnung vom Programmieren hat, der kann sich mit gotos 
natürlich übel seinen Code verunstalten. Es gibt aber Situationen, in 
dene gotos durchaus übersichtlicher sind, und dann ist es auch sinnvoll, 
diese einzusetzen. Mir ist nicht klar, was das mit verschleiern zu tun 
hat - im Gegenteil.

von Peter II (Gast)


Lesenswert?

radiostar schrieb:
> Es gibt aber Situationen, in
> dene gotos durchaus übersichtlicher sind,

da ist hier aber nicht der Fall. Der code ist damit sehr 
unübersichtlich.

wenn man Funktionen beschreiben will, dann verwenden man 
Prozeduren/Funktionen und nicht Labels.

von c-liker (Gast)


Lesenswert?

Peter II schrieb:
> wenn man Funktionen beschreiben will, dann verwenden man
> Prozeduren/Funktionen und nicht Labels.
und nicht zu vergessen Kommentare.

von TestTriHexagon (Gast)


Lesenswert?

radiostar schrieb:
> TriHexagon schrieb:
>> Wenn du den Programmfluss verschleiern willst nur zu.
>
> Wer keine Ahnung vom Programmieren hat, der kann sich mit gotos
> natürlich übel seinen Code verunstalten. Es gibt aber Situationen, in
> dene gotos durchaus übersichtlicher sind, und dann ist es auch sinnvoll,
> diese einzusetzen. Mir ist nicht klar, was das mit verschleiern zu tun
> hat - im Gegenteil.

Wenn man strukturiert in C programmiert, dann werden die Anweisungen 
standardmäßig von oben nach unten ausgeführt. Ausnahme sind höchstens 
Schleifen und Funktionen/Prozeduren. Bei der richtigen Einrückung hat 
man dann noch quasi visuell den Programmablauf im Auge und kann einzelne 
Abschnitte wie Schleifen einfach überspringen. Bei deinem gotos springst 
du mal nach unten, mal nach oben und man muss ständig nach Labels 
suchen. Das ist wie bei einem Buch in dem man sich ständig Verweise 
anschauen muss, was den Lesefluss absolut abträglich ist.

Es gibt Fälle in dem Gotos angebracht sind, doch hier sehe ich keine.

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

c-liker schrieb:
> Peter II schrieb:
>> wenn man Funktionen beschreiben will, dann verwenden man
>> Prozeduren/Funktionen und nicht Labels.
> und nicht zu vergessen Kommentare.

Ok, ich werde mir sowieso einen neuen Ansatz überlegen müssen, und dass 
Programm nochmal neu schreiben.

Es scheint folgendes zu passieren:
 1) main
 2) timer stackframe speichern
 3) funktion threadX aufrufen
 4) timer stackframe speichern
 5) timer stackframe nach main widerherstellen
 6) timer verlassen, zu main zurückkehren
 7) timer stackframe speichern
 8) timer stackframe nach timerX widerherstellen
    -> Zerstört stäck weil:
      * main
      * 1. stackframe timer <-- ist bereits zurückgekehrt, alles 
folgende existiert nichtmehr auf dem stack
      * timerX
      * 2. stackframe <-- widerherstellung zerstört den stack

Passt auch zur ausgabe von valgrind (im Anhang)

von Daniel A. (daniel-a)


Lesenswert?

TestTriHexagon schrieb:
> Bei deinem gotos springst
> du mal nach unten, mal nach oben

Dan hast du meinen Code nicht sehr genau Angeschaut, Da springe ich 
immer von oben nach unten, aber nie von unten nach oben.

von TriHexagon (Gast)


Lesenswert?

Daniel A. schrieb:
> TestTriHexagon schrieb:
>> Bei deinem gotos springst
>> du mal nach unten, mal nach oben
>
> Dan hast du meinen Code nicht sehr genau Angeschaut, Da springe ich
> immer von oben nach unten, aber nie von unten nach oben.

Mhm hab noch mal drüber geschaut, hätte schwören können das es einen 
Sprung nach oben gab. Na egal.

Wie Peter schon anmerkte sind Funktionen anstatt der Labels besser 
geeignet. Aber das musst du wissen.

von Bastler (Gast)


Lesenswert?

Longjmp restauriert doch auch den Stack-Pointer. Hab ich was übersehen, 
oder wo werden die n Stacks verwaltet?

von Daniel A. (daniel-a)


Lesenswert?

Bastler schrieb:
> Longjmp restauriert doch auch den Stack-Pointer. Hab ich was übersehen,
> oder wo werden die n Stacks verwaltet?

Nein, da hast du recht, deshalb zerstört der longjump ja den stack: die 
stackframes sind zu dem zeitpunkt nichtmehr da, darum ist der 
Stackpointer danach ja auch irgendwo im nirvana und der stack zerstört.

von Bastler (Gast)


Lesenswert?

Für preemptive braucht man je Task einen eigenen Stack. Und etwas ASM um 
ein setjump/longjump mit Stack-Umschaltung auf der jeweiligen Plattform 
zu implementieren. Und unter Linux könnte es sein, daß der Stack nicht 
beliebig auf dem Heap liegen darf, aber dazu könnte man die Task-Stacks 
als lokale Variablen in main() definieren. Ob man dabei was lernt, das 
auf dem μC weiterhilft? Who knows?

von Oliver S. (oliverso)


Lesenswert?

Um mal aufs ursprüngliche Problem zurückzukommen: Zähl mal nach, wie 
viele Variablen namens current_thread es in deinem Programm gibt...

Oliver

von Daniel A. (daniel-a)


Lesenswert?

Oliver S. schrieb:
> Um mal aufs ursprüngliche Problem zurückzukommen: Zähl mal nach, wie
> viele Variablen namens current_thread es in deinem Programm gibt...

Die überzälige lokale variable habe ich bereits bemerkt:

Daniel A. schrieb:
> Ich hab diesen Fehler gerade gefunden:
1
for(int current_thread=MAX_THREADS;current_thread--;){
> Muss natürlich so sein:
1
for(current_thread=MAX_THREADS;current_thread--;){

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.