Ich gehe dieses Buch durch , Advanced Linux Programming von Mark Mitchell, Jeffrey Oldham und Alex Samuel. Es ist von 2001, also ein bisschen alt. Aber ich finde es trotzdem ganz gut.
Ich habe jedoch einen Punkt erreicht, an dem es von dem abweicht, was mein Linux in der Shell-Ausgabe erzeugt. Auf Seite 92 (116 im Viewer) beginnt das Kapitel 4.5 GNU / Linux-Thread-Implementierung mit dem Absatz, der diese Anweisung enthält:
Die Implementierung von POSIX-Threads unter GNU / Linux unterscheidet sich in einem wichtigen Punkt von der Thread-Implementierung auf vielen anderen UNIX-ähnlichen Systemen: Unter GNU / Linux werden Threads als Prozesse implementiert.
Dies scheint ein wichtiger Punkt zu sein und wird später mit einem C-Code veranschaulicht. Die Ausgabe im Buch ist:
main thread pid is 14608
child thread pid is 14610
Und in meinem Ubuntu 16.04 ist es:
main thread pid is 3615
child thread pid is 3615
ps
Ausgabe unterstützt dies.
Ich denke, zwischen 2001 und jetzt muss sich etwas geändert haben.
Das nächste Unterkapitel auf der nächsten Seite, 4.5.1 Signalbehandlung, baut auf der vorherigen Anweisung auf:
Das Verhalten der Interaktion zwischen Signalen und Threads variiert von einem UNIX-ähnlichen System zum anderen. In GNU / Linux wird das Verhalten durch die Tatsache bestimmt, dass Threads als Prozesse implementiert werden.
Und es sieht so aus, als würde dies später in diesem Buch noch wichtiger werden. Könnte jemand erklären, was hier los ist?
Ich habe dieses gesehen. Sind Linux-Kernel-Threads wirklich Kernel-Prozesse? , aber es hilft nicht viel. Ich bin verwirrt.
Dies ist der C-Code:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_function (void* arg)
{
fprintf (stderr, "child thread pid is %d\n", (int) getpid ());
/* Spin forever. */
while (1);
return NULL;
}
int main ()
{
pthread_t thread;
fprintf (stderr, "main thread pid is %d\n", (int) getpid ());
pthread_create (&thread, NULL, &thread_function, NULL);
/* Spin forever. */
while (1);
return 0;
}
getpid
eine sogenannte Threadgruppen-ID zurück und erhalten eine eindeutige ID für einen Prozess, den Sie verwenden müssengettid
. Mit Ausnahme des Kernels bezeichnen die meisten Benutzer und Tools eine Thread-Gruppe als Prozess und einen Prozess als Thread, um die Konsistenz mit anderen Systemen zu gewährleisten.Antworten:
Ich denke, dieser Teil der
clone(2)
Manpage kann den Unterschied bezüglich aufklären. die PID:Der Ausdruck "Threads werden als Prozesse implementiert" bezieht sich auf das Problem von Threads, die in der Vergangenheit separate PIDs hatten. Grundsätzlich hatte Linux ursprünglich keine Threads innerhalb eines Prozesses, sondern nur separate Prozesse (mit separaten PIDs), die möglicherweise über gemeinsam genutzte Ressourcen wie virtuellen Speicher oder Dateideskriptoren verfügten.
CLONE_THREAD
Durch die Trennung von Prozess-ID (*) und Thread-ID ähnelt das Linux-Verhalten eher anderen Systemen und den POSIX-Anforderungen in diesem Sinne. Obwohl das Betriebssystem technisch noch keine separaten Implementierungen für Threads und Prozesse hat.Signalverarbeitung war ein weiterer Problembereich mit der alten Implementierung, dies wird ausführlicher in dem beschriebenen Papier @FooF verweist in ihrer Antwort .
Wie in den Kommentaren vermerkt, wurde Linux 2.4 im selben Jahr wie das Buch auch im Jahr 2001 veröffentlicht. Es ist also nicht verwunderlich, dass die Nachrichten nicht in diesen Druck gelangten.
quelle
Sie haben Recht, tatsächlich "muss sich zwischen 2001 und jetzt etwas geändert haben". Das Buch, das Sie lesen, beschreibt die Welt gemäß der ersten historischen Implementierung von POSIX-Threads unter Linux, LinuxThreads genannt (siehe auch Wikipedia- Artikel für einige).
LinuxThreads hatte einige Kompatibilitätsprobleme mit dem POSIX-Standard - zum Beispiel Threads, die keine PIDs gemeinsam haben - und einige andere schwerwiegende Probleme. Um diese Fehler zu beheben, wurde von Red Hat eine weitere Implementierung mit dem Namen NPTL (Native POSIX Thread Library) eingeführt, die die Unterstützung für Kernel und User Space Library hinzufügt, um eine bessere POSIX-Kompatibilität zu erreichen. Next Generation Posix Threads "), siehe Wikipedia-Artikel zu NPTL ). Die zusätzlichen Flags, die dem
clone(2)
Systemaufruf hinzugefügt wurden (insbesondereCLONE_THREAD
die@ikkkachu
in seiner Antwort aufgeführten ), sind wahrscheinlich der offensichtlichste Teil der Kernelmodifikationen. Der User Space Teil der Arbeit wurde schließlich in die GNU C Library integriert.Heutzutage verwenden einige eingebettete Linux-SDKs die alte LinuxThreads-Implementierung, da sie eine Version von uClibc (auch als µClibc bezeichnet) mit geringerem Speicherbedarf verwenden. Es dauerte einige Jahre, bis die NPTL-User-Space-Implementierung von GNU LibC als portiert und angenommen wurde Standardmäßige POSIX-Threading-Implementierung, da diese speziellen Plattformen im Allgemeinen nicht danach streben, blitzschnell den neuesten Moden zu folgen. Dies kann beobachtet werden, indem festgestellt wird, dass PIDs für verschiedene Threads auf diesen Plattformen auch anders sind als im POSIX-Standard angegeben - genau wie in dem Buch, das Sie gerade lesen. Eigentlich mal angerufen
pthread_create()
Sie hatten plötzlich die Prozessanzahl von eins auf drei erhöht, da zusätzlicher Prozess erforderlich war, um das Chaos zusammenzuhalten.Die Handbuchseite zu Linux pthreads (7) bietet einen umfassenden und interessanten Überblick über die Unterschiede zwischen den beiden. Eine weitere aufschlussreiche, wenn auch veraltete Beschreibung der Unterschiede ist dieses Papier von Ulrich Depper und Ingo Molnar über das Design von NPTL.
Ich empfehle Ihnen, diesen Teil des Buches nicht zu ernst zu nehmen. Ich empfehle stattdessen Butenhofs Programmieren von POSIX-Threads sowie POSIX- und Linux-Handbuchseiten zu diesem Thema. Viele Tutorials zu diesem Thema sind ungenau.
quelle
(Userspace-) Threads werden unter Linux nicht als Prozesse implementiert, da sie keinen eigenen privaten Adressraum haben und dennoch den Adressraum des übergeordneten Prozesses gemeinsam nutzen.
Diese Threads sind jedoch so implementiert, dass sie das Kernel-Prozess-Abrechnungssystem verwenden. Sie erhalten daher eine eigene Thread-ID (TID), jedoch die gleiche PID und 'Thread-Gruppen-ID' (TGID) wie der übergeordnete Prozess. Dies steht im Gegensatz zu Eine Verzweigung, bei der eine neue TGID und PID erstellt werden und die TID mit der PID identisch ist.
Es scheint also, dass neuere Kernel eine separate TID hatten, die abgefragt werden kann. Dies ist für Threads unterschiedlich. Ein geeignetes Code-Snippet, um dies in jeder der obigen main () thread_function () anzuzeigen, lautet:
Also wäre der gesamte Code mit diesem Code:
Geben Sie eine Beispielausgabe von:
quelle
Grundsätzlich sind die Informationen in Ihrem Buch historisch korrekt, da die Implementierungshistorie von Threads unter Linux schändlich schlecht ist. Diese Antwort von mir auf eine verwandte Frage zu SO dient auch als Antwort auf Ihre Frage:
https://stackoverflow.com/questions/9154671/unterscheidung-zwischen-prozessen-und-threads-in-linux/9154725#9154725
quelle
Intern gibt es im Linux-Kernel keine Prozesse oder Threads. Prozesse und Threads sind meistens ein Userland-Konzept. Der Kernel selbst sieht nur "Tasks", dh ein planbares Objekt, das möglicherweise keine, einige oder alle Ressourcen mit anderen Tasks gemeinsam nutzt. Threads sind Tasks, die so konfiguriert wurden, dass sie die meisten Ressourcen (Adressraum, MMAPs, Pipes, Open File Handler, Sockets usw.) für die übergeordnete Task freigeben. Prozesse sind Tasks, die so konfiguriert wurden, dass sie nur minimale Ressourcen für die übergeordnete Task freigeben .
Wenn Sie die Linux-API direkt verwenden ( clone () anstelle von fork () und pthread_create () ), können Sie viel flexibler festlegen, wie viele Ressourcen freigegeben oder nicht freigegeben werden sollen, und Sie können Aufgaben erstellen, die nicht vollständig a sind prozess noch voll ein thread. Wenn Sie diese Low-Level-Aufrufe direkt verwenden, ist es auch möglich, eine Aufgabe mit einer neuen TGID (die von den meisten Userland-Tools als Prozess behandelt wird) zu erstellen, die tatsächlich alle Ressourcen für die übergeordnete Aufgabe freigibt, oder umgekehrt Eine Aufgabe mit gemeinsam genutzter TGID (wird daher von den meisten Userland-Tools als Thread behandelt), die keine Ressource mit ihrer übergeordneten Aufgabe gemeinsam nutzt.
Während Linux 2.4 TGID implementiert, dient dies hauptsächlich der Ressourcenabrechnung. Viele Benutzer und Userspace-Tools finden es hilfreich, verwandte Aufgaben zu gruppieren und ihre Ressourcennutzung gemeinsam zu melden.
Die Implementierung von Tasks unter Linux ist viel flüssiger als die Prozesse und Threads, die die Userspace-Tools in der Welt darstellen.
quelle
Linus Torvalds erklärte 1996 in einem Posting der Kernel-Mailing-Liste, dass "sowohl Threads als auch Prozesse als 'Kontext der Ausführung' behandelt werden", was "nur ein Konglomerat des gesamten Zustands dieses CoE ist ... einschließlich Dinge wie CPU Status, MMU - Status, Berechtigungen und verschiedene Kommunikationsstatus (offene Dateien, Signalhandler usw.) ".
Wie Sie sehen, erzeugt dieses Programm 25 Threads auf einmal, von denen jeder 100 Sekunden lang ruht und sich dann wieder dem Hauptprogramm anschließt. Nachdem alle 25 Threads das Programm wieder verbunden haben, ist das Programm abgeschlossen und wird beendet.
Unter Verwendung von sehen
top
Sie 25 Instanzen des Programms "threads2". Aber kidna langweilig. Die Ausgabe vonps auwx
ist noch weniger interessant ... ABERps -eLf
wird ein bisschen aufregend.Hier sehen Sie alle 26 CoEs, die das
thread2
Programm erstellt hat. Sie haben alle dieselbe Prozess-ID (PID) und übergeordnete Prozess-ID (PPID), aber jede hat eine andere LWP-ID (Lightweight-Prozess). Die Anzahl der LWPs (NLWP) gibt an, dass es 26 CoEs gibt - das Hauptprogramm und die 25 davon gespawnte Threads.quelle
Wenn es um Linux Prozesse kommt und Threads sind Art von derselben Sache. Was zu sagen ist , dass sie mit dem gleichen Systemaufruf erstellt werden:
clone
.Wenn Sie darüber nachdenken, besteht der Unterschied zwischen Threads und Prozessen darin, in welchen Kernelobjekten das untergeordnete und das übergeordnete Element gemeinsam genutzt werden. Bei Prozessen ist es nicht viel: offene Dateideskriptoren, Speichersegmente, in die noch nichts geschrieben wurde, wahrscheinlich ein paar andere, an die ich auf Anhieb nicht denken kann. Bei Threads werden viel mehr Objekte gemeinsam genutzt, aber nicht alle.
Was Threads und Objekte unter Linux näher bringt, ist der
unshare
Systemaufruf. Kernel-Objekte, die als gemeinsam genutzt beginnen, können nach der Thread-Erstellung freigegeben werden. So können Sie beispielsweise zwei Threads desselben Prozesses mit unterschiedlichem Speicherplatz für Dateideskriptoren verwenden (indem Sie die gemeinsame Nutzung von Dateideskriptoren widerrufen, nachdem die Threads erstellt wurden). Sie können es selbst testen, indem Sie einen Thread erstellen,unshare
beide Threads aufrufen und dann alle Dateien schließen und neue Dateien, Pipes oder Objekte in beiden Threads öffnen. Dann schauen/proc/your_proc_fd/task/*/fd
Sie rein und Sie werden sehen, dass jedetask
(die Sie als Thread erstellt haben) unterschiedliche FDS hat.Tatsächlich sind sowohl das Erstellen neuer Threads als auch neuer Prozesse Bibliotheksroutinen, die
clone
darunter aufrufen und angeben, welches der Kernelobjekte das neu erstellte Prozess-Thread-Ding-amajig (dhtask
) für den aufrufenden Prozess / Thread freigibt .quelle