Bläddra i källkod

Merge pull request #5 from themultiplexer/hw4

hw4
Manuel Vögele 8 år sedan
förälder
incheckning
82d5d166d7
Inget konto är kopplat till bidragsgivarens mejladress

+ 1
- 1
.gitignore Visa fil

@@ -1,6 +1,6 @@
1 1
 # Generated by Cargo
2 2
 # will have compiled files and executables
3
-/target/
3
+*/target/
4 4
 
5 5
 # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 6
 # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock

+ 39
- 0
hw4/simu1/ANSWERS.md Visa fil

@@ -0,0 +1,39 @@
1
+# hw4 - Simulation 1 - Lösungen
2
+
3
+### Warmup
4
+
5
+`0x611c` =>
6
+
7
+`0110 0001 0001 1100` =>
8
+
9
+| Valid | PDE | PTE | Offset |
10
+|-------|-----|-----|--------|
11
+|0      |11000|01000|11100   |
12
+| False | 24  | 8   | 28     |
13
+
14
+PDE at index 24:
15
+
16
+> page 108: 83 [...] e9 **a1** e8 [...] ff
17
+
18
+
19
+
20
+`0xa1` => `1010 0001` => `1 | 0100001` => Valid | 33
21
+
22
+PTE at index 8 in page 33 (found in PDE):
23
+
24
+> page 33: 7f [...] 7f **b5** 7f [...] 7f
25
+
26
+
27
+`0xb5` => `1011 0101` => `1 | 0110101` => Valid | 53
28
+
29
+Final Value is in page `0xb5` (53) with offset 28 => 0x08 (8).
30
+
31
+
32
+
33
+### Aufgaben
34
+
35
+1. Für eine zweistufige Seitentabelle benötigt man ein Register, für eine dreistufige Seitentabelle bräuchte man ein zweites PDBR.
36
+
37
+2. Da es sich bei dieser Simulation um eine zweistufige Seitentabelle handelt, gibt es eine Referenz für den PDE, eine für den PTE und schlussendlich noch eine dritte Referenz, in der das Endergebnis steht.
38
+
39
+3. Da der Cache nicht im Voraus weiß, auf welche Adresse nach der Übersetzung des PDE bzw. PTE zugegriffen wird, müssen danach erst wieder Daten in den Cache geladen werden. Dies führt zu einer Reihe von Cache Misses.

+ 31
- 0
hw4/simu2/ANSWERS.md Visa fil

@@ -0,0 +1,31 @@
1
+
2
+# Simulation 2 - Antworten
3
+
4
+Diese Simulation wurde auf einem Laptop mit 8GB RAM, einer 8GB swapfile und 4 Prozessorkernen ausgeführt.
5
+
6
+
7
+1. Wenn man *mem* mit auch nur mit 1 MB Speicher aufruft, dann geht die CPU Auslastung eines Kerns auf 100%. Im Idle Zustand war die user-time bei ~1%. Bei nur einem laufenden mem-Programm betrug die user-time ~25%. Bei 3 laufenden Instanzen waren es ~80% und bei 4 ~99% user-time.
8
+
9
+   Das klingt absolut logisch, da der Prozessor des Systems 4 Kerne hat. Denn wenn auf jedem Kern ein mem-Programm läuft, dann hat das Bertriessystem keinen eigenen Kern und die prozentuale sys-time wird winzig.
10
+
11
+2. Direkt nach dem Ausführen von ./mem 1024 hat sich der freie Speicher um ~1.050.000 Byte verringert und die Spalte swpd hat sich nicht verändert, sondern blieb auf 0. Sobald wir das mem-Programm (mit Ctrl-C) beendet haben, ist der freie Speicher (free) fast wieder auf den Urprungswert gesprungen.
12
+
13
+   Es fehlten ~250 Byte. Der freie Speicher hat sich danach also verkleinert. Auf der Workstation wurde bereits bei 1024 Byte *geswapped* und der freie Speicher war nach dem Beenden des Programms größer.
14
+
15
+3. Bei ./mem 4000 gab es auf dem Rechner (mit 8GB RAM) absolut kein swap-in / swap-out. Es sei denn, der freie Speicher von den gesamten 8GB war kleiner als die ~4GB. Unter normaler Last war erst bei ./mem 7000 ein deutlicher swap-in und -out sichtbar.
16
+
17
+   Bei jedem der Werte war jedoch der erste *loop* immer langsamer. Durschnittlich war er oft ~50% langsamer. Im ersten Loop werden 6x durchschnittlich 244.000 Byte in den Swap geschrieben (swap-out). Das ergibt die ~1.700.000 bei (swpd). Es werden bei ./mem 7000 ~5.400.000 Bytes in den physikalischen Speicher ausgelagert und insgesamt ~1.700.000 in den Swap. Das sind zusammen ungefähr die angeforderten 7 GB.
18
+   
19
+   In den folgenden Loops fand kaum noch ein swap-out statt (durchschnittlich ~0 Byte), dafür aber Rückschrieb von Swap in den RAM (swap-in).
20
+
21
+4. Die CPU Auslastung durch das mem-Programm ist immens. Wenn man es auf dem Laptop ausführt, drehen direkt die Lüfter hoch. Doch wie zu erwarten und im Quellcode zu sehen, ist das C-Programm nicht *multi-threaded*. In einem beliebigen System Monitor (z.B. htop) sieht man, dass nur ein Kern ausgelastet ist.
22
+
23
+   Im ersten Loop entsprechen die Werte von **swap-out** ungefähr den den Werten von **block-out**, aber es finden auch **block-in**s im ähnlichen Werteberich statt.
24
+
25
+5. Bei 4000 Byte braucht der erste loop 1070 ms und alle restlichen 745ms
26
+
27
+   Da bereits beim Aufruf von ./mem 8192 ein Segmentation Fault auftritt, war es nicht möglich über die Grenze des physikalischen 8GB Speichers (8192 kByte) zu gehen. Der Rest dieser Aufgabe ist also nicht wirklich beantwortbar.
28
+
29
+   Bei ./mem 8191 muss allerdings auch schon massiv *geswapped* werden. Der erste Loop braucht 6 Sekunden und die folgenden mehr als 60 Sekunden. Die Bandbreite in Loop 0 war noch bei 1317 MB/s in Loop 1 schon 128 MB/s und in den folgenden Loops ~110 MB/s. Man merkt dass nun die Festplatte der *bottleneck* ist, da das Programm kaum noch CPU Leistung benötigt.
30
+
31
+6. Bei 14584 schlägt die Speicheralloziierung fehl.

+ 18
- 0
hw4/simu3/ANSWERS.md Visa fil

@@ -0,0 +1,18 @@
1
+# hw4 - Simulation 3 - Antworten
2
+
3
+1.
4
+    1. FIFO: Da immer das älteste Element aus dem Cache geworfen wird, verursacht `1,2,3,4,5,6,1,2,...` ausschließlich Cache-Misses.
5
+    2. LRU: Nun wird immer das am längsten nicht getroffene Element aus dem Cache geworfen. Die Strategie bei FIFO funktioniert auch hier.
6
+    3. MRU: Hier funktioniert die bisher verwendete Folge nicht mehr. Da immer das zuletzt verwendete Element aus dem Cache entfernt wird, ist `1,2,3,4,5,6,5,6,...` eine Möglichkeit, um ausschließlich Cache-Misses zu provozieren. Die Folge `1-6` dient dabei dazu, den Cache zuerst aufzufüllen.
7
+    4. Für unseren Fall (eine Abfolge von 6 verschiedenen Zugriffen) braucht der Cache eine minimale Größe von 6, um sehr viel mehr Cache-Hits zu erreichen. Um genau zu sein, sind dann immer alle Einträge im Cache vorhanden, und nach der anfänglichen Auffüllphase (in der nur Misses auftreten) ist jeder Zugriff ein Hit.
8
+
9
+2. Für unser Programm, siehe `randomtrace.c`.
10
+    1. Wir erwarten, dass sich die verschiedenen Policies entsprechend dem, wie wir es in der Vorlesung besprochen hatten, verhalten.
11
+
12
+3. *Traces* mit *locality*:
13
+
14
+    i. Um räumliche Lokalität (spatial locality) herzustellen darf man die TLB Abdeckung nicht überschreiten bzw. müssen Zahlen gewählt werden, die in der Nähe von einander liegen. Zusätzlich kann man zeitliche Lokalität erzeugen, in dem sich Zahlen wiederholen (temporal locality). Das bedeutet 1,2,2,3,2,3,2,3,2,3,2,2,2,2 ist von räumlicher und zeitlicher Lokalität.
15
+
16
+    ii. LRU hatte beim Auruf mit solchen traces eine Hitrate von ca. 80%
17
+
18
+    iii. Mit der Policy RAND gab es trotzdem immer die selbe Hitrate wie mit LRU...

+ 25
- 0
hw4/simu3/randomtrace.c Visa fil

@@ -0,0 +1,25 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <time.h>
4
+
5
+int main(int argc, char* argv[]) {
6
+  srand(time(NULL));
7
+
8
+  if (argc != 3) {
9
+    fprintf(stderr, "Usage: ./randomtrace <count> <size>\n");
10
+    return -1;
11
+  }
12
+
13
+  int i, r, s, t;
14
+  s = atoi(argv[1]);
15
+  t = atoi(argv[2]);
16
+  for (i = 0; i < s; i++) {
17
+    r = (int) (rand() % t);
18
+    if (i != s - 1) {
19
+      printf("%d,", r);
20
+    } else {
21
+      printf("%d", r);
22
+    }
23
+  }
24
+  printf("\n");
25
+}

+ 8
- 0
hw4/task1/Cargo.toml Visa fil

@@ -0,0 +1,8 @@
1
+[package]
2
+name = "task1"
3
+version = "0.1.0"
4
+authors = ["Lorenz Bung & Joshua Rutschmann"]
5
+
6
+[dependencies]
7
+procinfo = "^0.4.2"
8
+libc = "^0.2"

+ 95
- 0
hw4/task1/src/main.rs Visa fil

@@ -0,0 +1,95 @@
1
+extern crate procinfo;
2
+
3
+use std::env;
4
+use std::process;
5
+
6
+mod readproc;
7
+mod pstree;
8
+
9
+mod unit_test_pstree;
10
+mod unit_test_readproc;
11
+
12
+/// Mainfunction
13
+fn main() {
14
+
15
+    let args: Vec<String> = env::args().collect();
16
+
17
+    match args.len() {
18
+        1 => {
19
+            if let Ok(pid_tuple) = readproc::self_pids() {
20
+                let pid = pid_tuple.0;
21
+                let ppid = pid_tuple.1;
22
+
23
+                // Commands & Threads for PID
24
+                if let Ok(pid_command) = readproc::get_pid_command(pid) {
25
+                    if let Ok(pid_threads) = readproc::get_thread_count(pid) {
26
+                        println!(
27
+                            "My PID : {} - {} running {} threads",
28
+                            pid,
29
+                            pid_command,
30
+                            pid_threads
31
+                        );
32
+                    }
33
+                }
34
+
35
+                // Commands & Threads for Parent-PID
36
+                if let Ok(ppid_command) = readproc::get_pid_command(ppid) {
37
+                    if let Ok(ppid_threads) = readproc::get_thread_count(ppid) {
38
+                        println!(
39
+                            "My PPID: {} - {} running {} threads",
40
+                            ppid,
41
+                            ppid_command,
42
+                            ppid_threads
43
+                        );
44
+                    }
45
+                }
46
+
47
+            }
48
+
49
+
50
+            if let Ok(size_tuple) = readproc::get_ownprocess_mem() {
51
+                // Memory
52
+                let vspace = size_tuple.0;
53
+                let code = size_tuple.1;
54
+                let data = size_tuple.2;
55
+
56
+                println!(
57
+                    "My mem : {} (vspace), {} (code), {} (data)",
58
+                    vspace,
59
+                    code,
60
+                    data
61
+                );
62
+            }
63
+
64
+            if let Ok(last_command) = readproc::get_last_created_command() {
65
+                // Last Process
66
+                println!("Last process created in system was: {}", last_command);
67
+            }
68
+
69
+
70
+            if let Ok(task_total) = readproc::get_task_total() {
71
+                // Number of tasks
72
+                println!("Total number of tasks: {}", task_total);
73
+            }
74
+        }
75
+
76
+        2 => {
77
+            match args[1].parse::<i32>() {
78
+                Ok(pid) => {
79
+                    if !pstree::print(pid) {
80
+                        process::exit(1);
81
+                    }
82
+                }
83
+                Err(_) => {
84
+                    println!("Error while parsing PID");
85
+                    process::exit(1);
86
+                }
87
+            }
88
+        }
89
+
90
+        _ => {
91
+            println!("Correct usage: no param or param PID");
92
+            process::exit(1);
93
+        }
94
+    }
95
+}

+ 95
- 0
hw4/task1/src/pstree.rs Visa fil

@@ -0,0 +1,95 @@
1
+extern crate libc;
2
+
3
+use procinfo::pid;
4
+use self::libc::pid_t;
5
+
6
+/// Datenstruktur für einen Prozess.
7
+pub struct Process {
8
+    name: String,
9
+    pid: pid_t,
10
+    ppid: pid_t,
11
+}
12
+
13
+impl Process {
14
+    /// Erstellt eine Prozess-Datenstruktur aus procinfo::Stat.
15
+    pub fn new(with_pid: pid_t) -> Self {
16
+        if let Ok(stat) = pid::stat(with_pid) {
17
+            Process {
18
+                name: stat.command,
19
+                pid: stat.pid,
20
+                ppid: stat.ppid,
21
+            }
22
+        } else {
23
+            panic!("Internal Error: Process not found")
24
+        }
25
+    }
26
+
27
+    /// Erstellt eine Prozess-Datenstruktur aus procinfo::Stat.
28
+    pub fn me() -> Self {
29
+        if let Ok(my_pid) = pid::stat_self() {
30
+            Process::new(my_pid.pid)
31
+        } else {
32
+            panic!("Internal Error: I don't have a PID but I am running.")
33
+        }
34
+    }
35
+
36
+    /// Prüft ob das Prozess-Struct ein Elternprozess besitzt.
37
+    pub fn has_parent(&self) -> bool {
38
+        self.ppid != 0
39
+    }
40
+
41
+    /// Gibt den Elternprozess zurück.
42
+    pub fn parent(&self) -> Self {
43
+        Process::new(self.ppid)
44
+    }
45
+
46
+    /// Prüft ob das Prozess-Struct einen (entfernten) Elternprozess mit dem übergebenen pid hat.
47
+    pub fn has_parent_with_pid(&self, pid: pid_t) -> bool {
48
+        if self.pid == pid {
49
+            return true;
50
+        }
51
+
52
+        if self.has_parent() {
53
+            return self.parent().has_parent_with_pid(pid);
54
+        }
55
+
56
+        false
57
+    }
58
+
59
+    /// Gibt über Rekursion über die Eltern eine Prozesskette aus.
60
+    pub fn print_recursive(&self, to_pid: pid_t, output: &mut String) {
61
+
62
+        if output.len() == 0 {
63
+            *output = format!("{}({}){}", self.name, self.pid, output);
64
+        } else {
65
+            *output = format!("{}({})---{}", self.name, self.pid, output);
66
+        }
67
+
68
+        if self.has_parent() && self.pid != to_pid {
69
+            self.parent().print_recursive(to_pid, output);
70
+        }
71
+    }
72
+}
73
+
74
+/// Geht von eigenem Prozess aus und gibt die Prozesskette bis zum übergebenem PID aus
75
+/// und fängt mögliche Fehler ab.
76
+pub fn print(pid: pid_t) -> bool {
77
+
78
+    if let Err(_) = pid::stat(pid) {
79
+        println!("Invalid PID");
80
+        return false;
81
+    }
82
+
83
+    let my_proc = Process::me();
84
+
85
+    if !my_proc.has_parent_with_pid(pid) {
86
+        println!("This Process has no parent {}", pid);
87
+        return false;
88
+    }
89
+
90
+    let mut output = String::new();
91
+    my_proc.print_recursive(pid, &mut output);
92
+    println!("{}", output);
93
+
94
+    true
95
+}

+ 66
- 0
hw4/task1/src/readproc.rs Visa fil

@@ -0,0 +1,66 @@
1
+use procinfo::pid;
2
+use procinfo::loadavg;
3
+
4
+/// Returns the PID and PPID of the current process.
5
+/// Throws an error if the current process doesn't exist (should never occur).
6
+pub fn self_pids() -> Result<(i32, i32), &'static str> {
7
+    match pid::stat_self() {
8
+        Ok(stat) => Ok((stat.pid, stat.ppid)),
9
+        Err(_) => Err("PID not alive: PID and PPID not found"),
10
+    }
11
+}
12
+
13
+/// Returns the command (string) belonging to the given PID.
14
+/// Throws an error if the given PID doesn't exist.
15
+pub fn get_pid_command(pid: i32) -> Result<String, &'static str> {
16
+    match pid::stat(pid) {
17
+        Ok(stat) => Ok(stat.command),
18
+        Err(_) => Err("PID not alive: no command name found"),
19
+    }
20
+}
21
+
22
+/// Returns the last created command (string) of the system.
23
+/// Throws an error if there is no last Command.
24
+pub fn get_last_created_command() -> Result<String, &'static str> {
25
+    match loadavg() {
26
+        Ok(stat) => {
27
+            let last_pid = stat.last_created_pid;
28
+            match pid::stat(last_pid) {
29
+                Ok(st) => Ok(st.command),
30
+                Err(_) => Err("No last command via PID found"),
31
+            }
32
+        }
33
+        Err(_) => Err("No last command found"),
34
+    }
35
+}
36
+
37
+/// Returns the number of threads belonging to the given PID.
38
+/// Throws an error if the given PID doesn't exist.
39
+pub fn get_thread_count(pid: i32) -> Result<u32, &'static str> {
40
+    match pid::stat(pid) {
41
+        Ok(stat) => Ok(stat.num_threads as u32),
42
+        Err(_) => Err("PID not alive: no threads counted"),
43
+    }
44
+}
45
+
46
+/// Returns the number of total tasks running in the system.
47
+/// Throws an error if the total number of tasks doesn't exist.
48
+pub fn get_task_total() -> Result<u32, &'static str> {
49
+    match loadavg() {
50
+        Ok(stat) => Ok(stat.tasks_total),
51
+        Err(_) => Err("No total count of tasks in system found"),
52
+    }
53
+}
54
+
55
+/// Returns the size of the virtual, code and data memory size of the current process.
56
+/// Throws an error if the current process doesn't exist (should never occur).
57
+pub fn get_ownprocess_mem() -> Result<(usize, usize, usize), &'static str> {
58
+    match pid::stat_self() {
59
+        Ok(stat) => {
60
+            let csize = stat.end_code - stat.start_code;
61
+            let dsize = stat.end_data - stat.start_data;
62
+            Ok((stat.vsize, csize, dsize))
63
+        }
64
+        Err(_) => Err("PID not alive: no memory found"),
65
+    }
66
+}

+ 57
- 0
hw4/task1/src/unit_test_pstree.rs Visa fil

@@ -0,0 +1,57 @@
1
+#[cfg(test)]
2
+mod tests {
3
+    use pstree::Process;
4
+    use pstree::print;
5
+
6
+    #[test]
7
+    #[should_panic]
8
+    fn new_invalid_pid() {
9
+        Process::new(-1000);
10
+    }
11
+
12
+    #[test]
13
+    fn new_valid_pid() {
14
+        Process::new(1);
15
+    }
16
+
17
+    #[test]
18
+    fn hasparent_no() {
19
+        assert_eq!(false, Process::has_parent(&Process::new(1)));
20
+    }
21
+
22
+    #[test]
23
+    fn hasparent_yes() {
24
+        assert_eq!(true, Process::has_parent(&Process::me()));
25
+    }
26
+
27
+    #[test]
28
+    #[should_panic]
29
+    fn parent_not_existing() {
30
+        Process::parent(&Process::new(1));
31
+    }
32
+
33
+    #[test]
34
+    fn parent_existing() {
35
+        Process::parent(&Process::me());
36
+    }
37
+
38
+    #[test]
39
+    fn hasparent_with_pid_not_existing() {
40
+        assert_eq!(false, Process::has_parent_with_pid(&Process::me(), -1000));
41
+    }
42
+
43
+    #[test]
44
+    fn hasparent_with_pid_existing() {
45
+        assert_eq!(true, Process::me().has_parent_with_pid(1));
46
+    }
47
+
48
+    #[test]
49
+    fn print_not_existing() {
50
+        assert_eq!(false, print(-1000));
51
+    }
52
+
53
+    #[test]
54
+    fn print_existing() {
55
+        assert_eq!(true, print(1));
56
+    }
57
+}

+ 2
- 2
hw4/task1/src/unit_test_readproc.rs Visa fil

@@ -1,8 +1,8 @@
1 1
 #[cfg(test)]
2 2
 mod tests {
3 3
     use procinfo::pid::{status, status_self};
4
-    use {get_ownprocess_mem, get_pid_command, get_task_total, get_thread_count, self_pids};
5
-
4
+    use readproc::{get_ownprocess_mem, get_pid_command, get_task_total, get_thread_count,
5
+                   self_pids};
6 6
 
7 7
     fn sol_self_pids() -> (i32, i32) {
8 8
         match status_self() {

+ 0
- 376
hw4/task2/README.md Visa fil

@@ -1,376 +0,0 @@
1
-# Homework hw4 task2
2
-
3
-Die folgenden Informationen sollen helfen, sich schneller in die Materie des
4
-'Timings' von Programmen einzuarbeiten:
5
-
6
-- Das externe Crate [time][] bietet mehr und vor allem einfachere Funktionalität
7
-  als die Rust Standard-Bibliothek.
8
-- Alle Betriebssysteme stellen eine Time API zu Verfügung. Es kann hilfreich
9
-  sein zu verstehen, wie diese funktioniert. Daher beschäftigt sich das nächste
10
-  Kapitel ausführlich mit dieser API.
11
-- Das Modul [std::mem] aus der Standardbibliothek ist zur Lösung dieser Aufgabe
12
-  sehr hilfreich.
13
-
14
-[time]: https://docs.rs/time
15
-[std::mem]: https://doc.rust-lang.org/std/mem/
16
-
17
-## Warmup für das Programm
18
-
19
-Um mit hoher Wahrscheinlichkeit Instruktionen und Daten aus dem Cache zu beziehen, sollte Ihr Programm ein kleines 'Cache' Warmup durchlaufen.
20
-
21
-Dies kann über ein simples Inkrementieren erreicht werden. Bei der Berechnung müssen Sie aber darauf achten, dass der Compiler das Ergebnis nicht vorausberechnen kann. Sonst würde bei der Optimierung der Compiler einfach das Ergebnis ausrechnen und das Ergebnis direkt in den Code schreiben. Damit wäre das Warmup (Durchlaufen des Codes) dahin.
22
-
23
-Mit einer Parameterübergabe durch die Kommandozeile steht das Ergebnis zur Compile-Zeit nicht fest und somit kann der Code nicht einfach 'weg'-optimiert werden. Dazu ein Beispiel:
24
-
25
-```Rust
26
-//where page_count and jump are cmd arguments
27
-let mut a = vec![0i32; page_count * jump];
28
-
29
-    // warm up
30
-    for i in a.iter_mut() {
31
-        *i += 1;
32
-    }
33
-```
34
-
35
-## Zeiten lesen in C
36
-
37
-Das folgende Kapitel muss zur Lösung von task2 nicht komplett verstanden werden.
38
-Vielmehr soll es weitere Informationen liefern, wenn Ihnen gewisse
39
-Funktionalitäten der Thematik 'Timing' unklar sind.
40
-
41
-- [Datenstrukturen](#datenstrukturen)
42
-- [Zeit lesen](#zeit-lesen)
43
-- [Zeitvergleich: Differenzzeitmessung](#zeitvergleich-differenzzeitmessung)
44
-
45
-### Datenstrukturen
46
-
47
-Betriebssysteme stellen Anwendungen Zeitgeber mit unterschiedlichen
48
-Eigenschaften zur Verfügung, über die das Zeitverhalten kontrolliert wird. Diese
49
-sind gekennzeichnet durch ihre
50
-
51
-- Genauigkeit,
52
-- die Zuverlässigkeit,
53
-- den Bezugspunkt,
54
-- die Darstellung und
55
-- den maximalen Zeitbereich.
56
-
57
-Das Betriebssystem repräsentiert Zeiten unter anderem mit den folgenden
58
-Datentypen (Darstellung):
59
-
60
-- clock_t: Timerticks.
61
-- struct timeval: Zeit in Mikrosekunden-Auflösung.
62
-- struct timespec: Zeit in Nanosekunden-Auflösung.
63
-- struct tm: absolute Zeitangabe.
64
-
65
-```c
66
-struct timeval {
67
-
68
-    time_t      tv_sec;     /* seconds */
69
-    suseconds_t tv_usec;    /* microseconds */
70
-};
71
-
72
-struct timespec {
73
-    time_t tv_sec; /* seconds */
74
-    long tv_nsec; /* nanoseconds */
75
-};
76
-
77
-struct tm
78
-{
79
-    int tm_sec; /* seconds */
80
-    int tm_min; /* minutes */
81
-    int tm_hour; /* hours */
82
-    int tm_mday; /* day of the month */
83
-    int tm_mon; /* month */
84
-    int tm_year; /* year */
85
-    int tm_wday; /* day of the week */
86
-    int tm_yday; /* day in the year */
87
-    int tm_isdst; /* daylight saving time */
88
-};
89
-```
90
-
91
-Die Strukturen struct timeval und struct timespec bestehen aus jeweils zwei
92
-Variablen, die einmal den Sekundenanteil und einmal den Mikro- beziehungsweise
93
-den Nanosekundenanteil repräsentieren. Die Darstellung erfolgt jeweils normiert.
94
-Das bedeutet, dass der Mikro- oder Nanosekundenanteil immer kleiner als eine
95
-volle Sekunde bleibt. Ein Zeitstempel von beispielsweise 1 Sekunde, 123456
96
-Mikrosekunden ist gültig, 1 Sekunde, 1234567 Mikrosekunden ist ungültig. In
97
-normierter Darstellung ergäbe sich 2 Sekunden, 234567 Mikrosekunden.
98
-
99
-Die Darstellungs- beziehungsweise Repräsentationsform reflektiert auch den
100
-darstellbaren Wertebereich. Da bei den dargestellten Datenstrukturen für den Typ
101
-time\_t ein long eingesetzt wird, lassen sich auf einer 32-Bit Maschine rund 4
102
-Milliarden Sekunden zählen, auf einer 64-Bit Maschine 2\^64 (mehr als 500
103
-Milliarden Jahre).
104
-
105
-Als Bezugspunkte haben sich die folgenden eingebürgert: *Start des Systems,
106
-*Start eines Jobs und \*Start einer Epoche, beispielsweise "Christi Geburt" oder
107
-der 1.1.1970 (Unix-Epoche). Dieser Bezugspunkt weist zudem noch eine örtliche
108
-Komponente auf: Der Zeitpunkt 19:00 Uhr in Europa entspricht beispielsweise
109
-einem anderen Zeitpunkt in den USA (minus sechs Stunden zur Ostküste).
110
-
111
-Die Genauigkeit wird beeinflußt durch, die Taktung des Zeitgebers, deren
112
-Schwankungen und durch Zeitsprünge.
113
-
114
-Das Attribut Zuverlässigkeit eines Zeitgebers beschreibt dessen Verhalten bei
115
-(bewußten) Schwankungen der Taktung und bei Zeitsprüngen: Ein Zeitgeber kann
116
-beispielsweise der Systemuhr folgen (CLOCK\_REALTIME) oder unabhängig von
117
-jeglicher Modifikation an der Systemzeit einfach weiterzählen
118
-(CLOCK\_MONOTONIC). Die Posix-Realzeiterweiterung beziehungsweise Linux-Systeme
119
-definieren hierzu folgende Clocks [Man-Page zu
120
-clock\_gettime()](http://linux.die.net/man/3/clock_gettime):
121
-
122
-- **CLOCK\_REALTIME**: Dieser Zeitgeber repräsentiert die systemweite, aktuelle
123
-  Zeit. Er reagiert auf Zeitsprünge, sowohl vorwärts als auch rückwärts, die
124
-  beispielsweise beim Aufwachen (Resume) nach einem Suspend (Schlafzustand des
125
-  gesamten Systems) ausgelöst werden. Er reagiert ebenfalls auf unterschiedliche
126
-  Taktungen, die beispielsweise durch NTP erfolgen. Dieser Zeitgeber liefert die
127
-  Sekunden und Nanosekunden seit dem 1.1. 1970 UTC (Unixzeit) zurück.
128
-- **CLOCK\_MONOTONIC**: Dieser Zeitgeber läuft entsprechend seiner Auflösung
129
-  stets vorwärts, ohne dabei Zeitsprünge zu vollziehen. Er ist also unabhängig
130
-  von der mit Superuserprivilegien zu verändernden Systemuhr. Allerdings
131
-  reagiert dieser Zeitgeber auf Modifikationen der Taktung, die beispielsweise
132
-  durch NTP erfolgen.
133
-- **CLOCK\_MONOTONIC_RAW**: Dieser Zeitgeber ist linuxspezifisch. Er reagiert
134
-  weder auf Zeitsprünge noch auf in Betrieb geänderte Taktungen (NTP).
135
-- **CLOCK\_PROCESS\_CPUTIME_ID**: Dieser Zeitgeber erfasst die Verarbeitungszeit
136
-  (Execution Time) des zugehörigen Prozesses. Das funktioniert aber nur
137
-  zuverlässig auf Single-Core-Systemen beziehungsweise wenn sichergestellt
138
-  werden kann, dass keine Prozessmigration stattfindet.
139
-- **CLOCK\_THREAD\_CPUTIME\_ID**: Dieser Zeitgeber erfasst die Verarbeitungszeit
140
-  (Execution Time) des zugehörigen Threads. Das funktioniert aber nur
141
-  zuverlässig auf Single-Core-Systemen beziehungsweise wenn sichergestellt
142
-  werden kann, dass keine Prozessmigration stattfindet.
143
-
144
-Der maximale Zeitbereich schließlich ergibt sich durch die Auflösung des
145
-Zeitgebers und die Bitbreite der Variablen:
146
-
147
-```text
148
-zeitbereich = auflösung * 2^bitbreite
149
-```
150
-
151
-### Zeit lesen
152
-
153
-Es gibt unterschiedliche Systemfunktionen, mit denen die aktuelle Zeit gelesen
154
-werden kann. Favorisiert ist die Funktion *int clock\_gettime(clockid\_t
155
-clk\_id, struct timespec * tp)*, die die Zeit seit dem 1.1.1970 (Unixzeit) als
156
-Universal Time (Zeitzone UTC) zurückliefert (struct timespec). Konnte die
157
-aktuelle Zeit gelesen werden, gibt die Funktion Null, ansonsten einen Fehlercode
158
-zurück. Allerdings ist das Auslesen auf 32-Bit Systemen in so fern
159
-problematisch, da der 32-Bit Zähler am 19. Januar 2038 überläuft.
160
-
161
-```c
162
-struct timespec {
163
-        time_t   tv_sec;        /* seconds */
164
-        long     tv_nsec;       /* nanoseconds */
165
-};
166
-
167
-```
168
-
169
-```c
170
-struct timespec timestamp;
171
-    ...
172
-    if (clock_gettime(CLOCK_MONOTONIC,&timestamp))
173
-    {
174
-        perror("timestamp");
175
-        return -1;
176
-    }
177
-    printf("seconds: %ld, nanoseconds: %ld\n",
178
-        timestamp.tv\_sec, timestamp.tv\_nsec);
179
-```
180
-
181
-Durch die Wahl der Clock CLOCK\_PROCESS\_CPUTIME\_ID beziehungsweise
182
-CLOCK\_THREAD\_CPUTIME\_ID kann auch die Verarbeitungszeit ausgemessen werden
183
-(Profiling).
184
-
185
-Die Genauigkeit der zurückgelieferten Zeit kann mit Hilfe der Funktion
186
-*clock\_getres(clockid\_t clk\_id, struct timespec * res)* ausgelesen werden.
187
-
188
-Die Funktion *clock\_gettime()* ist nicht in der Standard-C-Bibliothek zu
189
-finden, sondern in der Realzeit-Bibliothek librt. Daher ist bei der
190
-Programmgenerierung diese Bibliothek hinzuzulinken (Parameter -lrt). Steht nur
191
-die Standard-C-Bibliothek zur Verfügung, kann
192
-
193
-- time\_t time(time\_t *t) oder auch
194
-- int gettimeofday(struct timeval * tv, struct timezone * tz)
195
-
196
-eingesetzt werden.
197
-
198
-*time()* gibt die Sekunden zurück, die seit dem 1.1.1970 (UTC) vergangen sind.
199
-
200
-```c
201
-time_t now;
202
-...
203
-now = time(NULL);
204
-```
205
-
206
-*gettimeofday()* schreibt an die per tv übergebene Speicheradresse die Sekunden
207
-und Mikrosekunden seit dem 1.1.1970. Das Argument tz wird typischerweise mit
208
-NULL angegeben.
209
-
210
-Liegen die Sekunden seit dem 1.1.1970 vor (timestamp.tv\_sec), können diese mit
211
-Hilfe der Funktionen
212
-
213
-- struct tm *localtime\_r(const time\_t *timep, struct tm *result) oder
214
-- struct tm *gmtime\_r(const time\_t * timep, struct tm *result)
215
-
216
-in die Struktur *struct tm* konvertiert werden.
217
-
218
-```c
219
-struct tm absolute_time;
220
-if (localtime_r( timestamp.tv_sec, &absolute_time )==NULL)
221
-{
222
-    perror("localtime_r" );
223
-    return -1;
224
-}
225
-
226
-printf("year: %d\n", absolute_time.tm_year);
227
-```
228
-
229
-Die Funktion *time\_t mktime(struct tm * tm)* konvertiert eine über die Struktur
230
-struct tm gegebene Zeit in Sekunden seit dem 1.1.1970 (time\_t).
231
-
232
-Mit Hilfe der Funktion *clock\_t times(struct tms \ buf)* lässt sich sowohl die
233
-aktuelle Zeit zu einem letztlich nicht genau definierten Bezugspunkt, als auch
234
-die Verarbeitungszeit (Execution-Time) des aufrufenden Prozesses bestimmen. Die
235
-Zeiten werden als Timerticks (clock\_t) zurückgeliefert. Die zurückgelieferte
236
-Verarbeitungszeit ist aufgeschlüsselt in die Anteile, die im Userland und die
237
-Anteile, die im Kernel verbraucht wurden. Außerdem werden diese Anteile auch für
238
-Kindprozesse gelistet.
239
-
240
-```c
241
-#include <stdio.h>
242
-#include <sys/times.h>
243
-#include <unistd.h>
244
-#include <time.h>
245
-
246
-int main( int argc, char **argv, char **envp )
247
-{
248
-    struct tms exec_time;
249
-    clock_t act_time;
250
-    long ticks_per_second;
251
-    long tickduration_in_ms;
252
-
253
-    ticks_per_second = sysconf(_SC_CLK_TCK);
254
-    tickduration_in_ms = 1000/ticks_per_second;
255
-
256
-    act_time = times( &exec_time );
257
-    printf("actual time (in ms): %ld\n", act_time*tickduration_in_ms);
258
-    printf("execution time (in ms): %ld\n",
259
-            (exec_time.tms_utime+exec_time.tms_stime)*tickduration_in_ms);
260
-
261
-    return 0;
262
-
263
-}
264
-```
265
-
266
-Sehr genaue Zeiten lassen sich erfassen, falls der eingesetzte Prozessor einen
267
-Zähler besitzt, der mit der Taktfrequenz des Systems getaktet wird. Bei einer
268
-x86-Architektur (PC) heißt dieser Zähler Time Stamp Counter (TSC). Der TSC kann
269
-auch von einer normalen Applikation ausgelesen werden, allerdings muss
270
-sichergestellt sein, dass sich die Taktfrequenz zwischen zwei Messungen ändert.
271
-Alternativ kann man sich vom Betriebssystem über die Taktänderung informieren
272
-lassen.
273
-
274
-### Zeitvergleich: Differenzzeitmessung
275
-
276
-Zwei Absolutzeiten (struct tm) werden am einfachsten über deren Repräsentation
277
-in Sekunden verglichen. Die Umwandlung erfolgt über die Funktion (time\_t
278
-mktime(struct tm \*tm)). Allerdings ist dabei zu beachten, dass es auf einem
279
-32-Bit System am 19. Januar 2038 zu einem Überlauf kommt. Wird einer der beiden
280
-Zeitstempel vor dem 19. Januar 2038 genommen, der andere danach, kommt es zu
281
-einem falschen Ergebnis, wenn nur die beiden Werte per "\<" beziehungsweise "\>"
282
-verglichen werden.
283
-
284
-Das ist ein generelles Problem und kann dann gelöst werden, wenn sichergestellt
285
-ist, dass die zu vergleichenden Zeiten nicht weiter als die Hälfte des gesamten
286
-Zeitbereiches auseinanderliegen. In diesem Fall lassen sich die Makros
287
-einsetzen, die im Linux-Kernel für den Vergleich zweier Zeiten eingesetzt
288
-werden. Das Makro time\_after(a,b) liefert true zurück, falls es sich bei a um
289
-eine spätere Zeit als b handelt. Das Makro time\_after\_eq(a,b) liefert true
290
-zurück, falls es sich bei a um eine spätere Zeit oder um die gleiche Zeit
291
-handelt, wie b handelt. Die Zeitstempel a und b müssen beide vom Typ unsigned
292
-long sein. Natürlich können die Makros auch auf andere Datentypen angepasst
293
-werden
294
-
295
-[HEADERDATEI linux/jiffies.h].
296
-
297
-```c
298
-#define time_after(a,b)         \
299
-        (typecheck(unsigned long, a) && \
300
-         typecheck(unsigned long, b) && \
301
-         ((long)(b) - (long)(a) < 0))
302
-
303
-#define time_before(a,b)        time_after(b,a)
304
-
305
-#define time_after_eq(a,b)      \
306
-        (typecheck(unsigned long, a) && \
307
-         typecheck(unsigned long, b) && \
308
-         ((long)(a) - (long)(b) \>= 0))
309
-
310
-#define time_before_eq(a,b)     time_after_eq(b,a)
311
-```
312
-
313
-Beim Benchmarking ist es häufig notwendig eine Zeitdauer zu messen, Zeitpunkte
314
-zu erfassen oder eine definierte Zeit verstreichen zu lassen. Dabei sind
315
-folgende Aspekte zu beachten:
316
-
317
-- Die Genauigkeit der eingesetzten Zeitgeber,
318
-- die maximalen Zeitdifferenzen,
319
-- Schwankungen der Zeitbasis, beispielsweise durch Schlafzustände,
320
-- Modifikationen an der Zeitbasis des eingesetzten Rechnersystems (Zeitsprünge)
321
-  und
322
-- die Ortsabhängigkeit absoluter Zeitpunkte.
323
-
324
-Zur Bestimmung einer Zeitdauer verwendet man häufig eine Differenzzeitmessung.
325
-Dabei wird vor und nach der auszumessenden Aktion jeweils ein Zeitstempel
326
-genommen. Die Zeitdauer ergibt sich aus der Differenz dieser beiden Zeitstempel.
327
-
328
-Dies ist allerdings nicht immer ganz einfach. Liegt der Zeitstempel
329
-beispielsweise in Form der Datenstruktur struct timeval (als Ergebnis der
330
-Funktion *gettimeofday()*) vor, müssen die Sekunden zunächst getrennt von den
331
-Mikrosekunden subtrahiert werden. Ist der Mikrosekundenanteil negativ, muss der
332
-Sekundenanteil um eins erniedrigt und der Mikrosekundenanteil korrigiert werden.
333
-Dazu wird auf die Anzahl der Mikrosekunden pro Sekunde (also eine Million) der
334
-negative Mikrosekundenanteil addiert.
335
-
336
-```c
337
-#define MICROSECONDS\_PER\_SECOND 1000000
338
-
339
-struct timespec * diff_time( struct timeval before, struct timeval after,
340
-    struct timeval *result )
341
-{
342
-    if (result==NULL)
343
-        return NULL;
344
-
345
-    result->tv_sec = after.tv_sec - before.tv_sec;
346
-    result->tv_usec= after.tv_usec- before.tv_usec;
347
-
348
-    if (result->tv_usec<0) {
349
-        result->tv_sec--;
350
-        /* result->tv_usec is negative, therefore we use "+" */
351
-        result->tv_usec = MICROSECONDS_PER_SECOND+result->tv_usec;
352
-    }
353
-    return result;
354
-
355
-}
356
-```
357
-
358
-Für Zeitstempel vom Typ struct timeval gilt entsprechendes. Anstelle der
359
-MICROSECONDS\_PER\_SECOND sind NANOSECONDS\_PER\_SECOND einzusetzen. Sind die
360
-Zeitstempel vorzeichenlos, sieht die Rechnung für den Sekundenanteil etwas
361
-komplizierter aus. Das soll hier aber nicht weiter vertieft werden.
362
-
363
-Etwas einfacher ist die Differenzbildung, wenn aus der Datenstruktur eine
364
-einzelne Variable mit der gewünschten Auflösung, beispielsweise Mikrosekunden,
365
-generiert wird. Im Fall von struct timeval wird dazu der Sekundenanteil mit
366
-einer Million multipliziert und der Mikrosekundenanteil aufaddiert. Bei der
367
-Multiplikation können natürlich Informationen verloren gehen, allerdings geht
368
-der gleiche Informationsgehalt auch beim zweiten Zeitstempel verloren. Für die
369
-Differenzbildung ist das damit nicht relevant, solange der zu messende zeitliche
370
-Abstand kleiner als 1000 Sekunden ist und es während der Messung keinen Überlauf
371
-beim Sekundenanteil gibt.
372
-
373
-```c
374
-time_in_usec=((nachher.tv_sec*1000000)+nachher.tv_usec)-
375
-    ((vorher.tv_sec*1000000)+vorher.tv_usec);
376
-```

Laddar…
Avbryt
Spara