diff --git a/20220620/bs-20220620.txt b/20220620/bs-20220620.txt new file mode 100644 index 0000000000000000000000000000000000000000..9d8e8d26c291dab75e8e05e3a01e9351e59d3de0 --- /dev/null +++ b/20220620/bs-20220620.txt @@ -0,0 +1,60 @@ +Macro zum Setzen eines Wertes (RP6-Bibliothek): + + #define setStopwatch1(__VALUE__) stopwatches.watch1 = __VALUE__ + +Benutzung: + + setStopwatch1 (0); + +Falsche Benutzung: + + setStopwatch1 (0) + 42; /* kein sinnvoller Funktionsaufruf */ + +expandiert zu + + stopwatches.watch1 = 0 + 42; + +--> Falsche Benutzung ist möglich. + + +Macro im Linux-Kernel: + + + #define spin_lock_mutex(lock, flags) \ + do \ + { \ + spin_lock (lock); \ + (void) (flags); \ + } \ + while (0) + +"do xxx while (0)" bedeutet: "mache xxx genau einaml". + +Benutzung: + + spin_lock_mutex (my_lock, my_flags); + +expandiert zu + + do + { + spin_lock (my_lock); + (void) (my_flags); + } + while (0); + +Falsche Benutzung: + + spin_lock_mutex (my_lock, my_flags) + 42; + + do + { + spin_lock (my_lock); + (void) (my_flags); + } + while (0) + 42; + +--> Falsche Benutzung ist nicht möglich. + +Fazit: "do xxx while (0)" ermöglicht die Verwendung des Macros +wie eine Funktion, ohne dadurch eine falsche Benutzung zu ermöglichen. diff --git a/20220620/sched.c b/20220620/sched.c new file mode 100644 index 0000000000000000000000000000000000000000..3cec73a4d18b4efd840affb3dae794617a71498f --- /dev/null +++ b/20220620/sched.c @@ -0,0 +1,171 @@ +struct task_struct *task[NR_TASKS]; + +/* + * 'schedule()' is the scheduler function. This is GOOD CODE! There + * probably won't be any reason to change this, as it should work well + * in all circumstances (ie gives IO-bound processes good response etc). + * The one thing you might take a look at is the signal-handler code here. + * + * NOTE!! Task 0 is the 'idle' task, which gets called when no other + * tasks can run. It can not be killed, and it cannot sleep. The 'state' + * information in task[0] is never used. + */ +void schedule(void) +{ + int i,next,c; + struct task_struct ** p; + +/* check alarm, wake up any interruptible tasks that have got a signal */ + + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) { + if ((*p)->alarm && (*p)->alarm < jiffies) { + (*p)->signal |= (1<<(SIGALRM-1)); + (*p)->alarm = 0; + } + if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE) + (*p)->state=TASK_RUNNING; + } + +/* this is the scheduler proper: */ + + while (1) { + c = -1; + next = 0; + i = NR_TASKS; + p = &task[NR_TASKS]; /* außerhalb des Arrays! */ + while (--i) { + if (!*--p) /* jetzt innerhalb des Arrays */ + continue; + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + } + if (c) break; + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) + (*p)->counter = ((*p)->counter >> 1) + + (*p)->priority; + } + switch_to(next); +} + +-------------------------------------------------------------------------------- + + while (--i) { + if (!*--p) /* jetzt innerhalb des Arrays */ + ; /* do nothing */ + else if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + } + +-------------------------------------------------------------------------------- + + + while (--i) { + if (*--p) /* jetzt innerhalb des Arrays */ + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + } + +-------------------------------------------------------------------------------- + +struct task_struct *task[NR_TASKS]; + +/* ... */ + + struct task_struct ** p; + +/* ... */ + + /* Das zweite if wird ausgeführt, wenn *p != NULL ist, + * wenn also der Zeiger-Zeiger p auf einen Zeiger != NULL zeigt. + */ + + c = -1; + next = 0; + i = NR_TASKS; + p = &task[NR_TASKS]; /* außerhalb des Arrays! */ + while (--i) { + if (!*--p) /* jetzt innerhalb des Arrays */ + continue; + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + } + + /* Diese Schleife ermittelt den größten Wert von (*p)->counter + * innerhalb des Arrays, setzt c auf diesen Counter und + * setzt next auf den Index, wo der Counter gefunden wurde. + * + * Das anschließende switch_to(next); gibt also demnigen Programm + * Rechenzeit, dessen (*p)->counter am größten ist. + */ + +-------------------------------------------------------------------------------- + + Wie funktioniert diese Zeile? + + c = (*p)->counter, next = i; + + Üblicher wäre: + + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) { + c = (*p)->counter; + next = i; + } + + Die Zeile enhält einen Komma-Operator: + + c = (*p)->counter, next = i; + + "Berechne den Ausdruck 'c = (*p)->counter', vergiß ihn wieder, + und berechne stattdessen den Ausdruck 'next = i'." + Das Ergebnis des Ausdrucks wird dann wieder verworfen. + Beide Teilausdrücke haben jedoch Seiteneffekte, nämlich die + Zuweisungen. + + --> "c = (*p)->counter, next = i;" führt in einem Befehl zwei + Zuweisungen aus. Dies funktioniert nur, weil beide Ausdrücke + denselben Typ (int) haben. + Wir sparen uns auf diese Weise ein Semikolon und ein Paar + geschweifter Klammern. (Jedoch keine Rechenzeit. Der erzeugte + Code ist identisch.) + +-------------------------------------------------------------------------------- + + if (c) break; /* c kann nur 0 sein, wenn alle counter 0 sind. */ + + /* Wenn alle counter 0 sind, verteile die Rechenzeit neu. */ + + for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p) + (*p)->counter = ((*p)->counter >> 1) + + (*p)->priority; + + Wenn kein Programm mehr einen (*p)->counter > 0 hat, + bekommen alle Programme neue Rechenzeit zugewiesen, + nämlich ihre (*p)->priority plus den verbleibenden counter >> 1. + + counter >> 1 ist dasselbe wie counter / 2. + + Wieso berücksichtigen wir den counter, wenn der doch = 0 ist? + + Wegen der zusätzlichen Bedingung mit TASK_RUNNING + + if ((*p)->state == TASK_RUNNING && (*p)->counter > c) + c = (*p)->counter, next = i; + + werden bei der Ermittlung von c nur solche Programme berücksichtigt, + deren status == TASK_RUNNING ist, also nur Programme, die tatsächlich + Rechenzeit haben wollen. Programme, die auf etwas warten, können einen + counter > 0 haben, ohne daß dadurch c > 0 würde. + (Beispiel: Programm, das auf einen Tastendruck wartet) + + Ergebnis: Bei der Zuteilung neuer Rechenzeit kann man Rechenzeit + "ansparen", aber nicht unbegrenzt (weil wir durch 2 dividieren). + Rechenzeit verfällt daher exponentiell, wächst gleichzeitig linear, + bleibt daher endlich. + + Gesamtergebnis: Interaktive Programme können, während sie auf einen + Tastendruck warten, Rechenzeit "ansparen", die sie dann zur Verfügung + haben, sobald der Tastendruck kommt. +