Skip to content
Snippets Groups Projects
Commit 9cd23492 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Notizen und Beispiel 20.6.2022

parent 68bc4bfd
No related branches found
No related tags found
No related merge requests found
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.
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.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment