Beiträge von shabby

    prepare liegt im Verzeichnis /root, d.h. nach dem Einloggen sollte man ./prepare <username> aufrufen.
    Das Skript
    (1) mounted [hostfs] /opt/osue/uml auf /media/host (Kernelsourcen)
    (2) mounted [hostfs] /homes/<username> auf /root/homedir (dort sollte entwickelt werden)
    (3) bereitet die Swap-Partition vor

    ein studienkollege hat mir gesagt dass ich alle variablen direkt unter dem funktionskopf definieren muss. da ich noch nicht herausgefunden hab wie man den kernel mit gnu99 compilieren kann, muss ich mich an die regeln von c89 :awake:


    Um das Kernelmodul mit -std=gnu99 zu uebersetzen, folgende Zeile in das Makefile reingeben (siehe http://lxr.free-electrons.com/…kbuild/makefiles.txt#L286)

    Code
    1. ccflags-y:=-std=gnu99


    Achtung: Da das Kernel Buildsystem auch die Option "-Wdeclaration-after-statement" an gcc uebergibt, kommt es weiterhin zu Warnungen, wenn Deklarationen nicht C89-konform vor den Anweisungen eines Blocks plaziert werden. Theoretisch kann man auch diese Option ueberschreiben (-Wno-declaration-after-statement), ist wegen des Stilbruchs aber nicht empfehlenswert.

    aber werds mal mit alloc_chrdev_region probiern, aber moment mal...wie soll ich dann dann die einzelnen sv_data und sv_ctl ansprechen? alle habne dann die selbe major und minor number? weil wenn ich in der UML unter /dev/ mal die anderen geräte ansehe habne zb ram[0-x] alle die selbe major number und aber untereinander eine ANDERE minor number :confused:


    Vorab: Unbedingt die empfohlenen Teile von LDD3 lesen (in besondere Kapitel 3).

    Zitat von Ravu al Hermio

    In aktuellen Kernelmodulen ist es jedenfalls empfohlen, alloc_chrdev_region zu verwenden, um an noch nicht verwendete Devicezahlen zu kommen.


    Stimmt; für das Bonusbeispiel ist es aber ok und sinnvoll, feste Device Numbers zu verwenden.
    Inbesondere sind im UML bereits Dateien in /dev/ vorhanden, um das secvault device anzusprechen:

    Code
    1. sysprog-bonus:~# ls /dev/sv* -la
    2. crw-rw-rw- 1 root root 231, 0 Dec 3 17:13 /dev/sv_ctl
    3. crw-rw-rw- 1 root root 231, 1 Dec 3 17:13 /dev/sv_data0
    4. crw-rw-rw- 1 root root 231, 2 Dec 3 17:13 /dev/sv_data1
    5. crw-rw-rw- 1 root root 231, 3 Dec 3 17:13 /dev/sv_data2
    6. crw-rw-rw- 1 root root 231, 4 Dec 3 17:13 /dev/sv_data3


    Zitat von mr_apps


    habs jetzt so gemacht dass ich ein modul namens sv_vault habe, das 5 devices anlegt ein sv_ctl und 4 sv_data in /dev/ mit major 231 und minor 0-4


    Ja, beim Laden des Moduls sollten die Devices Numbers (231, 0-4 - falls statisch) registriert werden. Was ist mit "in /dev/" gemeint?


    Zitat von mr_apps


    jetzt arbeit ich gerade am user tool svctl und mir fallt auf dass es keine option zum auslesen oder hineinschreiben in den vault selber gibt. passiert das über eine 3tes tool? zb vim, cat, echo etc?


    Am besten ist es, sich mit dd vertraut zu machen, um unterschiedliche Lese- und Schreibzugriffe einfach zu testen; mit echo oder cat kann man natürlich auch auf die Datendevices zugreifen.

    Das ist mir schon klar, aber warum wird das hier gemacht? Wenn ich das höchstwertige Bit um 15 Stellen nach rechts schiebe, bleibt mir doch ohnehin nur noch das unterste Bit und der Rest wird mit Nullen aufgefüllt. Oder lieg ich da falsch?


    Richtig bemerkt, im ersten Fall (Anm: req ist vom Typ uint16_t) ist es nicht notwendig, das LSB zu maskieren. Die UND-Verknüpfung an dieser Stelle mag verwirrend wirken, ich finde den Code so leichter zu lesen -- auf diese Weise ist nämlich auch ohne die Deklaration von req zu kennen klar, dass das 16. Bit extrahiert wird. Verwendet man einen optimierenden Übersetzer (so wie wir gcc) sollte es bezüglich des resultierenden Maschinencodes keine Rolle spielen, ob die bitweise UND-Verknüpfung weggelassen wird.
    Im zweiten Fall (parity_calc &= 0x1) ist die bitweise UND-Verknüpfung notwendig; direkt vor dieser Anweisung kann parity_calc einen Wert zwischen 0 und 7 (0b111) einnehmen; nur das erste Bit ist von Interesse.

    Hallo,


    A3: How do you deal with or prevent page faults in file system routines. Argue why your solution is deadlock free.


    Könnte mir die Frage jemand näher erläutern, ich versteh nicht ganz was damit eigentlich gemeint ist?


    Erster Teil der Frage: Kann es in den Filesystem-Routinen (z.B. filesys_open, aufgerufen von einem system call handler) zu einem Pagefault kommen? Wenn nein, warum nicht? Wenn ja, wurden zusätzliche Maßnahmen getroffen, um einen Deadlock zu verhindern?


    Zweiter Teil der Frage: Page Faults in Filesystem-Routinen können rasch zu einem Deadlock führen (lock file system to read file on behalf of the user -> page fault -> lock file sytem to read page -> deadlock). Wie wird dieses Problem in (der/eurer) PintOS Implementierung behandelt?


    Diese Fragestellungen sind zwar spannend, aufgrund der bereits vorgegebenen Implementierung der Syscalls in der PintOS/Progos Variante aber eher theoretischer Natur. Darüber nachzudenken lohnt sich denke ich trotzdem.

    Danke fuer dein Feedback.
    Das start Skript wurde nun weiter verbessert, sodass die swap Datei keinen Speicherplatz belegt, solange sie nicht verwendet wird.
    Leider konnte ich das Problem an sich nur bedingt reproduzieren; die beiden Dateien (swap und copy-on-write) benoetigen lediglich wenige MB auf der Platte, und eigentlich sollten alle Studenten 500M zur Verfuegung haben.


    Der Administrator des TI-Labs (Heinz Deinhart) sieht sich das Problem gerne genauer an, benoetigt dazu aber weitere Informationen. Bitte kontaktiere ihn, falls das Problem noch immer auftritt.


    Als vorlaeufigen Workaround kann man ausserdem die COW Datei im /tmp/ Dateisystem erstellen:

    Code
    1. /opt/zanal/uml/vmlinux ubda=/tmp/uml.`id -nu`.cow,/opt/zanal/uml/sysprog_bonus.umldisk con=pts con0=fd:0,fd:1


    Allerdings sind in diesem Fall alle Aenderungen ausserhalb des (mit ./prepare yxxxxxxx gemounteten) ~/homedir fluechtig.

    Liegt vermutlich am 256M Swapfile ~/.uml.swap, welches das start Skript anlegt. Grund: ohne Swapfile war es mir im TILAB nicht möglich das Testmodul zu kompilieren. Das Swapfile wird ab sofort vom start Skript mit 64M angelegt, sofern es noch nicht existiert; das genügt ebenfalls zum Kompilieren des Kernelmoduls.
    Am besten wäre es, erst lewurms Vorschlag zu folgen (vielleicht gibt es noch andere große Dateien), dann dass swap file zu löschen (rm ~/.uml.swap) und schließlich UML ohne swap file zu starten:

    Code
    1. ti1$ cd /opt/zanal/uml
    2. ti1$ ./vmlinux ubda=$HOME/.uml.cow,sysprog_bonus.umldisk con=pts con0=fd:0,fd:1


    Klappt es ohne oder mit dem kleineren Swapfile?
    Nach dem Start sollte (in einem anderen Terminal) folgendes funktionieren:

    Code
    1. ti1$ screen /dev/pts/XX
    2. # login as root, no pwd
    3. uml$ ./prepare yxxxxxxx
    4. uml$ cd test_module
    5. uml$ make


    Praktisch gesehen ist es eine gute Idee, die SO_REUSEADDR Option zu setzen weil:

    Zitat von http://www.unixguide.net/network/socketfaq/2.7.shtml


    When you close a socket, the server goes into a TIME_WAIT state, just to be really really sure that all the data has gone through.


    Das bedeutet aber auch, dass für eine bestimmte Zeit der Port nicht verfügbar ist. Da der Mastermind-Server oft neu gestartet wird, wäre es recht mühsam, jedes mal zu warten, bis der Port wieder frei wird. Deshalb SO_REUSEADDR:

    Zitat von http://www.unixguide.net/network/socketfaq/4.5.shtml

    What exactly does SO_REUSEADDR do? This socket option tells the kernel that even if this port is busy (in the TIME_WAIT state), go ahead and reuse it anyway.


    Um die SO_REUSEADDR Option zu setzten, siehe man 2 setsockopt, level sollte SOL_SOCKET sein.


    Zitat von luna


    also, ich bin letztes Semester leider durchgerasselt aber da haben wir den Server als auch den Client selbst ausprogrammieren müssen.
    Jetzt muss ich leider feststellen - die Vorgabe des Servers ist ja uuuuuur-kompliziert (zu lesen)!!!!
    Genau so fühlte ich mich bei jedem Test - komplizierter kann man die Programmierung nicht gestalten...


    Nun ja, möglicherweise lassen sich Teile des vorgegebenen Source Codes vereinfachen, schwer zu sagen ohne konkreten Vorschlag. Und ja, diesmal wurde Source Code genau deshalb vorgegeben /um/ die Überraschung in der Testsituation zu vermeiden.


    Zitat


    NACHTRAG - (aber wo ich [SO_REUSEADDR] setzen muss, weiß ich noch immer nicht):


    Direct nach dem Erstellen des Sockets.

    Ja, es ist okay, wenn du so oder so keine Warnungen bekommst. Der Zweck des Castens ist, das Verwerfen des Rückgabewerts explizit zu machen. Damit kannst du den Unterschied zwischen "ich hab vergessen, den Rückgabewert abzufragen" und "mir ist der Rückgabewert egal" direkt im Code notieren.


    [..]noch zwei schwaechere argumente:
    *) es kostet nichts/tut nicht weh/es wird nicht langsamer => also ist es nur ein vorteil es explizit zu machen.
    *) historisch: man konnte lint damit nervoes machen wenn man den cast nicht gemacht hat.


    Es macht den Code nicht wirklich lesbarer und ist IMO völlig unnötig. (Das es in der LVA verlangt / erwünscht ist, ist ein anderes Thema).


    Noch ein paar Anmerkungen:

    • Das Casten auf void ist bei manchen Funktionen sogar notwendig, um Warnungen vom gcc zu vermeiden (wenn die Funktion mit warn_unused_result attributiert ist). In dem Fall ist es allerdings sowieso keine gute Idee, den return Wert zu ignorieren.
    • @lint: Nicht nur historisch: Safe Programming lint verlangt mit -retval{int,bool} dass keine Rückgabewerte ignoriert werden. Sicherheitskritischer Code muss oftmals auch als möglicherweise streng empfunden, formalen Kriterien genügen (Analysetool findet keinen Fehler).
    • Die Technik ("casten auf void") wird auch in anderen Sprachen verwendet; dazu gab es z.B. eine verhältnismäßig lange Diskussion of haskell.libraries 1 2

    Im übrigen stimmt es schon, dass die Fehlerbehandlung in C recht mühsam ist und verhältnismäßig viel Code benötigt. In Java find ich Exceptions auch ganz nett, aber die bringen für systemnahe Sprachen wie C wieder andere Probleme mit sich.
    Nur ein Beispiel: Erlaubt man in C++ Exceptions, dann gibt es im folgenden Code ein potentielles memory leak:

    Code
    1. x = new A(0); x.doSomething(); delete x; /* ... one reason to use auto_ptr */


    Und einige safety-critical Menschen stehen Exceptions sowieso skeptisch gegenüber (auch wegen dem Ariane 5 Unfall, obwohl es hier IIRC um eine HW Exception in der Fliesskommaeinheit ging).


    Irgendwie macht genau so was den ganzen Sinn der Sache "Compiler kennt die erforderliche Stackgröße bereits am Anfang der Funktion" wieder kaputt. Insofern habe auch ich nicht verstanden, warum es diese Einschränkung überhaupt jemals gegeben hat. What were they thinking??


    Insbesondere aus der Sicht des Übersetzers ist die Einschränkung, wie du richtig gesagt hast, historischer Natur. Moderne Compiler analysieren in welchen Bereichen der Funktionen welche Variablen 'aktiv' sind (Live Ranges), und versuchen die Variablen so oft wie möglich in Registern zu halten und gar nicht auf den Stack zu schreiben.


    Edit: Nachdem in Deklarationen auch Funktionsaufrufe und beliebige Anweisungen als Initializer erlaubt sind, sehe ich als einzigen kleinen Vorteil der C89 Restriktion dass die Grammatik möglicherweise als etwas einfacher gesehen wurde, also (decls* stmts*) statt (decls|stmts)*. Eine vernünftige Begründung für das Design konnte ich auch noch nicht finden. Zwei Gründe, sich trotzdem an C89 zu halten: Einige Compiler unterstützten kein C99, und viele Programmierer haben sich schon an die Einschränkungen von C89 gewöhnt ;)


    Ich hab generell ziemlich viele Fehler bei splint - müssen die alle weg sein für die Abgabe (bzw. damit das Programm sauber geschrieben ist).


    Generell gilt mal: Manche Tests von splint liefern zu viele falsche Positive (spurious warnings), vorallem dann, wenn man sich nicht an bestimmte Programmierrichtlinien hält, oder sein Programm nicht hinreichend annotiert (oder annotieren kann/will).
    Zu viele unnötige Warnungen frustrieren den Testenden, weshalb auch in der Praxis der Auswahl der richtigen Menge von Tests eine grosse Rolle spielt. Dazu gibst einen tollen Artikel: A few billion lines of code later


    Die Fehler gehen in die Richtung, dass mir splint sagt, dass Variablen aus einer Struktur nicht definiert sind... ich habe aber vorher z.b. memset() darauf aufgerufen, damit alles eben 0 ist. Oder ich hole mir mit der Funktion strtok() die Token aus den Strings, überprüfe danach, ob der Rückgabewert == Null war (wenn ja printError) und danach arbeite ich mit der Variable eben weiter. Splint sagt mir, dass aber an den späteren Stellen die Variable gleich Null sein kann.....


    Wenn zu viele solche Fehler auftreten, würde ich die dementsprechenden Tests ausschalten, ansonsten ignorieren. Der zweite Fehler sollte eigentlich nicht auftreten: Was passiert nach printError? Wenn man einfach weitermacht, kann der Fehler ja trotzdem auftreten. Wenn splint versteht, dass in diesem Zweig das Programm beendet wird, sollte es keine Fehlermeldung geben. Vielleicht kannst du die Beispiele im Wiki posten?


    Kann ich generell solche Fehlermeldungen einfach "ignorieren", sobald ich weiß, dass sie eh nicht eintreten können?


    Genau, gerade dann, wenn die Fehlermeldungen nur schwierig oder umständlich verhindert werden können.

    Zitat von Bernd


    Die Abgabe selbst werden wir "haendisch" ohne automat. Analyse bewerten; aber es ist sehr erfreulich, dass du dich mit splint beschaeftigst: selbst wenn es momentan hier im Rahmen der LVA (noch) keine Punkte od. deutlich sichtbare Vorteile gibt, ist das sicher keine verschwendete Zeit.


    Ich würde auch behaupten, dass es ein sehr gutes Zeichen ist, wenn man die Fehlermeldungen von splint interpretieren und gegebenenfalls korrigieren kann. Das geht nämlich nicht automatisch, oder ohne ein solides Verständnis von C. In Zukunft wäre es vielleicht denkbar, dass bei der Abgabe einige Tests von splint laufen gelassen werden, deren Ausgabe dann erklärt werden müßte.


    In der Übung sollte man die Optionen +posixlib (wenn POSIX Bibliotheken verwendet werden) und -warnsysfiles (weil man System Libraries nicht verändern kann/will) verwenden. Der Artikel im Wiki weiß mehr darüber.

    Guten Morgen,


    ich hätte eine Frage zum Codeauszug:

    Code
    1. char *szInputFile = (char*)0;
    2. if(szInputFile != (char*) 0) // mehrmalige verwendung?
    3. {
    4. bError = 1;
    5. break;
    6. }

    Meine Fragen:
    Was genau macht die Variable szInputFile?


    In der Variable szInputFile wird in diesem Fall (Folie 12?) ein Zeiger auf das Argument zur Option "a" gespeichert (präziser: ein Zeiger auf das erste Zeichen des null-terminierten Strings).


    Nehmen wir an, ein Programmaufruf "hallo -a opt -b bar" wird mit getopt(argc,argv,"a:b:") analysiert. Dann liefert der erste Aufruf von getopt 'a' zurück. Zusätzlich zeigt direkt nach dem Aufruf optarg auf den String "opt". Nach einem weiteren Aufruf von getopt zeigt optarg auf den String "bar". Daher ist es notwendig, das Argument zu "-a" vor dem nächsten Aufruf zu speichern. In diesem Fall eben in der Variable szInputFile.

    Zitat


    Warum ist diese auf (char*) 0 initialisiert?
    Leider weiß ich nicht, was das ist und was es tut und vorallem warum es so initialisiert ist...

    Um zu erkennen, ob der Variable bereits etwas zugewiesen wurde. Da gültige Addressen ungleich 0 sind, kann man durch den Vergleich mit 0 feststellen, ob der Variable bereits ein gültiger Wert zugewiesen wurde. Würde die Variable gar nicht initialisiert, so ist der Wert der Variablen vor der ersten Zuweisung undefiniert (d.h., beliebig).

    Zitat


    Was bedeutet in den Folien //mehrmalige verwendung?

    Ist szInputString ungleich 0, dann wurde der Variable bereits ein String zugewiesen; daraus folgt, dass die Option mindestens zweimal angegeben wurde (Fehler). Diese Kommentar stammt jedoch meines Wissens nach nicht aus den aktuellen Folien. Ein ähnliches Programmiermuster würde in Java etwa wie folgt aussehen:

    Code
    1. String foo = null;
    2. for(String s : input) {
    3. if(s.equals(foo)) {
    4. if(foo != null) throw new Exception("Bad input: Found two foo's");
    5. foo = s;
    6. }
    7. }
    Code
    1. C-Code // Kommentar bis zum Ende der Zeile

    Diese Form von Kommentaren (C++ Stil) ist nich ANSI C89 konform, und sollte in den Folien nicht vorkommen. Falls doch, bitte einen Hinweis auf die dementsprechende Folie.