Linux-Grundlagen


Dirk Geschke,LUG Erding

Letzte Änderung: 7.11.2007

Die Historie im Schnelldurchgang

Die ausführliche Geschichte gab es ja nun schon, hier sind noch einmal die wichtigsten Punkte die bei der Entstehung von Linux beitrugen:

Multics -> Unix
Multics war ein Flop, das System brach unter der eigenen Last ein. Durch dieses Projekt gab es aber eine gute, ausführliche Dokumentation auf die bei der Entstehung von Unix zurückgegriffen wurde. Viele Ideen und Konzepte von Unix haben dort ihre Wurzeln. Für die Entstehung von Linux war es nur insofern wichtig, dass diese Dokumentation erst einmal die Basis für eine breite Akzeptanz von Unix darstellten und somit zu dessen Verbreitung beitrug.
AT&T, Berkeley, bzw. SysV versus BSD -> Die Copyright-Klage
Diese Klage gegen die Freigabe von BSD bewirkte, dass Anfang der '90er Jahre alle Unix-Sourcen unter Verschluß waren. Die einzige frei erhältliche unixartige Version war Minix von Andrew S. Tanenbaum von der Vreije Universiteit Amsterdam. Dieses System war aber als Lehrmittel für ein Betriebssystem gedacht und nicht als ein produktives. Daher wurden viele Funktionalitäten trotz drängen der Anwender nicht eingebaut. Die Lizenz erlaubte es auch nicht eine modifizierte Version herauszubringen. Daher mußten diese Funktionen jedesmal von jedem Anwender nachträglich in das System gepatched werden. Das ist natürlich ein frustrierender Vorgang.
Aufkommen des Internets
Anfang der '90er Jahre fand das Internet an den Universitäten seine Verbreitung. Damit hatten viele Wissenschaftler und Studenten Zugang zu diesem neuen Medium Man darf eines nicht vergessen: Selbst das "freie" BSD war nur über Magnet-Bänder zu beziehen. Das Unix allein ist zwar gratis gewesen aber die Gebühren für das Erstellen und Versenden der Bänder waren sehr hoch. Damit war Linux bereits in den Anfängen gut zu verbreiten. Hinzu kamen die Newsgroups als Forum für die Kommunikation. (Man stelle sich einmal vor, dass für jede neue Version erst ein Band bzw. Disketten erstellt und verschickt werden müßten. Das wäre wohl weder preiswert noch interessant gewesen...)
Gnu is Not Unix
Das GNU-System existierte bereits, es fehlte nur noch der Kernel. Die ganze notwendige Infrastruktur wie Editor, Compiler, Shell waren schon vorhanden, das mußte nicht mehr entwickelt werden.
Die langen dunklen Winter in Finnland
Auch das sollte man nicht außer acht lassen, dies war die beste Zeit um stundenlang am Computer zu arbeiten.

Alles dies zusammen stellte die Plattform für den Aufstieg von Linux bereit. Hinzu kommt natürlich auch das allgemeine Interesse die Entstehung eines Betriebssystems mitzuverfolgen.

Wobei hier klar gesagt werden muß: Linux ist nur der Kernel.

Daher kommen auch zu Recht die Äußerungen von RMS, dass das Betriebs- system eigentlich GNU/Linux heißen müßte. Linus lehnte es für den Kernel ab, da er den Namen unansehnlich fand.

Die ersten Versionen des Kernels erschienen nicht unter der GPL, diese hatten eine linuseigene Lizenz. Diese hatte aber ihre Probleme, so dass eine andere Lizenz ratsam wurde. Da der Kernel aber vom GCC lebt hat sich Linus dazu entschieden die GPL zu verwenden. Dies war nicht, weil er so hinter der Ideologie von GNU stand. Linus war nur wichtig, dass es eine freie Lizenz ist, das ist wohl eine Folge des Minix-Problems.

Dadurch, dass Linux eigentlich nur der Kernel ist, war die Basis für die vielen verschiedenen Distributionen geschaffen. Das dürften die meisten vermutlich eher als einen Nachteil ansehen.

Aufbau eines Betriebssystems

Das Herz eines jeden Betriebssystems ist der Kernel. Dieser umschließt gewissermaßen die gesamten Ressourcen, insbesondere die Hardware. Daher stammt der Name, er schottet das Innerste von den Benutzern ab.

Um den Kernel herum befindet sich die Schale: Shell.

Jeder Benutzer interagiert mit der Shell, die Shell wiederum mit dem Kernel, der wiederum die Hardware ansteuert. So hat weder ein Benutzer noch ein Programm (außer dem Kernel) direkten Zugriff auf die Hardware. Dies ist die Voraussetzung dafü, dass mehrere Programme (Benutzer) parallel arbeiten können ohne sich gegenseitig zu stören.

Es gibt natürlich auch die Möglichkeit, dass Programme direkt mit dem Kernel kommunizieren. Aber für die Sichtweise und Namensgebung ist es nicht unwichtig obiges zu wissen. Die Shell ist schließlich auch nichts anderes als ein Programm.

Programme die nicht innerhalb einer Shell laufen werden gewöhnlich Daemon genannt, Disk and execution monitor.

Laut dem Hacker's Dictionary war die ursprüngliche Bezeichnung wohl doch eher aus dem Reich der Mythologie und wurde dann später als dieses Akronym verkauft. Wichtig ist: Daemonen sind Programme die im Hintergrund laufen und irgendwelche Dinge verrichten. Bei Windows heißen diese Services, ein vergleichsweise profaner Name.

Die Philosophie von Unix

Die Grundaussage ist recht einfach

Alles ist eine Datei

Einen entgegengesetzten Ansatz verwendet Microsoft, hier ist alles geräteorientiert. Es gibt z.B. verschiedene Laufwerke die explizit benannt werden müssen (A:, B:, C:,...), Drucker (LPT:), etc.

Unter Unix sind dies alles Dateien, so heißt die erste Partition einer IDE Festplatte (Master am 1. IDE-Bus) /dev/hda1, der erste Drucker heißt /dev/lp0.

Selbst Netzwerkverbindungen lassen sich via Dateidescriptoren abbilden, d.h. die Routinen für das Lesen aus einer Datei sind die gleichen wie für das Lesen aus einer Netzwerkverbindung. Das macht das Programmieren sehr leicht, allerdings nur auf Benutzerseite, die Kernelseite ist da etwas schwieriger.

Das Alles ist eine Datei stimmt auch nicht in allen Beziehungen, dies trifft eher auf Plan-9 von den Bell-Labs zu. Das sollte einmal der Nachfolger von Unix werden: http://www.cs.bell-labs.com/plan9dist/ Hier werden dann auch Ressourcen auf anderen Computern wie eine Datei behandelt.

Eine spezifischere Beschreibung der Unix-Philosophie ist die von Doug McIlroy, dem Erfinder der Pipe:

Also:

Ein Programm für jede Aufgabe und keine Monsteranwendungen. Das ergibt viele kleine, schlanke und effiziente Programme die zudem leicht zu debuggen sind da sie weniger komplex sind.

Die Programme sollen in der Lage sein zusammenarbeiten zu können, das führt dann zu einem Modulbaukasten.

Als Kommunikationssprache soll Text verwendet werden. Der kann von jedem gelesen werden und er erleichtert die Fehlersuche sehr, es müssen keine binären Standards interpretiert werden. Wenn die Zusammenarbeit zweier Programme schief läuft kann leicht die Zwischenausgabe analysiert werden wodurch Fehler eventuell schneller lokalisiert werden können.

Das ist ein sehr elegantes Prinzip. Durch die Modularität und klaren Grenzen zwischen den Programmen können Fehler wunderbar gesucht und beseitigt werden.

Bei Monsterapplikationen wie z.B. Office-Lösungen ist das nicht so einfach. Entsprechend häufig werden Artikel gefunden die beschreiben wie deren Fehler elegant umschifft werden.

Es ist auch irgendwie verständlich: Ab einer gewissen Komplexität ist das Wissen darüber welche Routine für was zuständig ist und zu welchen Nebenwirkungen Änderungen daran führen können nicht mehr vorhanden oder leicht ableitbar. Viel Eleganter sind da klare Schnittstellen.

Wenn dann auch noch ASCII-Text verwendet wird, ist jeder in der Lage direkt zu sehen wo es klemmen könnte, selbst bei derart komplexen Strukturen wie z.B. XML, auch wenn die Analyse dann ein wenig länger dauert. Wenn das ganze binär wäre, dann müsste für die Fehlersuche das Binäre in Text umgewandelt werden, dann kann aber auch der Fehler in der Umwandlungsroutine liegen!

Oder wenn der Standard in einer Applikation erweitert wird, die zweite die neuen Strukturen noch nicht kennt, dann könnte diese Applikation z.B. einfach sagen:

unsupported directive newimage

Schon kann auf Anhieb festgestellt werden wo der Fehler liegt. In diesem Fall war es ein Programm, dass die MagicPoint-Folien in HTML umwandeln soll. Diese Routine kennt aber newimage noch nicht. Klar, der Name deutet es an, es ist eine neue Methode um Bilder eleganter einzubinden. Hier war es das HW->Kernel->Shell->User Bild aus dem Vortrag.

Dieses Prinzip liegt auch den meisten Konfigurationsdateien zugrunde. Microsoft hat soetwas früher auch verwendet, da waren es die sogenannten INI-Dateien.

Wo soll der Vorteil liegen, wenn diese Konfigurationsdatei derart kryptisch ist, dass ein spezieller Editor verwendet werden muß?

Angeblich soll binär performanter sein. Aber stimmt das auch, wenn alle Konfigurationen in einer Datei liegen anstatt pro Programm eine eigene Datei? Wie oft wird eine Konfigurationsdatei gelesen? Eigentlich doch nur einmal beim Programmstart, da machen die Millisekunden zum Parsen der Datei nicht wirklich etwas aus insbesondere wenn jedes Programm seine eigene Konfigurationsdatei hat.

Aufgaben des Kernels

Der Kernel stellt die Schnittstelle zwischen den Programmen und den Ressourcen/Hardware dar. Keine Anwendung darf direkt auf die Hardware zugreifen. Es wäre auch etwas chaotisch wenn dies möglich wäre, ganz abgesehen von der Sicherheit.

Da Anwendungen aber durchaus mit der Hardware arbeiten müssen gibt es entsprechende Schnittstellen zum Kernel, diese werden Systemaufrufe bzw. System Calls genannt. Sie stehen im Abschnitt 2 der Handbuchseiten.

Die Anwendung teilt z.B. dem Kernel mit welchen Bereich einer Datei sie haben möchte, z.B. Dateinamen und wieviel Bytes. Der Kernel sucht dann die Stelle auf der Festplatte, liest den oder die entsprechenden Blöcke ein und liefert die Daten an die Anwendung zurück. Wo die Datei physikalisch auf der Festplatte liegt braucht die Anwendung nicht wissen.

Der nächste wichtige Punkt ist das Prozessmanagement, das heißt, das Erstellen von Prozessen und deren Überwachung wird auch vom Kernel gesteuert. Dazu zählt das Scheduling, d.h. die Zeiten wann ein Programm wie lange laufen darf.

Multitasking heißt zwar, dass die Prozesse parallel laufen sollen aber in der Realität laufen sie gewöhnlich nacheinander, jeder Prozess bekommt dann kurz die CPU um seine Aufgaben zu erledigen. Nach einer vom Kernel bestimmten Zeit wird der Prozess angehalten und der nächste kommt an die Reihe. Dieser Vorgang wird Scheduling genannt.

Ferner obliegt dem Kernel die Handhabung des Speichers. Dieser zählt nur zum Teil zur Hardware, jeder Prozess bekommt seinen eigenen virtuellen Speicher. Die Abbildung dieses Speichers auf die RAM- Bausteine übernimmt wiederum der Kernel.

Zusammen mit dem Swapspeicher, d.h. der Auslagerung des aktuellen Speicherinhaltes auf die Festplatte, Microsoft nennt es temporäre Auslagerungsdatei, kann so deutlich mehr Speicher adressiert werden als der Computer an Hauptspeicher besitzt.

Genaugenommen braucht nicht jeder Prozess den gesamten Speicher den er anfordert. Aber das ist eine andere Geschichte...

Last but not Least: Der Kernel handhabt auch das Netzwerk sowie die vielen Protokolle wie z.B. TCP oder UDP. Der Kernel stellt zwar verschiedene Schnittstellen bereit, so dass auch Applikationen die höheren Protokolle auswerten können aber in der Regel wird dies dem Kernel überlassen.

Ausnahmen sind z.B. ping und traceroute und natürlich die Tools zum Fälschen von Adressen...

Die Shell

Das meiste bezieht sich hier auf die bash, der Standardshell unter Linux.

Allgemeines über die Shell

Es gibt eine Vielzahl von Shells, was hauptsächlich daran liegt, dass sie die Schnittstelle zum Anwender darstellen, daher auch der Name Shell.

Die Fähigkeiten der ersten Shells waren sehr bescheiden von daher gab es immer Bestrebungen etwas besseres zu erstellen. Auf der anderen Seite musste die Kompatibilität mit der alten Shell erhalten bleiben.

Diese alte Shell ist die Bourne-Shell und befindet sich immer unter /bin/sh.

(Bei Solaris liegt sie unter /usr/bin/sh aber es gibt einen symbolischen Link von /bin/sh dahin. Wer nun im Vorgriff fragt warum die Shell in einem Verzeichnis liegt, dass auf einer anderen Partition als der Root-Partition liegen darf: Der Superuser hat als Loginshell /sbin/sh, das ist eine statisch gelinkte Shell in der Root-Partition.)

Die meistverwendete Shell ist heutzutage die bash, bourne again shell in Anspielung auf den Klassiker unter der sprachlichen Bedeutung von born - geboren, es ist also eine wiedergeborene Shell...

Andere bekannte Shells sind:

Jetzt beschränke ich mich auf die bash als Shell, einige Dinge wie z.B. einen Prozess anhalten und in den Hintergrund verschieben können nicht alle Shells, insbesondere die klassische Bourne Shell kann dies nicht.

Aufgaben der Shell:

Das Starten ist leicht, einfach ein Programmnamen eintippen und Enter drücken. Das Stoppen ist genausoleicht: ^C (STRG-C) drücken, das sendet dem Programm das Signal SIGINT, einen Interrupt also. Dies ist aber nur notwendig, wenn das Programm keine "normale" Prozedur für das Beenden hat.

Programme können mit ^Z (SIGSTOP) angehalten werden. In den Hintergrund geschoben wird es mit dem Kommando bg (background) und fg (foreground) holt es wieder in den Vordergrund.

#! /bin/sh

echo "Dies ist ein Shellskript"

Werden diese 3 Zeilen in einer Datei "testscript" abgespeichert und wird ein "chmod a+x testscript" ausgeführt, so kann das Programm auch tatsächlich innerhalb einer Shell gestartet werden. Dazu muss; bei den meisten Distributionen der Pfad mit angegeben werden. Das Einfachste besteht darin "./testscript" zu verwenden. Die Zeichen "./" bewirken, dass nach der Datei im aktuellen Verzeichnis gesucht wird. echo gibt nur wieder was dahinter steht. Die Datei kann aber direkt mit der Shell aufgerufen werden " sh testscript ". Hier zeigt sich der Vorteil, dass die erste Zeile mit einem Kommentarzeichen beginnt: Diese Zeile wird dann einfach ignoriert.

Aber es gibt noch Kontrollstrukturen wie if-then, while-do, for-in-do, etc.

Ein einfaches wiedergeben der Dateien im aktuellen Verzeichnis geht mit:

for i in *
do
  echo $i
done

Der Stern * expandiert zu allem was im aktuellen Verzeichnis steht.

Eine Besonderheit gibt es noch: Bei der Zuweisung von Variablen wird kein $-Zeichen verwendet, bei der Referenzierung schon!

Obiges kann auch in eine Zeile geschrieben werden. Dann muß aber jeder Befehl durch ein Semikolon getrennt werden:

    for i in *; do echo $i; done

Das kann natürlich direkt auf der Kommandozeile ausgeführt werden

Relevant werden die Programmierfähigkeiten der Shell bei den Shellskripten. Diese sind besonders wichtig in Form der Startskripte beim Booten.

Umgebungsvariablen sowie Umlenkung der Ein- und Ausgabe

Die Shell kennt auch Umgebungsvariablen (Environment), das sind einfach nur gesetzte Variablen, diese werden aber von bestimmten Programmen gesondert ausgewertet. Die Shell selber nutzt diese ebenfalls, z.B. setzt PATH den Suchpfad für Programme. Diese Variable enthält eine Liste von Verzeichnissen die durch einen Doppelpunkt getrennt sind.

Soll z.B. der Suchpfad um das jeweils aktuelle Verzeichnis erweitert werden, so geht dies mit

PATH=$PATH:.

Allerdings gilt dies nur für Programme die direkt aus der aktuellen Shell gestartet werden. Wird einfach eine neue Shell durch Aufruf von bash gestartet, so ist der alte Wert wieder vorhanden, z.B:

geschke$ echo $TEST

geschke$ TEST=true
geschke$ echo $TEST
true
geschke$ bash
geschke$ echo $TEST

geschke$

Soll TEST auch in Unterprogrammen gesetzt sein, so muß diese Variable exportiert werden:

geschke$ echo $TEST
true
geschke$ export $TEST
geschke$ bash
geschke$ echo $TEST
true
geschke$

Bei der bash kann dies auch alles in eine Zeile geschrieben werden:

geschke$ export TEST=true
< : lesen aus einer Datei
> : Schreiben in eine Datei, es wird eine alte gelöscht.
>>: Anhängen an eine existierende Datei
| : Pipe, die Ausgabe des 1. Programms ist die Eingabe des 2.
2>: Die Fehlermeldungen werden in eine Datei umgelenkt
2>&1: Die Fehlermeldungen werden mit der normalen Ausgabe zusammengelegt

Z.B.:

  ls -l | less

Dieser Befehl listet die Dateien im aktuellen Verzeichnis im Langformat (-l) auf und die Ausgabe wird in das Programm less geschrieben. less ist ein sogenannter Pager, d.h. es kann mit ihm seitenweise die Ausgabe angeschaut werden, ansonsten würde alles auf dem Bildschirm ausgegeben.

 ls -l > Listing

Dies schreibt obige Ausgabe in die Datei Listing. Diese könnte dann mit less Listing ebenfalls angesehen werden. Hier wird der Vorteil der Pipe offensichtlich, es können ein Zwischenschritt und eine Datei gespart werden.

 less < Listing 

zeigt natürlich auch den Inhalt von Listing im Pager an.

 ls -l >> Listing

hängt die Ausgabe von ls an die Datei an, es ist dann alles doppelt in der Datei.

Mit 2> können explizit die Fehlermeldungen in eine Datei umgelenkt werden, 1> ist der default: die Standardausgabe. Die Funktionsweise der folgenden Zeile ist dann wohl offensichtlich:

  /usr/bin/Programm > Programm.out 2> Programm.err

Es besteht nun auch die Möglichkeit die beiden "Kanäle" zu bündeln:

  /usr/bin/Programm > Programm.outerr 2>&1 

Jetzt werden die Fehlermeldungen in die gleiche Datei umgelenkt wie die normale Ausgabe. Wichtig ist hierbei die Reihenfolge! Diese Zeile hier macht etwas anderes als die Vermutung nahelegt:

  /usr/bin/Programm 2>&1 > Programm.out

Jetzt werden die Fehlermeldungen auf die normale Ausgabe umgelenkt. Das ist in diesem Fall der Bildschirm! Die normale Ausgabe wird aber anschließend in eine Datei umgelenkt. Das kann hilfreich sein, wenn z.B. nur die Fehlermeldungen via Pipe weiterverarbeitet werden sollen, z.B.:

  /usr/bin/Programm 2>&1 > Programm.out | less

Aliase, Funktionen, Builtins

Aliase funktionieren wie es der Name vermuten läßt:

  alias la='ls -la'

Schon gibt es das Kommando la welches nichts anders macht als ein ls -la auszuführen.

Mit Aliasen können auch Befehle "überschrieben" werden:

  alias ls='ls --color=auto'

Dies bewirkt, dass bei einem ls alles entsprechend des Dateityps eingefärbt wird.

Mit

  unalias ls

wird dieser Alias wieder gelöscht, ein nacktes alias, d.h. ohne Argumenten, listet alle gesetzten Aliase auf.

Funktionen sind ebenfalls leicht zu erstellen, z.B.:

  listDG() { ls $1; }

$1 ist dabei das erste Argument der Funktion.

Hier wird auch die Funktionsweise der Hochkommata ersichtlich:

majestix:~$ listDG h* 
highlight-2.2-1.tar.bz2

majestix:~$ listDG "h*"
highlight-2.2-1.tar.bz2  honeyd-man.pdf  htop-0.3.2.tar.gz

Im ersten Fall expandiert die Shell das h* zu

  listDG highlight-2.2-1.tar.bz2 honeyd-man.pdf htop-0.3.2.tar.gz

In dem Fall ist dann $1 = highlight-2.2-1.tar.bz2, also das erste Argument.

Bei listDG "h*" wird der Stern nicht von der Shell expandiert, d.h. $1 = "h*" bleibt und nun listet die Funktion alle Dateien auf die mit h beginnen.

Der Unterschied zwischen dem einfachen (') und dem doppelten (") Hochkomma bezieht sich auf die Auswertung von Variablen. Das wird an einem Beispiel am deutlichsten:

majestix:~$ TEST=true
majestix:~$ echo "$TEST" 
true
majestix:~$ echo '$TEST'
$TEST

In einfachen Hochkommata werden die Variablen nicht ersetzt.

Der letzte Punkt sind builtins, dies sind eingebaute Funkionen in der Shell. Sie haben zum Teil die gleichen Namen wie Programme, z.B. pwd:

majestix:~$ ln -s /tmp 
majestix:~$ cd tmp
majestix:~/tmp$ pwd
/home/geschke/tmp
majestix:~/tmp$ /bin/pwd 
/tmp

Es fällt auf, dass mit dem Pfad aufgerufene Programme just diese sind, keine builtins.

Ein builtin heißt übrigens test, daher sollte nach Möglichkeit ein selbstgeschriebenes Programm nicht test genannt werden, sonst kann oft vergeblich danach gesucht werden warum es nicht so läuft, wie es erwartet wird wenn es mit dem Pfad aufgerufen wurde.

Aufbau des Dateisystems

Der Aufbau hat eine Baumstruktur woher sich auch die Bezeichnung root als Anfang ergibt. Verzeichnisse werden durch Slashes "/" getrennt, das oberste Verzeichnis ist nur ein Slash "/" und heißt root-Verzeichnis.

Dies ist das erste Verzeichnis, das beim Booten gemounted ("montiert") wird. Bei Modulen gibt es noch initrd, die initial ram disk die ein Bestandteil des Kernels ist und Module enthalten kann die für das Mounten des root-Verzeichnisses notwendig sind. Dies wird in diesem Fall zuerst gemounted. Das ist aber ein Sonderfall und nicht unbedingt notwendig.

Andere Laufwerke oder Partitionen können einfach in das System ein- gehängt (gemounted) werden. Dazu kann jedes Verzeichnis als Einhängepunkt (Mount-Point) verwendet werden. Einige Verzeichnisse müssen aber auf der root-Partition liegen!

Diese sind im Folgenden mit (r) gekennzeichnet:

/bin (r): (eigentlich) statisch gelinkte Userprogramme
/boot: Bootdateien, Kernel
/dev (r): Devices, Schnittstellen zur Hardware
/etc (r): die Konfigurationsdateien liegen meistens hier
/home: Benutzerverzeichnisse
/initrd: Inital ram disk
/lib(r): Kernel-Module /lib/modules/{kernel.version}/..., 
         Bibliotheken die beim booten benötigt werden für /bin, /sbin
/mnt: temporärer Mount-Point, normalerweise ein leeres Verzeichnis
/opt: optionale Software
/proc: Informationen über Prozesse
/root: Homeverzeichnis des Systemadministrators root
/sbin(r): (eigentlich) statisch gelinkte Programme für root
/sys: Kernel-Informationen, sysctl
/tmp: Verzeichnis für temporäre Dateien
/usr: Programme für Benutzer
/usr/local: Lokale Erweiterungen, nicht Bestandteil des Grundsystems
/usr/share: Architekturunabhängige Dateien
/usr/share/man: Manual pages, man1-man9
/var: variabel, Änderbare Daten zur Laufzeit liegen hier

Das /proc-Verzeichnis soll nur Informationen über die Prozesse beinhalten, diese liegen innerhalb von /proc in den Verzeichnissen die der Prozess-ID entsprechen.

Einen Sonderfall stellt /proc/self dar, es zeigt immer auf den eigenen Prozess.

Im /proc-Dateisystem stehen auch Kernel-Einstellungen. Diese haben hier eigentlich nichts zu suchen. Das /proc-Verzeichnis hat sich aber als gut geeignete Schnittstelle zu den Kernel-Parametern herausgestellt.

Seit Kernel 2.6 sind diese nach /sys verschoben worden. Aus Gründen der Kompatibilität existieren die Einträge im /proc-Verzeichnis jedoch noch immer.

Unterhalb von /usr/local gibt es fast die gesamte Hierarchie noch einmal. Diese ist für eigene lokale Erweiterungen des Systems gedacht um diese klar von der Distribution oder bei kommerziellen Unixen von den Herstellerprogrammen zu trennen. Allerdings halten sich die meisten Distributionen nicht immer daran.

Arten von Dateisystemen

Es gibt verschiedene Klassen von Dateisystemen (Filesysteme).

Zu den klassischen Dateisystemen zählen z.B.: minix, ext2, msdos. Das größte Problem mit diesen Dateisystemen besteht darin, dass bei einem Crash (Kernelabsturz oder Stromausfall) die Integrität des Dateisystems nicht mehr gegeben ist. D.h. es muss hier das gesamte Dateisystem nach Inkonsistenzen durchsucht werden. Bei Festplatten aktueller Größe kann das schon einmal ein paar Stunden dauern.

Aus dieser Not entstanden die journaling Dateisysteme wie ext3, jfs, reiserfs, xfs. Diese führen ein Journal in dem vor der Ausführung einer Aktion auf der Festplatte diese dort vermerkt wird. Nach der Beendigung wird der Vermerk aus dem Journal entfernt.

Findet nun ein Crash statt, so müssen nur noch die offenen Bereiche die im Journal noch vermerkt sind auf Inkonsistenzen überprüft werden. Ein Dateisystemcheck dauert jetzt lediglich ein paar Sekunden.

Jfs ist das erste Journaling Dateisystem von IBM, es ist aber erst recht spät in den Linux-Kernel aufgenommen worden. Das ext3 ist nur eine Erweiterung des ext2-Systems um ein Journal. Der Vorteil ist, dass ein ext2-System problemlos in ein ext3 umgewandelt werden kann. Das geht übrigens auch umgekehrt!

Xfs ist das Dateisystem von SGI, dass sich bei Irix bewährt hat. Es gilt als das Schnellste aller journaling Dateisysteme.

Reiserfs war das erste als stabil deklarierte journaling Dateisystem von Linux. Es ist bei vielen kleinen Dateien recht performant. Ein Nachteil war aber, dass es eigentlich am Anfang nicht stabil war, da hatte es wohl jemand zu eilig gehabt es in den Kernel zu bekommen. Ein Crash eines reiserfs Systems führte nicht selten zum Verlust des gesamten Festplatteninhaltes! Von daher hat dieses Dateisystem bei vielen noch einen schlechten Ruf. Mittlerweile scheint es aber wirklich stabil zu sein.

Die bislang beste Erfahrung habe ich noch immer mit xfs gemacht, dies setze ich seit weit über 6 Jahren ohne irgendwelche Probleme ein.

Es gibt dann noch die Speicher (Memory) Dateisysteme, shfs sowie eine ramdisk. Das shfs wird häufig für das /tmp-Verzeichnis verwendet. Die Dateien die hier normalerweise abgelegt werden sind nur temporäre Auslagerungen von Programmen. D.h. die Wahrscheinlichkeit ist recht hoch, dass diese vom Programm auch wieder gelesen werden. Das erhöht dann natürlich die Ausführungsgeschwindigkeit denn es gibt wohl kein schnelleres Dateisystem als eines das im Hauptspeicher liegt.

Dann existieren noch Pseudo-Dateisysteme: procfs, devfs, usbfs, sysfs. Dies sind keine Dateisysteme die auf einer Festplatte oder im Hauptspeicher abgebildet werden sondern sind rein virtuelle Dateisysteme die der Kernel bereitstellt.

Das procfs wird für die Bereitstellung der Prozessinformationen verwendet, das sysfs für die Kernelparameter und devfs ist eigentlich für die Devices gedacht. Die Dateien in diesem Verzeichnis stellen eine Kernel-Schnittstelle zur Hardware dar. Allerdings hat sich dieses Dateisystem bislang noch nicht durchgesetzt.

Das usbfs ist ein Dateisystem, dass der Kernel bereitstellt und das Auslesen von USB-Informationen erlaubt. Das Programm lsusb wertet dieses aus.

Und schließlich existieren noch die Netzwerkdateisysteme NFS, SMB/CIFS, AFS und Coda.

NFS, das Network File System ist das älteste Netzwerk Dateisystem und stammt von Sun. Es ist heute noch das Standard Dateisystem für Netzwerke innerhalb von Unixsystemen.

AFS ist gewissermassen eine Erweiterung von NFS, es benutzt Kerberos und ACL's für die Sicherheit sowie lokale Caches.

Lokale Caches verwendet auch Coda wodurch Dateien z.B. auf einem Notebook gecached und später beim reconnect des Notebooks mit dem Server abgeglichen werden.

SMB/CIFS ist das hauptsächlich von Microsoft verwendete Protokoll für ein Netzwerk-Dateisystem. SMB steht für Server Message Block. Das Linuxpaket für dieses Dateisystem heißt samba, der Name war der Erste in einem Lexikon der die Buchstaben S, M und B enthielt.

CIFS ist das Common Internet File System und ist die Weiterentwicklung von SMB. Konsequenter Weise beherrscht samba dieses Dateisystem ebenfalls, es ist zur Abwechslung einmal ein offener Standard von Microsoft, siehe

http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html

Allerdings hat es dieser Draft nie zu einem offiziellen Standard geschafft, der Draft ist obendrein expired.

Die verschiedenen Dateiarten

Da die Philospohie von Unix besagt, dass (fast) alles eine Datei ist, so gibt es auch enstsprechend reichlich verschiedene Dateitypen.

Das Programm /bin/ls (list) mit der Option -l liefert eine Ausgabe des aktuellen Verzeichnisses in der Langversion. Dabei gibt das erste Zeichen einer jeden Zeile Auskunft über die Dateiart:

Ein hilfreiches Kommando ist file, dies gibt an um welche Art von Datei es sich handelt und bei normalen Dateien welche Form von Daten enthalten sind, z.B.

majestix:~$ file Lion_seul.mpg 
Lion_seul.mpg: MPEG system stream data

Wobei in diesem Fall die Endung schon helfen würde. Aber file ist ein bischen intelligenter, es analysiert die (ersten) Bytes der Datei und versucht anhand von sogenannten Magic-Codes zu bestimmen was für eine Datei es wirklich ist. Ein simples Umbenennen hilft hier nicht um den Typ zu ändern im Gegensatz z.B. bei einem anderen bekannten Betriebssystem:

majestix:~$ mv Lion_seul.mpg Lion_seul.jpg
majestix:~$ file Lion_seul.jpg 
Lion_seul.jpg: MPEG system stream data

Die Dateien sind zusätzlich mit Zugriffsrechten versehen. Hier gibt es normalerweise drei verschiedene Bereiche. Das sind die nächsten neun Zeichen bei einem ls -l. Die ersten drei Zeichen sind für den Eigentümer der Datei, die nächsten für die Gruppe und die letzten für alle anderen.

Der Name des Eigentümers muss in der Datei /etc/passwd stehen, ansonsten steht hier nur eine User ID. Analog ist das mit der Gruppe, diese muss in /etc/group stehen. Der Rest bezieht sich auf alle die nicht in die ersten beiden Bereiche fallen.

Das erste Zeichen ist ein r (read) für Leserechte oder ein - für nicht lesbar, das nächste ist ein w (write) für die Schreibrechte und das dritte ist für ausführbare Dateien: x (execute).

Bei Verzeichnissen werden die Leserechte benötigt um das Verzeichnis lesen zu können. Aber zusätzlich wird hier das x benötigt um in das Verzeichnis wechseln zu können. Allein mit Leserechten können nur die Programmnamen im Verzeichnis erhalten werden, keine weiteren Informationen wie Größe oder Datum.

Allein mit x-Rechten kann zwar in das Verzeichnis gewechselt werden, es besteht aber nicht die Möglichkeit die Dateien aufzulisten.

Es gibt noch zwei Sonderzeichen, zum einen das SUID (set user ID, also setze die Benutzer ID) bzw. SGID (set group ID, setze die Gruppen ID): s. In diesem Fall erhält der ausführende eines Programmes die Rechte entweder des Besitzers oder der Gruppe. Dies funktioniert aber nicht bei Shellskripten!

Das andere Sonderzeichen ist das sticky (klebrige) Bit. Der Name rührt daher, dass Programme mit diesem Bit im Speicher verbleiben sollen, sie kleben am RAM. Dank Paging-Mechanismen, das geschickte Auslagern einzelner nichtbenötigter Bereiche, ist das nicht mehr notwendig und funktioniert demzufolge bei Dateien nicht mehr.

Allerdings hat dieses Bit bei Verzeichnissen noch eine Bedeutung: Ist das t-Bit gesetzt, dann darf entgegen der üblichen Zugriffsrechte nur derjenige eine Datei verändern/löschen der sie auch angelegt hat. Das ist für temporäre Verzeichnisse wie /tmp oder /var/tmp wichtig. Dieses Verzeichnis darf per default jeder beschreiben, also Dateien editieren oder löschen.

Damit wird auch die Bedeutung des Schreibrechts w für Verzeichnisse deutlich. Nur wer dieses hat, darf Dateien anlegen oder löschen.

Und zu guter letzt gibt es auch noch ACL's, hier können die Rechte granularer vergeben werden als via Eigentümer/Gruppe/Rest. Da diese Rechte aber bei einem normalen ls -l nicht angezeigt werden sind sie auch nicht sehr beliebt. Es ist lediglich im Langformat anhand des Plus-Zeichens nach den klassischen Zugriffsrechten zu erkennen, dass ACLs gesetzt sind.

Die ACLs können mit setfacl gesetzt und mit getfacl ausgelsen werden, z.B.:

majestix:~$ setfacl -m u:lug:- Datei
majestix:~$ getfacl Datei 
# file: Datei
# owner: geschke
# group: geschke
user::rw-
user:lug:---
group::r--
mask::r--
other::r--

majestix:~$ ls -l Datei 
-rw-r--r--+ 1 geschke geschke 23 2008-11-18 17:51 Datei

Dass diese ACLs auch wirklich greifen kann leicht getestet werden indem die User-ID von lug angenommen wird:

majestix:~$ su lug
Password: 
lug@majestix:/home/geschke$ ls -l Datei 
-rw-r--r--+ 1 geschke geschke 23 2008-11-18 17:51 Datei
lug@majestix:/home/geschke$ cat Datei
cat: Datei: Keine Berechtigung

Der Hauptgrund für diese erweiterten Rechte liegt wohl darin, dass Windows solche Granularitäten zuläßt. (Oder auch nicht, es gibt viele Programme unter Windows die diese gesonderten Rechte einfach ignorieren, siehe z.B.: http://security.tombom.co.uk/shatter.html)

Der Bootprozess


Der Bootloader sitzt gewöhnlich in den ersten 512 Bytes einer Festplatte, Disktte oder CD-Rom. Hierin befindet sich auch noch die Partitionstabelle.

Daher ist verständlich, dass die meisten Bootloader in mehr als einem Schritt arbeiten müssen. In der ersten Stufe wird nur ein minimaler Bootloader geladen, der in der zweiten Stufe den eigentlichen Bootloader nachlädt. Dieser liegt gewöhnlich in /boot.

Dieser lokalisiert den Kernel und lädt ihn in den Hauptspeicher wo er entpackt wird, je nach Art des Kernels an unterschiedliche Stellen, make zImage versus make bzImage.

Typische Bootloader von Linux sind lilo (linux loader) und grub (grand unified bootloader). Der erste ist der älteste und hatte lange Zeit Probleme bei IDE-Festplatten den Kernel jenseits von Zylinder 1024 zu booten. Daher war /boot meist die erste kleine Partition bzw. sie wurde daher überhaupt eingeführt. Grub stammt aus dem GNU-Hurd Projekt und besitzt auch die Fähigkeit über das Netzwerk zu booten.

Das erste was der Kernel durchführt ist die CPU und den Hauptspeicher (RAM) zu initialisieren. Danach werden die diversen Hardware-Bestandteile erkannt und initialisiert. Dazu muss der entsprechende Treiber auch im Kernel enthalten sein. Die Kernelmeldungen können mit dem Programm dmesg ausgegeben werden und geben manchmal einen Hinweis darauf warum etwas nicht korrekt funktioniert.

Wenn Kernelmodule beim Booten benötigt werden dann kann eine ''Initial Ramdisk'' (initrd) gemounted werden. Über diese können Module geladen werden die z.B. notwendig sind um die Root-Partition zu mounten. Die Verwendung des initrd muss aber explizit in den Kernel kompiliert werden.

Der nächste Schritt besteht im Mounten der root-Partition (/) und dem Starten des init Prozesses. Dieser ist immer der erste (Userland-) Prozess der gestartet wird und hat daher die Prozess-ID (PID) 1:

majestix:~$ ps waux |head -2
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  1492  176 ?        S    Aug10   0:07 init [2]       

Das gilt auch wenn bereits Kernelprozesse die vorher gestartet wurden in der Prozesstabelle stehen. Die PID von init bleibt traditionell bei eins.

ps gibt die Prozessliste aus, die Optionen w für weite, also lange Ausgabe, a für alle Prozesse, u für die Benutzerangabe (user) und x für Daemonen bzw. Prozesse ohne controlling tty. Dies ist die BSD-Syntax, die SysV-Syntax gibt es auch, dann werden die Optionen mit - angegeben, z.B.:

majestix:~$ ps -ef |head -2
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Aug10 ?        00:00:07 init [2]       

Aber meistens ist die BSD-Variante besser zu lesen und ist daher meist beliebter.

Der Wert am Ende in Klammern gibt den aktuellen Runlevel an. Dieser wird normalerweise unter Unix mit who -r ausgegeben:

majestix:~$ who -r
         run-level 2  Aug 10 12:11                   last=S  

Früher gab es diese Funktion aber noch nicht in who (bei Linux!). Dafür existiert dann das Programm runlevel.

Das geht natürlich nur bei SysV basierten Systemen, BSD-Systeme haben keinen Runlevel...

Der Init-Prozess

Der Prozess der als erstes gestartet wird ist der init-Prozess. Dieser wertet die Datei /etc/inittab aus (SysV, BSD startet direkt /etc/rc)

Es gibt auch BSD-artige init's bei Linux, insbesondere Slackware verwendete dieses Konzept.

Heutzutage verwenden alle Linux-Distributionen die SysV-Version von init die über /etc/inittab gestartet wird. Diese wird sequentiell abgearbeitet. Slackware verwendet auch diese Version um dann über /etc/inittab das BSD-Verhalten zu simulieren.

Die ersten 2 Zeichen sind nur ein Identifier der eigentlich nicht mehr verwendet wird aber aus historischen Gründen noch existiert. Bei tty's wurde das 2. Zeichen, dass eine Ziffer sein mußte als Nummer des Devices verwendet, also z.B. aus T3 folgte das Device ttyS3.

Der zweite Wert ist der Runlevel, der dritte die Aktion, die init ausführen soll und der letzte Eintrag ist der Prozess der von init gestartet werden soll:

Dies hier legt nur den gewünschten Runlevel fest.

# The default runlevel.
id:2:initdefault:

Die nächsten Zeilen werden immer ausgeführt, da hier hier die Aktion sysinit steht.

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

D.h. bei jedem Boot wird das Shellskript /etc/init.d/rcS gestartet:

majestix:~$ file /etc/init.d/rcS
/etc/init.d/rcS: Bourne shell script text executable

Das Skript lädt ein paar Voreinstellungen und startet dann alle Skripte in /etc/rcS.d/ die mit S?? anfangen mit der Option start:

#
#       Call all parts in order.
#
for i in /etc/rcS.d/S??*
do
        # Ignore dangling symlinks for now.
        [ ! -f "$i" ] && continue

        case "$i" in
                *.sh)
                        # Source shell script for speed.
                        (
                                trap - INT QUIT TSTP
                                set start
                                . $i
                        )
                        ;;
                *)
                        # No sh extension, so fork subprocess.
                        $i start
                        ;;
        esac
done

$i enthält hier die Dateinamen, die case-Zeile nimmt eine Fallunterscheidung vor: Endet die Datei auf .sh, so wird die Datei gesourced, d.h. es handelt sich vermutlich um ein Shellskript und das Starten einer zusätzlichen Shell wird gespart was dann schneller abläft. Auf diese Weise können aber auch Umgebungsvariablen über Shellskripte gesetzt werden die für die anderen Shellskripte dann gesetzt sind. * paßt auf alles andere. Da $i den Dateinamen mit vollem Pfad enthält funktioniert hier auch der Aufruf.

Eigentlich müßte es S[0-9][0-9]* heißen, also nur die Skripte starten, die mit S und zwei Ziffern anfangen. Die Abarbeitung erfolgt in der Reihenfolge der Nummern, also S01 vor S99. Die Sortierung erledigt die Shell automatisch durch die Expansion.

Die Dateien in /etc/rcS.d/ sind gewöhnlich symbolische Links auf die eigentlichen Start- bzw. Stoppskripte in /etc/init.d/.

Der nächste Eintrag wird nur ausgeführt wenn in den sogenannten Singleuser-Modus gestartet wird. In diesem Modus kann sich gewöhnlich nur der Benutzer root einloggen, es gibt dann meist auch kein Netzwerk oder virtuelle Konsolen. Da /etc/rcS.d/ (s.o.) vorher ausgeführt wird kann das Verhalten natürlich geändert werden, daher auch das Wort meist:

# What to do in single-user mode.
~~:S:wait:/sbin/sulogin

Dies wird meistens nur für Systemarbeiten benutzt, wenn z.B. andere Benutzer explizit das System nicht benutzen sollen, z.B. wenn die Home-Verzeichnisse auf eine neue Partition verschoben werden sollen. Da wäre es nicht gut, wenn gleichzeitig User arbeiten würden. Oder es wird neue Hardware installiert und konfiguriert. Bevor dann bei jedem Reboot die Benutzer aufgefordert werden müssen sich wieder auszuloggen und die Arbeit zu beenden ist es besser sie explizit nicht zuzulassen. Ein Vorteil ist auch, dass die meisten Startskripte nicht mehr ausgeführt werden, der Login-Prompt erscheint deutlich schneller.

Die nächsten Zeilen sind für die verschiedenen Runlevel, es wird einfach nur das Skript /etc/init.d/rc mit dem Runlevel aufgerufen:

l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6

Bei einem Runlevel von 2 wird nur /etc/init.d/rc 2 ausgeführt. wait gibt hierbei an, dass init warten soll bis alle Skripte abgearbeitet worden sind bevor die Weiterverarbeitung fortschreitet.

/etc/init.d/rc 2 startet alle S??* Skripte in /etc/rc2.d/ mit der Option start, wie es zu erwarten war.

Weiter geht es mit der Definition was bei der Tastenkombination CTRL-ALT-DEL passieren soll:

# What to do when CTRL-ALT-DEL is pressed.
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now

Diese Tastenkombination wird aber gewöhnlich vom X11-Server abgefangen.

Weiter geht es mit dem Starten der virtuellen Konsolen:

1:2345:respawn:/sbin/getty 38400 tty1
2:23:respawn:/sbin/getty 38400 tty2
3:23:respawn:/sbin/getty 38400 tty3
4:23:respawn:/sbin/getty 38400 tty4
5:23:respawn:/sbin/getty 38400 tty5
6:23:respawn:/sbin/getty 38400 tty6

D.h. die virtuellen Konsolen gibt es nur im Runlevel 2 oder 3. Der respawn-Eintrag besagt, dass die Programme, in diesem Fall getty, neu gestartet werden sollen wenn sie sich beenden.

Das Programm getty öffnet einen Terminal-Port tty, der Name geht auf die Anfänge zurück als die Server noch über Teletyper also Fernschreiber angebunden waren. Die Tastatur war der Fernschreiber, der Bildschirm war einfach das gedruckte Papier. Dieser Prozess präsentiert einen Login-Prompt. Das Umschalten zwischen den Text-Konsolen funktioniert mit ALT-F1 bis ALT-F6, es werden oben 6 Terminal-Sitzungen gestartet.

Auf der nächsten sitzt gewöhnlich der X11-Server, d.h. mit ALT-F7 wird zum X11-Bildschirm gewechselt. Der Wechsel vom X11-Bildschirm auf die erste Konsole funktioniert mit der Tastenkombination CTRL-ALT-F1. Der Grund liegt einfach darin, dass unter X11 viele Programme die ALT-F? Kombinationen anderweitig nutzen.

Dann gibt es noch zwei weitere interessante Einstellungen:

# Example how to put a getty on a serial line (for a terminal)
#
T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100

Dies startet in den Runleveln 2 und 3 einen getty auf der seriellen Leitung, d.h. es besteht die Möglichkeit sich dann z.B. via Nullmodemkabel einzuloggen.

Sollen die Kernelmeldungen auf die serielle Konsole umgelenkt werden, so muß das im Kernel einkompiliert sein und via append-Zeile aktiviert werden. Ferner sollte dann auch der Bootloader auf die serielle Leitung umgestellt werden.

Und schließlich:

# Example how to put a getty on a modem line.
#
#T3:23:respawn:/sbin/mgetty -x0 -s 57600 ttyS3

Dies würde ein Modem an ttyS3 ansteuern. Der mgetty würde auf eingehende Anrufe warten und dann einen Login-Prompt via Modem präsentieren.

Informationsquellen

Zum einen gibt es die klassischen Handbuchseiten (manual pages), diese existieren schon seit der allerersten Unix-Version und sind immer einheitlich aufgebaut. Das Format ist nroff und wird mit der speziellen Option -man aufgerufen um die Datei in das bekannte, lesbare Format umzuwandeln. Dieses erledigt aber das man-Kommando automatisch.

Die manual pages liegen gewöhnlich unter /usr/share/man/, dort gibt es für jede Sektion ein Unterverzeichnis man1-man9 in dem die eigentlichen Seiten im nroff-Format liegen.

Der Aufbau ist nahezu immer derselbe, zuerst erfolgt der Name als Einzeiler mit kurzem Kommentar. Danach kommt eine kurze Auflistung in welcher Form z.B. das Programm aufgerufen werden kann (SYNOPSIS).

Anschließend folgt eine ausführlichere Beschreibung sowie eine Auflistung der Optionen mit deren Funktion. Zum Schluss gibt es noch die Autoren, eine Liste von Handbuchseiten die verwandt sind (SEE ALSO) sowie ein Abschnitt mit bekannten Fehlern (BUGS). Letzterer ist insofern interessant, dass sie überhaupt existiert. Die meisten Systeme verschweigen lieber die bekannten Fehler. Hier steht aber auch oft, wie die Fehler umgangen werden können oder warum sie nicht behoben wurden: Manchmal ist es nicht klar, wie dies erfolgen sollte.

Es besteht auch die Möglichkeit nur nach einem Stichwort zu suchen. Dafür bieten sich die zwei Programme man -k Stichwort bzw. apropos Stichwort an, z.B.:

majestix:~$ man -k fifo
fifo (4)             - first-in first-out special file, named pipe
mkfifo (1)           - make FIFOs (named pipes)
mkfifo (3)           - make a FIFO special file (a named pipe)
perlipc (1)          - Perl interprocess communication (signals, fifos, 
                       pipes, safe subprocesses, sockets, and semaphores) 

Das erste ist das Programm, Bibliotheksaufruf oder Dateiname der manual page basierend auf der Sektion, diese ist in runden Klammern angegeben. Danach folgt die Kurzbeschreibung.

Existiert eine Handbuchseite in verschiedenen Sektionen (z.B. hier mkfifo) so kann beim Aufruf der manual page diese Sektion einfach mitangeben werden:

  man 3 mkfifo

Es gibt bei Linux 9 Sektionen die nach den Kapiteln des ersten Handbuch über Unix benannt wurden. Die Sektion 9 ist linuxspezifisch, die Sektion 6 kam mit BSD ins Spiel:

  1. Benutzerbefehle, Proramme für normale Benutzer
  2. System Calls, also Schnittstellen zum Kernel
  3. Bibliotheken, Routinen die im Userland (nicht im Kernel) laufen
  4. Spezielle Dateien
  5. Dateiformate, hautpsächlich der Aufbau der Konfigurationsdateien
  6. Spiele, im wesentlichen die BSD-Games
  7. verschiedenes, alles was woanders nicht paßt, z.B. charsets
  8. Befehle für den Superuser, sie sind nur für root interessant
  9. Kernel-Routinen, eine Linux-Besonderheit.

Beenden kann man die Manual-Pages mit q. Das ist übrigens ein gutes Beispiel für die Verwendung verschiedener Programme. nroff wird verwendet um die Datei in das lesbare Format umzuwandeln, angezeigt werden diese mit less bzw. dem Programm das in der Umgebungsvariablen PAGER angegeben ist. Solaris verwendet per default more, das bietet nicht die Möglichkeit wieder nach oben zu blättern, ein PAGER=less bewirkt dann ein Verhalten wie bei Linux.

Da die manual pages aus den Urzeiten von Unix stammen gab es auch Bestrebungen, hauptsächlich von der FSF, der Free Software Foundation, bekannt für GNU, ein besseres System zu verwenden: Die info Seiten, diese sind querverlinkt ähnlich wie HTML. Allerdings sind diese nicht unbedingt gut, d.h. intuitiv zu bedienen.

Der Aufruf info --apropos Suchbegriff hat ein ähnliches Verhalten wie man -k Suchbegriff, z.B.:

majestix:~$ info --apropos "stream editor"
"(gawk)Simple Sed" -- stream editors <1>
"(gawk)Field Splitting Summary" -- stream editors
"(sed)Introduction" -- Stream editor

Mit CTRL-X CTRL-C kann das Programm info verlassen werden, in diesem Fall ist es ein emacs. Kein Wunder, es kommen emacs und info pages aus dem gleichen Stall(man).

Eine weitere meist sehr gute Quelle für Informationen sind die FAQ's (Frequently Asked Questions, häufig gestellte Fragen). Die Wurzeln gehen auf die Anfänge von Linux in newsgroups zurück. Hier wurden häufig immer die gleichen Fragen gestellt, die schon mehrfach beantwortet worden sind. Daraufhin haben sich einige bereit erklärt diese Fragen und Antworten zu sammeln und zu veröffentlichen.

Jeder sollte dann erst in eine FAQ schauen bevor er eine Frage stellt. Im anderen Fall gibt es meist unschöne Antworten wie RTFM. Aus den FAQ's wurden dann später Anleitungen wie etwas z.B. einzurichten ist. Das sind die HOWTO's (Wie geht es). Im Linux-Umfeld sind diese unter:

http://www.tldp.org/

zu finden, the linux documentation project.

Informationen den Kernel betreffend und insbesondere wie diverse Hardware angesprochen/konfiguriert werden kann befindet sich im Kernel-Baum. Diese liegt gewöhnlich unter

/usr/src/linux/Documentation

und enthält Textdateien.

Eine weitere Quelle sind manchmal die Konfigurationsdateien selber, diese sind oft ASCII-Dateien und meistens sind die Einträge mit erläuternden Kommentaren versehen.

Schließlich gibt es noch

http://www.google.de/

und letztendlich den Standard des Internets: RFC's (request for comments). Dieses können unter

http://www.rfc-editor.org/

gefunden werden oder unterhalb von http://www.ietf.org/. Hier gibt es verschiedene Stufen von Draft, Informal bis Standard. Sie sind von der Internet Engeneering Task Force (IETF) ins Leben gerufen worden um den Standart des Internets verbindlich festzulegen.

Jeder hat sich an diese Standards zu halten! Nur leider gibt es Firmen, die dies nicht tun und es gibt keine Instanz die diese Einhaltung überwacht oder sogar erzwingen würde.

Wer sich aber daran hält, der darf sich RFC-konform nennen.

Hier wird z.B. der Aufbau der Protokolle IP, ICMP, TCP, UDP, HTTP, SMTP, ... festgehalten.

Kommandozeilentools

Zuerst einmal ein paar Anmerkungen zu den Kommandozeilentools.

Die meisten Kommandozeilentools verwenden kurze Optionen (1 Zeichen) und/oder lange Optionen. Da klassischerweise Optionen mit einem - Zeichen anfangen und kombiniert werden können, mußte für die Langfassung ein neues Symbol verwendet werden. Der Einfachheit halber sind das 2 -- Zeichen.

Z.B. kann statt

  ps -e -l -f

auch

  ps -elf

geschrieben werden.

Bei der Langfassung gibt es bei nahezu allen Programmen eine Option um eine kurze Hilfe zu bekommen: --help, bei der Kurzfassung ist dies oft -h, allerdings kommt dann auch oft die Ausgabe: unbekannte Option, versuche --help.

Manche Programme verzichten auch auf das Minus-Zeichen, so z.B. tar oder ps. Bei letzterem wiederum wird dadurch zwischen zwei Modi unterschieden, mit Minus-Zeichen ist es SysV-Syntax und ohne die BSD-Syntax. Entsprechend unterschiedlich wirken auch die Optionen.

Beenden bzw. Abbrechen von Programmen kann meistens auf verschiedene Weisen erfolgen:

Dateien und Verzeichnisse

Hier werden nur einige der wichtigsten Tools vorgestellt. Die Wichtigsten sind gewöhnlich die Kürzesten (2 Buchstaben).

cp
copy, kopieren von Dateien
mv
move, verschieben oder umbenennen von Dateien
rm
remove, löschen von Dateien, nicht Verzeichnissen
ln
link, Verweis auf eine andere Datei
cd
change directory, wechseln eines Verzeichnisses
mkdir
make directory, erstellen eines Verzeichnisses
rmdir
remove directory, löschen eines Verzeichnisses, dieses muß vorher leer sein!
ls
list, auflisten eines Verzeichnisinhaltes
find
suchen einer Datei nach gegebenem Muster
tar
tape archiver, eigentlich um ein Archiv auf ein Band zu schreiben, wird für Backup-Zwecke verwendet und kann in eine Datei statt Band schreiben.
cpio
copy in/out, auch eine Form des Archivierens, dies ist etwas aus der Mode gekommen.
dd
eigentlich copy command, aber cc war schon vergeben, dd kopiert Daten, hat aber eine seltsame Syntax basierend auf IBM's 'JCL'
file
gibt Auskunft über Art der Datei, meistens Anhand der ersten magischen Bytes der Datei
diff
Zeigt die Differenz, d.h. den Unterschied zweier Textdateien, bei Binärdateien gibt es nur eine Auskunft ob sie gleich oder verschieden sind. Hier hilft dann cmp, compare
mount
montieren, eigentlich zum Einhängen von Laufwerken, zeigt aber auch an, was wo und wie eingehängt wurde
df
disk file, zeigt an wieviel Platz auf den einzelnen Laufwerken bzw. Partitionen vorhanden ist, wieviel belegt ist
du
disk usage, listet auf wieviel Platz belegt wurde

Beispiele:

mount zeigt an, was wo und wie eingehängt ist:

$ mount
/dev/hda1 on / type xfs (rw)
proc on /proc type proc (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
/dev/hda6 on /home type xfs (rw)
/dev/hda7 on /opt type xfs (rw)
sysfs on /sys type sysfs (rw)
usbfs on /proc/bus/usb type usbfs (rw)

Dies zeigt Auslastung der Partitionen an:

$ df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/hda1              7995536   5170040   2825496  65% /
/dev/hda6              4091740   3430416    661324  84% /home
/dev/hda7              2935060    409508   2525552  14% /opt

Anzeigen der Belegung einzelner Verzeichnisse in kB:

$ du -k -s /usr/share/man/man*
12172   /usr/share/man/man1
868     /usr/share/man/man2
14468   /usr/share/man/man3
428     /usr/share/man/man4
1408    /usr/share/man/man5
408     /usr/share/man/man6
652     /usr/share/man/man7
2192    /usr/share/man/man8
1428    /usr/share/man/man9

Erzeugen einer Datei aus binären Nullen mit 512 Bytes (bs: block size)

$ dd if=/dev/zero of=/var/tmp/empty bs=512 count=1

Sichern der Home-Verzeichnisse in die Datei /var/tmp/home.tar

$ tar cvf /var/tmp/home.tar /home

Editoren

ed, ex
Zeileneditoren, nicht sehr komfortabel
vi
visual interface, Bildschirmeditor, der Standardeditor unter Unix, er ist auf jedem System zu finden, außer bei einigen Linux-Distributionen!
emacs
editing macros, extrem mächtiger Editor mit vielen Modi
joe, pico, nano,...
eine Vielzahl von Editoren mit allen möglichen Schwerpunkten

Der vi hat zwei Modi, einen Insert-Modus in dem Text über die Tastatur eingegeben werden kann und einen Command-Modus. Hier kann z.B. mit den Tasten h, j, k, l im Text navigiert werden, x löscht das Zeichen unter dem Cursor, i schaltet in den Insert-Modus und die Escape-Taste wechselt in den Command-Modus zurück. ZZ speichert die Datei ab und beendet den vi, alternativ kann auch die Tastenkombination :wq! verwendet werden. Das Ausrufezeichen forciert ein Abspeichern was bei ZZ nicht der Fall ist!

Der emacs ist sehr mächtig, er enthält sogar einen Psychiater: M-X doctor

M-X ist das Meta-X, d.h. gewöhnlich ALT-X und man landet in dem unteren Mini-Buffer des emacs'. Er hat auch Funktionen zum highlighten, also dem farbigen Hervorheben von Textpassagen, Syntaxchecks, er kann Compiler und Debugger aufrufen und vieles mehr.

Spötter behaupten der Emacs sei ein eigenes Betriebssystem, nur leider ohne Editor.

Verarbeitung von Text/Textdateien

more, less
Pager, less is more, less kann deutlich mehr als more wie z.B. rückwärts scrollen
cat
concatenate, verbinden. Gibt den Inhalt der angebenen Dateien auf dem Bildschirm aus. Durch Umlenken in eine Datei werden alle Dateien in eine zusammengebunden. Das ist vor allem hilfreich um Werte aus /proc oder /sys auszulesen oder wenn der Inhalt einer Datei in eine Pipe geschoben werden soll
grep
suchen mittels regulärer Ausdrücke in Dateien
sed
stream editor, kann in einem Strom von Daten Änderungen aufgrund eines regulären Ausdrucks vornehmen
awk
Aho, Weinberger, Kernighan, interpretierte Sprache für Textbearbeitung, sehr elegant zum Aufsplitten nach Feldern.

Beispiele:

Das Zählen aller Startskripte aus rc2.d geht mittels:

$ ls /etc/rc2.d/ | grep ^S[0-9][0-9] | wc -l
42

Ausgeben alle Startdateien aus /etc/init.d die mit r beginnen, es werden nur der Name und die Größe ausgegeben:

$ ls -l /etc/init.d/r* |awk '{print $9 " " $5}'
/etc/init.d/rc 2235
/etc/init.d/rcS 1190
/etc/init.d/reboot 197
/etc/init.d/rinetd 1182
/etc/init.d/rmnologin 261
/etc/init.d/rsync 2026

Tools zum Umgang mit Programmen

which
wo liegt ein Programm im Suchpfad?
type
Shell builtin, ähnlich zu which, gibt aber auch builtins aus!
file
trifft Aussagen über Programme bzw. Dateien
ldd
list dynamic object dependencies, listet die dynamisch gelinkten Bibliotheken und deren Lage im Dateisystem auf
strings
Zeichenketten, listet die lesbaren Teile einer Datei oder eines Programms auf
strace
system call trace, verfolgt die Systemaufrufe eines Programms, manchmal sehr hilfreich bei der Fehlersuche

Beispiel zum Unterschied von which und type:

$ which bash
/bin/bash
$ which pwd
/bin/pwd
$ type bash
bash is /bin/bash
$ type pwd
pwd is a shell builtin

In nachfolgenden Fall handelt es sich um ein Programm vom Typ ELF, executable and linkable format. Das ist das aktuelle Format von ausführbaren Programmen und stammt dem System V (SysV) ab. Es ist sehr gut für das dynamische Linken gegen Bibliotheken geeignet.

Ferner ist die Datei im little endian Format, least significant bits (LSB) im Gegensatz zu big endian, most significant bits (MSB). Die Aussage bezieht sich auf die Reihenfolge wie die Bytes abgespeichert werden. Big und little endian geht zurück auf Gullivers Reisen, dort gab es die Einen, die Ihre Eier immer an dem dicken Ende zuerst aufgeschlagen haben, die anderen an dem dünnen Ende.

Stripped bezieht sich darauf, dass alle Symbole für ein Debugging abgestreift wurden. Das macht die Datei schlanker, erschwert aber die Fehlersuche bei Problemen.

$ file /bin/bash
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), 
           dynamically linked (uses shared libs), stripped

Soviel zu den statisch gelinkten Programmen in /bin: Unter Linux sind diese gegen den Standard dynamisch gelinkt. Aber es kann gesehen werden, dass alle Abhängigkeiten auf der root-Partition liegen:

$ ldd /bin/bash
ldd /bin/bash
        libncurses.so.5 => /lib/libncurses.so.5 (0x4002a000)
        libdl.so.2 => /lib/libdl.so.2 (0x40069000)
        libc.so.6 => /lib/libc.so.6 (0x4006c000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Hier ist ein Beispiel zum Auffinden der einkompilierten Lage der sshd_config Datei im secure shell daemon (sshd):

$ strings /usr/local/sbin/sshd |grep sshd_config /usr/local/etc/sshd_config

Prozesse

ps
processes, listet die laufenden Prozesse auf
pstree
zeigt den Prozessbaum auf, welcher Prozess wurde von welchem anderen gestartet. Der erste ist immer init.
top
listet die Programme auf, die am meisten CPU verbrauchen, es kann auch nach anderen Kriterien sortiert werden, wird alle paar Sekunden aktualisiert, wird mit q beendet
lsof
list open files, zeigt welcher Prozess welche Dateien oder Neztwerksockets geöffnet hat

Beispiele:

pstree, -A für ASCII Ausgabe, alles fängt mit init an, der Windowmanager ist via gdm (gnome display manager) gestartet worden.

$ pstree -A
init-+-acpid
     |-atd
     |-bash-+-bash---Xprt
     |      `-bash
     |-cardmgr
     |-cron
     |-dictd
     |-events/0
     |-famd
     |-gdm---gdm-+-XFree86
     |           `-fvwm2-+-FvwmAuto
     |                   |-FvwmPager
     |                   |-ssh-agent
[...]

Auflisten des Prozesses elvis:

$  ps wa |grep elvis
 8866 pts/0    S+     0:05 /usr/bin/elvis LG12

Die offenen Dateien von elvis sind:

$ lsof -P 8866
COMMAND  PID    USER   FD   TYPE DEVICE    SIZE     NODE NAME
elvis   8866 geschke  cwd    DIR    3,6    4096   235120 /home/geschke/LUG
elvis   8866 geschke  rtd    DIR    3,1    4096      128 /
elvis   8866 geschke  txt    REG    3,1  521400 22195163 /usr/bin/elvis
[...]

Beim TYPE ist DIR ein Verzeichis (directory), REG ist eine reguläre Datei, DEVICE gibt an auf welchem Device (Major, Minor) die Datei liegt, in diesem Fall ist es /dev/hda1 bzw. /dev/hda6:

$ ls -l /dev/hda[16]
brw-rw----  1 root disk 3, 1 Mar 14  2002 /dev/hda1
brw-rw----  1 root disk 3, 6 Mar 14  2002 /dev/hda6

Das erste ist die root-Partition, das andere die home-Partition:

$ df / /home            
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/hda1              7995536   5168936   2826600  65% /
/dev/hda6              4091740   3540664    551076  87% /home

(Und ja, ich sollte mal wieder aufräumen...)

Netzwerktools

ifconfig
interface configuration, Konfiguration der Netzwerkkarten, neuerdings oft durch ifup/ifdown ersetzt
mii-tool
gibt Auskunft über den Netzwerkmodus aus (sofern möglich), full/half-duplex, Geschwindigkeit, Advertising-Verhalten aka Autonegotiation, Link-Status
arp
address resolution protocol, Manipulation der ARP-Tabelle
netstat
Listet offene und verbundene Ports auf
lsof
list open files, funktioniert auch für Netzwerke
tcpdump, ethereal
belauschen (sniffen) des Datenverkehrs (traffic) auf der Leitung
ping
senden von ICMP echo requests, gut geeignet um auf IP Ebene eine Verbindung zu testen.
nmap
network map, Portscanner, listet offene Ports auf
telnet
Client für das telnet Protokoll, kann aber auch zum Testen diverser anderer Protokolle verwendet werden, SMTP, POP, IMAP, HTTP, FTP,...

Beispiele:

Hier kann die MAC-Adresse (HWaddr) der Ethernetkarte gefunden werden, die gesetzte IP-Adresse sowie die Broadcast-Adresse und die Netzmaske sowie Statistik über die ein- und ausgegangen Pakete (RX: Receive, TX: Transmit)

# ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:A0:CC:D8:AC:17  
          inet addr:192.168.1.18  Bcast:192.168.1.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:22211 errors:0 dropped:0 overruns:0 frame:0
          TX packets:22347 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:13683545 (13.0 MiB)  TX bytes:3406355 (3.2 MiB)
          Interrupt:9 Base address:0x4700 

Hier kann gesehen werden, dass 100MBit im Full-Duplex Modus ausgehandelt (negotiated) worden sind und der Link-Status ok ist:

# mii-tool eth0
eth0: negotiated 100baseTx-FD flow-control, link ok

Auflisten der bekannten Zuordnung zwischen MAC und IP-Adresse geht auch relativ leicht:

# arp -a
majestix.physik.home (192.168.1.5) at 00:E0:4C:00:82:9F [ether] on eth0

Mitsniffen des ARP Verkehrs, hier wird nach der MAC-Adresse von 192.168.1.5 gefragt und just dieser Computer antwortet:

# tcpdump arp
22:39:10.209273 arp who-has 192.168.1.5 tell debix.physik.home
22:39:10.209397 arp reply 192.168.1.5 is-at 0:e0:4c:0:82:9f

Normalerweise wird nur der Netzwerkverkehr von der Ethernetkarte angenommen, der auch für diese Karte bestimmt ist. Soll alles gezeigt werden, so muß die Karte in den promiscous Modus gesetzt werden. tcpdump macht dies automatisch, aus dmesg:

device eth0 entered promiscuous mode

Das nützt aber wenig, wenn der Anschluß an das Netzwerk über einen Switch erfolgte, dieser leitet die anderen Netzwerkpakete nicht weiter.

ethereal hat eine graphische Oberfläche und bietet bessere Analysemöglichkeiten der höheren Protokollebenen. Allerdings sind damit auch einhergehende Sicherheitslücken möglich. Daher bietet sich das Mitschneiden in eine Datei mit tcpdump (Option -w Dateiname) an. Diese Datei kann dann von ethereal ausgewertet werden, dazu muß ethereal nicht mehr mit root-Rechten laufen!

nmap listet die offenen Ports eines entfernten Computers auf. Per default sind das nur TCP-Ports, es geht aber auch mit UDP und es gibt diverse Methoden das Scannen zu verschleiern:

# nmap 192.168.1.18

Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
Interesting ports on debix-1.physik.home (192.168.1.18):
(The 1589 ports scanned but not shown below are in state: closed)
Port       State       Service
9/tcp      open        discard                 
13/tcp     open        daytime                 
21/tcp     open        ftp                     
22/tcp     open        ssh                     
23/tcp     open        telnet                  
25/tcp     open        smtp                    
37/tcp     open        time                    
79/tcp     open        finger                  
111/tcp    open        sunrpc                  
113/tcp    open        auth                    
515/tcp    open        printer                 
866/tcp    open        unknown                 

Nmap run completed -- 1 IP address (1 host up) scanned in 1 second

netstat listet die offenen Ports des eigenen Computers auf. Hier ergibt sich für TCP bei 192.168.1.18:

# netstat -ta
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 *:866                   *:*                     LISTEN     
tcp        0      0 *:printer               *:*                     LISTEN     
tcp        0      0 *:dict                  *:*                     LISTEN     
tcp        0      0 *:time                  *:*                     LISTEN     
tcp        0      0 *:discard               *:*                     LISTEN     
tcp        0      0 *:vboxd                 *:*                     LISTEN     
tcp        0      0 localhost:620           *:*                     LISTEN     
tcp        0      0 *:daytime               *:*                     LISTEN     
tcp        0      0 *:finger                *:*                     LISTEN     
tcp        0      0 *:sunrpc                *:*                     LISTEN     
tcp        0      0 *:auth                  *:*                     LISTEN     
tcp        0      0 *:ftp                   *:*                     LISTEN     
tcp        0      0 *:ssh                   *:*                     LISTEN     
tcp        0      0 *:telnet                *:*                     LISTEN     
tcp        0      0 *:smtp                  *:*                     LISTEN     
tcp        0      0 localhost:5180          *:*                     LISTEN     
tcp        0      0 debix.physik.home:44062 majestix.physik.hom:ssh ESTABLISHED

Eine Verbindung besteht und zwar zu majestix via ssh.

Mithilfe von telnet kann zum Beispiel eine Verbindung einem Mailserver hergestellt werden:

$ telnet 192.168.1.5 25
Trying 192.168.1.5...
Connected to 192.168.1.5.
Escape character is '^]'.
220 majestix.physik.home ESMTP Sendmail 8.13.4/8.13.4/Debian-1; 
    Sat, 10 Sep 2005 22:50:14 +0200; (No UCE/UBE) logging access from: 
    debix-1.physik.home(OK)-root@debix-1.physik.home [192.168.1.18]
quit
221 2.0.0 majestix.physik.home closing connection
Connection closed by foreign host.

Dirk Geschke, dirk@lug-erding.de