heim - Browser
Multitasking-Programmierung unter Linux. Multitasking-Linux-Programmierhintergrund und Prioritätsprozessmanagement

Linux- Multitasking und Mehrbenutzer operationssystem für Bildung, Wirtschaft, individuelle Programmierung. Linux gehört zur Familie der UNIX-ähnlichen Betriebssysteme.

Linux wurde ursprünglich von Linus Torvalds geschrieben und dann von unzähligen Menschen auf der ganzen Welt weiterentwickelt. Es ist ein Klon des Unix-Betriebssystems, eines der ersten leistungsstarken Betriebssysteme, die für Computer entwickelt wurden, aber es ist nicht kostenlos. Aber weder Unix System Laboratories, die Erfinder von Unix, noch die Berkeley University, die Entwickler der Berkeley Software Distribution (BSD), waren an seiner Entstehung beteiligt. Einer der meisten Interessante Fakten Aus der Geschichte von Linux geht hervor, dass Menschen aus der ganzen Welt gleichzeitig an seiner Entstehung beteiligt waren – von Australien bis Finnland – und dies bis heute tun.

Linux wurde ursprünglich für die Ausführung auf dem 386-Prozessor entwickelt. Eines der ersten Projekte von Linus Torvalds war ein Programm, das zwischen Prozessen wechseln konnte, von denen einer AAAA und der andere BBBB ausgab. Aus diesem Programm entwickelte sich später Linux. Richtiger wäre es zu sagen, dass Linus den Betriebssystemkernel entwickelt hat und für dessen Stabilität verantwortlich ist.

Linux unterstützt die meisten gängigen Unix Software, einschließlich Grafiksystem X Window – und das ist eine große Anzahl von Programmen, aber es ist erwähnenswert, dass Linux ABSOLUT KOSTENLOS ist. Das meiste, was Sie bezahlen müssen, ist die Verpackung und die CD, auf der die Linux-Distribution aufgezeichnet ist. Eine Distribution ist das Betriebssystem selbst + eine Reihe von Softwarepaketen für Linux. Erwähnenswert ist auch, dass all dies mit Quellcode geliefert wird und jedes für Linux geschriebene Programm an Ihre Bedürfnisse angepasst werden kann. Dies ermöglicht Ihnen auch die Übertragung jedes Programms auf jede Plattform – Intel PC, Macintosh. All dies geschah übrigens dank der Free Software Foundation, einem Fonds kostenlose Programme, das Teil des GNU-Projekts ist. Und zu diesem Zweck wurde die GPL (General Public License) geschaffen, auf deren Grundlage Linux wie alle Software dafür kostenlos ist und die kommerzielle Nutzung von Software für Linux oder Teilen davon verboten ist. Konfigurationssystem Unix Linux

Darüber hinaus ist Linux ein sehr leistungsfähiges und stabiles Betriebssystem. Der Einsatz im Internet lohnt sich, und das Hacken ist gar nicht so einfach.

Die Entwicklung von Linux erfolgt heute in zwei Zweigen. Die erste mit geraden Versionsnummern (2.0, 2.2, 2.4) gilt als stabilere und zuverlässigere Version von Linux. Die zweite, deren Versionen mit ungeraden Nummern nummeriert sind (2.1, 2.3), ist gewagter, entwickelt sich schneller und ist daher (leider) fehlerhafter. Aber das ist Geschmackssache.

Unter Linux gibt es keine Trennung fährt C, D, und die Kommunikation mit Geräten ist sehr bequem. Alle Geräte haben ihre eigenen Systemdatei, alle Festplatten sind mit einem Dateisystem verbunden und alles sieht monolithisch aus, eins. Eine übersichtliche Verzeichnisstruktur ermöglicht es Ihnen, alle Informationen sofort zu finden. Für Bibliotheksdateien – ein eigenes Verzeichnis, für gestartete Dateien – ein eigenes, für Dateien mit Einstellungen – ein eigenes, für Gerätedateien – ein eigenes Verzeichnis und so weiter.

Durch die Modularität des Kernels können Sie beliebige Betriebssystemdienste verbinden, ohne den Computer neu starten zu müssen. Darüber hinaus können Sie den Betriebssystemkernel selbst neu erstellen, da die Kernel-Quellcodes auch in jeder Distribution verfügbar sind.

Das Linux-Betriebssystem nutzt sozusagen sehr geschickt den Gedanken des Multitasking, d.h. alle Prozesse im System werden gleichzeitig ausgeführt (vergleiche mit Windows: Das Kopieren von Dateien auf eine Diskette und der Versuch, in diesem Moment Musik zu hören, sind nicht immer kompatibel).

Aber nicht alles ist so einfach. Linux ist etwas komplexer als Windows und nicht jeder kann nach der Verwendung von Windows problemlos darauf umsteigen. Auf den ersten Blick scheint es sogar sehr umständlich und schwierig zu konfigurieren zu sein. Aber das ist nicht so. Der ganze Clou von Linux besteht darin, dass Sie es individuell anpassen und so konfigurieren können, dass Sie mit der Verwendung dieses Betriebssystems große Zufriedenheit verspüren. Eine Vielzahl von Einstellungen ermöglicht es Ihnen, das äußere (und innere) Erscheinungsbild des Betriebssystems zu ändern und nicht Ein einzelnes Linux-System ähnelt Ihrem. Unter Linux haben Sie die Wahl zwischen der Verwendung einer grafischen Shell, es gibt mehrere Büropakete, Serverprogramme, Firewalls... Einfach eine ganze Menge verschiedene Programme für jeden Geschmack.

Im Jahr 1998 war Linux das am schnellsten wachsende Server-Betriebssystem, die Akzeptanz stieg in diesem Jahr um 212 %. Heute gibt es mehr als 20.000.000 Linux-Benutzer. Unter Linux gibt es viele Anwendungen, die für beides konzipiert sind Heimgebrauch und für voll funktionsfähige UNIX-Workstations und Internet-Server.

Linux ist nicht mehr nur ein Betriebssystem. Linux wird immer mehr zu einem Kult. Im Falle einer Sekte wird es immer schwieriger, der Wahrheit auf den Grund zu gehen. Beginnen wir mit den Fakten. Linux ist also:

  • * kostenloser (oder besser gesagt frei verteilter) Unix-Klon;
  • * Betriebssystem mit echtem Multitasking;
  • * Ein Betriebssystem, das jeder „Benutzer“ ändern kann, da Sie für fast jeden Teil davon Quellcodes finden können;
  • * die genau nach Ihren Wünschen konfiguriert ist und nicht nach den Vorgaben des Herstellers.

Linux-Neulinge werden vor allem davon angezogen, dass es „cool“ und trendy ist. Es gibt den Mythos, dass dieses Betriebssystem nicht wirklich für den Endbenutzer geeignet ist. Um einen zuverlässigen und hacksicheren Server zusammenzustellen, ist dies mehr als gute Entscheidung, aber nicht für einfacher Benutzer der Komfort und Bequemlichkeit benötigt und das System, mit dem er gerade arbeitet, überhaupt nicht verstehen und fühlen möchte. Das ist nicht ganz richtig. Das maßgeschneiderte Linux-System mit grafischer Oberfläche ist so einfach zu bedienen und intuitiv wie ein Microsoft-Betriebssystem. Es ist nur so, dass die Einrichtung von Linux viel Aufwand und Wissen erfordert.

Aufgrund dieser Merkmale seiner Entstehung und Entwicklung hat Linux ganz spezifische „Charaktereigenschaften“ erworben. Einerseits handelt es sich um ein typisches UNIX-System, Multiuser und Multitasking. Auf der anderen Seite gibt es ein typisches System aus Hackern, Studenten und generell allen Menschen, die ständig lernen und alles bis ins kleinste Detail verstehen möchten. Die Flexibilität bei der Einrichtung und Nutzung von Linux sucht wahrscheinlich ihresgleichen. Sie können es auf der Ebene verwenden, auf der Win95 funktioniert, d. h. Sie haben einen grafischen Desktop mit allen Funktionen unter Windows: Symbole, Taskleiste, Kontextmenü usw. Darüber hinaus können Sie einen Desktop installieren, der sich überhaupt nicht unterscheidet Aussehen und Funktionen von Windows. (Im Allgemeinen Optionen Fenstermanager Unter Linux gibt es einfach kein Ende, vom superspartanischen Icewm bis zum superausgeklügelten Enlightment + Gnome. Andererseits ermöglicht Ihnen Linux einen beispiellosen Zugriff auf die Hardware bei jedem Verfügbarkeitsgrad. Dafür reicht es zwar nicht aus, die rechte Maustaste drücken zu können, man muss SI und Computerarchitektur erlernen. Aber ein Mensch, der einmal diesen Gedankengeruch, diese Inspiration eines Programmierers gespürt hat, wenn man eine Maschine „an den Ohren“ hält und mit ihr buchstäblich alles machen kann, wozu sie fähig ist – so ein Mensch wird nie wieder dorthin zurückkehren können die weichen Pfoten von Windows.

Wenn der Benutzer bei der Verwendung eines kommerziellen Betriebssystems gezwungen ist, auf die Veröffentlichung der nächsten Version zu warten, um ein System ohne Störungen und Fehler der vorherigen Version zu erhalten, dann ermöglicht die Modularität von Linux das Herunterladen eines neuen Kernels. die mindestens alle zwei Monate oder sogar öfter (stabile Version) veröffentlicht wird.

Antworten auf die Frage „Was ist Linux?“ man kann viele finden. Viele Leute glauben, dass Linux nur ein Kernel ist. Doch der Kernel allein nützt dem Anwender nichts. Obwohl der Kernel zweifellos die Basis des Linux-Betriebssystems ist, muss der Benutzer ständig mit Anwendungsprogrammen arbeiten. Diese Programme sind nicht weniger wichtig als der Kernel. Daher handelt es sich bei Linux um eine Sammlung von Kernel- und Hauptanwendungsprogrammen, die normalerweise auf jedem Computer mit diesem Betriebssystem installiert sind. Die Verbindung von Kernel und Anwendungsprogrammen zu einem Ganzen spiegelt sich auch im Namen des Systems wider: GNU/Linux. GNU ist ein Projekt zur Erstellung einer Reihe von Programmen, die denen ähneln, die normalerweise mit einem Unix-ähnlichen System einhergehen.

Eine häufige Beschwerde von Linux-Anhängern ist, dass sie, wenn sie über die Vorteile von Linux sprechen, die Nachteile von Windows aufzählen. Dies ist jedoch oft unvermeidlich, da alles durch Vergleichen gelernt wird und die meisten Computerbenutzer nur noch mit Windows vertraut sind. Was bietet Ihnen Linux?

Original: Leichte Prozesse: Linux-Threads zerlegen
Autoren: Vishal Kanaujia, Chetan Giridhar
Erscheinungsdatum: 1. August 2011
Übersetzung: A. Panin
Veröffentlichungsdatum der Übersetzung: 22. Oktober 2012

Dieser Artikel richtet sich an Linux-Entwickler und Informatikstudenten und behandelt die Grundlagen von Threads und deren Implementierung mithilfe von Lightweight-Prozessen in Linux, mit Quellcodebeispielen zum besseren Verständnis.

Programm-Threads sind das Grundelement einer Multitasking-Softwareumgebung. Ein Programmthread kann als Ausführungsumgebung eines Prozesses beschrieben werden; Daher verfügt jeder Prozess über mindestens einen Thread. Beim Multithreading handelt es sich um mehrere parallele (auf Multiprozessorsystemen) und in der Regel synchronisierte Ausführungsumgebungen für einen Prozess.

Programm-Threads verfügen über eigene Bezeichner (Thread-ID) und können unabhängig voneinander ausgeführt werden. Sie teilen sich einen Prozessadressraum und nutzen diese Funktion als Vorteil, um die Verwendung von IPC-Kanälen (I– gemeinsam genutzter Speicher, Pipes und andere Systeme) für den Datenaustausch zu vermeiden. Threads eines Prozesses können interagieren – unabhängige Threads können beispielsweise den Wert einer globalen Variablen abrufen/ändern. Dieses Kommunikationsmodell eliminiert den Overhead von IPC-Aufrufen auf Kernel-Ebene. Da Threads in einem einzigen Adressraum arbeiten, sind Thread-Kontextwechsel schnell und nicht ressourcenintensiv.

Jeder Programmthread kann vom Taskplaner einzeln bearbeitet werden, sodass Multithread-Anwendungen gut für die parallele Ausführung auf Multiprozessorsystemen geeignet sind. Darüber hinaus erfolgt die Erstellung und Zerstörung von Threads schnell. Im Gegensatz zu einem fork()-Aufruf erstellt ein Thread keine Kopie des Adressraums des übergeordneten Prozesses; stattdessen teilen sich Threads den Adressraum zusammen mit anderen Ressourcen, einschließlich Dateihandles und Signalhandlern.

Eine Multithread-Anwendung nutzt Ressourcen optimal und so effizient wie möglich. In einer solchen Anwendung übernehmen Programm-Threads verschiedene Aufgaben unter Berücksichtigung der optimalen Nutzung des Systems. Ein Thread kann eine Datei von der Festplatte lesen, ein anderer kann Daten in einen Socket schreiben. Beide Threads arbeiten im Tandem und sind unabhängig voneinander. Dieses Modell optimiert die Systemnutzung und verbessert dadurch die Leistung.

Mehrere interessante Funktionen

Das bekannteste Merkmal der Arbeit mit Threads ist ihre Synchronisierung, insbesondere wenn eine gemeinsam genutzte Ressource als kritisches Segment markiert ist. Dies ist das Codesegment, das auf eine gemeinsam genutzte Ressource zugreift und nicht für mehr als einen Thread gleichzeitig zugänglich sein sollte. Da jeder Thread unabhängig ausgeführt werden kann, wird der Zugriff auf die gemeinsam genutzte Ressource nicht kontrolliert, was dazu führt, dass Synchronisierungsprimitive verwendet werden müssen, einschließlich Mutexe (gegenseitiger Ausschluss), Semaphore, Lese-/Schreibsperren und andere.

Mit diesen Grundelementen können Programmierer den Zugriff auf eine gemeinsam genutzte Ressource steuern. Darüber hinaus besteht bei Threads ebenso wie bei Prozessen die Gefahr, dass sie in einen Blockierungs- oder Wartezustand wechseln, wenn das Synchronisationsmodell nicht korrekt konzipiert ist. Auch das Debuggen und Analysieren von Multithread-Anwendungen kann recht mühsam sein.

Wie werden Threads unter Linux implementiert?

Mit Linux können Sie Multithread-Anwendungen entwickeln und verwenden. Auf Benutzerebene folgt die Implementierung von Threads in Linux dem offenen Standard POSIX (Portable Operating System Interface for uniX). Unix-Systeme), bezeichnet als IEEE 1003. Die Benutzerebenenbibliothek (glibc.so unter Ubuntu) stellt eine Implementierung der POSIX-API für Threads bereit.

Unter Linux existieren Programm-Threads in zwei separaten Räumen – dem Benutzerraum und dem Kernelraum. Im Benutzerbereich werden Threads mithilfe der POSIX-kompatiblen pthread-Bibliotheks-API erstellt. Diese User-Space-Threads sind untrennbar mit Kernel-Space-Threads verbunden. Unter Linux werden Kernel-Space-Threads als „Lightweight-Prozesse“ behandelt. Ein Lightweight-Prozess ist eine Einheit der Hauptlaufzeitumgebung. Im Gegensatz zu verschiedenen UNIX-Varianten, einschließlich Systemen wie HP-UX und SunOS, verfügt Linux nicht über ein separates Threading-System. Ein Prozess oder Thread unter Linux wird als „Aufgabe“ behandelt und verwendet dieselben internen Strukturen (eine Reihe von struct task_structs-Strukturen).

Für eine Reihe von Prozessthreads, die im Benutzerbereich erstellt werden, gibt es im Kernel eine Reihe einfacher Prozesse, die ihnen zugeordnet sind. Ein Beispiel veranschaulicht diesen Punkt: #include #enthalten #enthalten Int main() ( pthread_t tid = pthread_self(); int sid = syscall(SYS_gettid); printf("LWP id is %dn", sid); printf("POSIX thread id is %dn", tid); return 0; )

Mit dem Dienstprogramm ps können Sie Informationen über Prozesse sowie leichtgewichtige Prozesse/Threads dieser Prozesse abrufen: kanaujia@ubuntu:~/Desktop$ ps -fL UID PID PPID LWP C NLWP STIME TTY TIME CMD kanaujia 17281 5191 17281 0 1 11. Juni Punkte/ 2 00:00:02 bash kanaujia 22838 17281 22838 0 1 08:47 Punkte/2 00:00:00 ps -fL kanaujia 17647 14111 17647 0 2 00:06 Punkte/0 00:00:00 vi clone. S

Was sind Lightweight-Prozesse?

Ein Lightweight-Prozess ist ein Prozess, der den User-Space-Thread unterstützt. Jeder User-Space-Thread ist untrennbar mit einem leichtgewichtigen Prozess verbunden. Das Verfahren zum Erstellen eines einfachen Prozesses unterscheidet sich vom Verfahren zum Erstellen eines regulären Prozesses. Der Benutzerprozess „P“ kann mehrere verwandte Lightweight-Prozesse mit derselben Gruppen-ID haben. Durch die Gruppierung kann der Kernel Ressourcen partitionieren (Ressourcen umfassen Adressraum und Seiten). physikalischer Speicher(VM), Signalhandler und Dateideskriptoren). Dadurch kann der Kernel auch Kontextwechsel bei der Verarbeitung dieser Prozesse vermeiden. Die umfassende gemeinsame Nutzung von Ressourcen ist der Grund, warum diese Prozesse als leichtgewichtig bezeichnet werden.

Wie erstellt Linux leichtgewichtige Prozesse?

IN Linux-Erstellung Lightweight-Prozesse werden mithilfe des nicht standardisierten Systemaufrufs clone() implementiert. Es ähnelt fork(), bietet jedoch mehr Funktionalität. Im Allgemeinen wird ein fork()-Aufruf mithilfe eines clone()-Aufrufs mit implementiert zusätzliche Parameter, was auf Ressourcen hinweist, die von Prozessen gemeinsam genutzt werden. Durch den Aufruf von clone() wird ein Prozess erstellt, bei dem das untergeordnete Element Laufzeitelemente mit dem übergeordneten Element teilt, einschließlich Speicher, Dateihandles und Signalhandler. Die pthread-Bibliothek verwendet auch den clone()-Aufruf, um Threads zu implementieren. Sehen Sie sich die Quellcodedatei an ./nptl/sysdeps/pthread/createthread.c im Glibc-Quellcodeverzeichnis Version 2.11.2.

Erstellen Sie Ihren eigenen Lightweight-Prozess

Lassen Sie uns ein Beispiel für die Verwendung des Aufrufs clone() demonstrieren. Ansehen Quelle aus der Datei demo.c unten:

#enthalten #enthalten #enthalten #enthalten #enthalten #enthalten #enthalten //Stackgröße 64kB #define STACK 1024*64 // Der untergeordnete Thread führt diese Funktion aus int threadFunction(void* argument) ( printf("child thread enter\n"); close((int*)argument); printf( "untergeordneter Thread wird verlassen\n"); return 0; ) int main() ( void* stack; pid_t pid; int fd; fd = open("/dev/null", O_RDWR); if (fd< 0) { perror("/dev/null"); exit(1); } // Резервирование памяти для стека stack = malloc(STACK); if (stack == 0) { perror("malloc: could not allocate stack"); exit(1); } printf("Creating child thread\n"); // Вызов clone() для создания дочернего потока pid = clone(&threadFunction, (char*) stack + STACK, SIGCHLD | CLONE_FS | CLONE_FILES |\ CLONE_SIGHAND | CLONE_VM, (void*)fd); if (pid == -1) { perror("clone"); exit(2); } // Ожидание завершения дочернего потока pid = waitpid(pid, 0, 0); if (pid == -1) { perror("waitpid"); exit(3); } // Попытка записи в файл закончится неудачей, так как поток // закрыл файл if (write(fd, "c", 1) < 0) { printf("Parent:\t child closed our file descriptor\n"); } // Освободить память, используемую для стека free(stack); return 0; }

Mit dem Programm demo.c können Sie Threads im Wesentlichen auf die gleiche Weise wie mit der pthread-Bibliothek erstellen. Die direkte Verwendung des clone()-Aufrufs ist jedoch nicht ratsam, da bei falscher Verwendung die in der Entwicklung befindliche Anwendung möglicherweise fehlschlägt. Die Syntax der clone()-Funktion unter Linux ist unten angegeben: #include int clone (int (*fn) (void *), void *child_stack, int flags, void *arg);

Das erste Argument ist die Thread-Funktion; Es wird aufgerufen, wenn der Thread gestartet wird. Nachdem der Aufruf von clone() erfolgreich abgeschlossen wurde, beginnt die Ausführung der Funktion fn gleichzeitig mit dem aufrufenden Prozess.

Das nächste Argument ist ein Zeiger auf einen Speicherort für den Stapel des untergeordneten Prozesses. Der Schritt vor dem Aufruf von fork() und clone() erfordert, dass der Programmierer Speicher reserviert und einen Zeiger zur Verwendung als Stapel des untergeordneten Prozesses übergibt, da der übergeordnete und der untergeordnete Prozess Speicherseiten gemeinsam nutzen – dazu gehört auch der Stapel. Ein Kindprozess kann eine andere Funktion aufrufen als der Elternprozess, weshalb ein separater Stack erforderlich ist. In unserem Programm reservieren wir dieses Stück Speicher mit der Funktion malloc() auf dem Heap. Die Stackgröße wurde auf 64 KB festgelegt. Da der x86-Stack nach unten wächst, ist es notwendig, ein ähnliches Verhalten zu simulieren, indem der zugewiesene Speicher ab dem Ende des Abschnitts verwendet wird. Aus diesem Grund übergeben wir der Funktion clone() folgende Adresse: (char*) stack + STACK

Das nächste Flags-Argument ist besonders wichtig. Hier können Sie angeben, welche Ressourcen mit dem erstellten Prozess geteilt werden sollen. Wir haben uns für die Bitmaske entschieden SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM nachstehend beschrieben:

  • SIGCHLD: Der Thread sendet bei Beendigung das SIGCHLD-Signal an den übergeordneten Prozess. Wenn Sie diese Option festlegen, kann der übergeordnete Prozess mithilfe der Funktion „wait()“ auf den Abschluss aller Threads warten.
  • CLONE_FS: Dateisysteminformationen zwischen dem übergeordneten Prozess und dem Thread teilen. Die Informationen umfassen Root Dateisystem, Arbeitsverzeichnis und umask-Wert.
  • CLONE_FILES: Teilen Sie die Dateideskriptortabelle zwischen dem übergeordneten Prozess und dem Thread. Änderungen an der Tabelle werden im übergeordneten Prozess und allen Threads widergespiegelt.
  • CLOSE_SIGHAND: Teilen Sie die Signalhandlertabelle zwischen den übergeordneten und untergeordneten Prozessen. Auch hier gilt: Wenn der übergeordnete Prozess oder einer der Threads den Signalhandler ändert, wird die Änderung in den Tabellen der anderen Prozesse widergespiegelt.
  • CLONE_VM: Übergeordneter Prozess und Threads werden im selben Speicherbereich ausgeführt. Alle von einem von ihnen vorgenommenen Speicherschreibvorgänge oder Speicherzuordnungen stehen anderen Prozessen zur Verfügung.

Der letzte Parameter ist das an die Funktion (threadFunction) übergebene Argument, in unserem Fall ein Dateideskriptor.

Bitte beachten Sie das Beispiel für die Arbeit mit Lightweight-Prozessen demo.c, das wir zuvor veröffentlicht haben.

Der Thread schließt die vom übergeordneten Prozess geöffnete Datei (/dev/null). Da der übergeordnete Prozess und der Thread die Dateideskriptortabelle gemeinsam nutzen, wirkt sich der Vorgang zum Schließen der Datei auch auf den übergeordneten Prozess aus und führt dazu, dass nachfolgende write()-Aufrufe fehlschlagen. Der übergeordnete Prozess wartet auf die Beendigung des Threads (in dem Moment, in dem er das SIGCHLD-Signal empfängt). Danach gibt es den reservierten Speicher frei und gibt die Kontrolle zurück.

Kompilieren Sie das Programm und führen Sie es wie gewohnt aus. Die Ausgabe sollte der folgenden ähneln: $gcc demo.c $./a.out Untergeordneten Thread erstellen, untergeordneter Thread eingeben, untergeordneten Thread verlassen, Eltern: Untergeordneter Thread hat unseren Dateideskriptor $ geschlossen

Linux bietet Unterstützung für eine effiziente, einfache und skalierbare Threading-Infrastruktur. Dies hat das Interesse der Programmierer geweckt, mit Threading-Bibliotheken zu experimentieren und diese zu entwickeln, die clone() als Kernfunktion verwenden.

Wir setzen das Thema Multithreading im Linux-Kernel fort. Das letzte Mal habe ich über Unterbrechungen, deren Verarbeitung und Tasklets gesprochen, und da ursprünglich vorgesehen war, dass dies ein einziger Artikel sein würde, werde ich mich in meiner Geschichte über Workqueue auf Tasklets beziehen, vorausgesetzt, dass der Leser mit ihnen bereits vertraut ist.
Wie beim letzten Mal werde ich versuchen, meine Geschichte so detailliert und detailliert wie möglich zu gestalten.

Artikel der Reihe:

  1. Multitasking im Linux-Kernel: Workqueue

Arbeitswarteschlange

Arbeitswarteschlange- Dies sind komplexere und schwerere Einheiten als Tasklets. Ich werde hier nicht einmal versuchen, alle Feinheiten der Implementierung zu beschreiben, aber ich hoffe, dass ich die wichtigsten Dinge mehr oder weniger detailliert analysieren kann.
Workqueues dienen wie Tasklets der verzögerten Interrupt-Verarbeitung (obwohl sie auch für andere Zwecke genutzt werden können), werden aber im Gegensatz zu Tasklets im Kontext eines Kernel-Prozesses ausgeführt; sie müssen dementsprechend nicht atomar sein und können Sleep nutzen ()-Funktion, verschiedene Synchronisierungstools usw.

Lassen Sie uns zunächst verstehen, wie der Verarbeitungsprozess der Arbeitswarteschlange im Allgemeinen organisiert ist. Das Bild zeigt es sehr grob und vereinfacht, wie alles tatsächlich abläuft, wird weiter unten ausführlich beschrieben.

An dieser dunklen Materie sind mehrere Wesen beteiligt.
Erstens, Arbeitsmittel(nur kurz) ist eine Struktur, die die Funktion (z. B. einen Interrupt-Handler) beschreibt, die wir planen möchten. Sie kann als Analogon der Tasklet-Struktur betrachtet werden. Bei der Planung wurden Tasklets zu für den Benutzer verborgenen Warteschlangen hinzugefügt, aber jetzt müssen wir eine spezielle Warteschlange verwenden – Arbeitswarteschlange.
Tasklets werden von der Scheduler-Funktion geharkt und die Arbeitswarteschlange wird von speziellen Threads namens Workern verarbeitet.
Arbeiter ermöglichen die asynchrone Ausführung von Arbeiten aus der Arbeitswarteschlange. Obwohl sie Arbeit in der Reihenfolge der Rotation nennen, ist im allgemeinen Fall von einer strikten, sequentiellen Ausführung keine Rede: Schließlich finden hier Vorrang, Schlaf, Warten usw. statt.

Im Allgemeinen sind Worker Kernel-Threads, das heißt, sie werden vom Haupt-Linux-Kernel-Scheduler gesteuert. Allerdings greifen die Arbeitnehmer teilweise in die Planung zur zusätzlichen Organisation der parallelen Arbeitsausführung ein. Darauf wird weiter unten noch näher eingegangen.

Um die Hauptfunktionen des Workqueue-Mechanismus zu skizzieren, schlage ich vor, die API zu erkunden.

Über die Warteschlange und ihre Erstellung

alloc_workqueue(fmt, flags, max_active, args...)
Die Parameter fmt und args sind das printf-Format für den Namen und die Argumente dafür. Der Parameter max_activate ist für die maximale Anzahl an Arbeiten verantwortlich, die aus dieser Warteschlange parallel auf einer CPU ausgeführt werden können.
Eine Warteschlange kann mit den folgenden Flags erstellt werden:
  • WQ_HIGHPRI
  • WQ_UNBOUND
  • WQ_CPU_INTENSIVE
  • WQ_FREEZABLE
  • WQ_MEM_RECLAIM
Besonderes Augenmerk sollte auf die Flagge gelegt werden WQ_UNBOUND. Basierend auf dem Vorhandensein dieses Flags werden Warteschlangen in gebundene und nicht angebundene Warteschlangen unterteilt.
In verknüpften Warteschlangen Beim Hinzufügen werden Arbeiten an die aktuelle CPU gebunden, d. h. in solchen Warteschlangen werden Arbeiten auf dem Kern ausgeführt, der sie plant. In dieser Hinsicht ähneln gebundene Warteschlangen Tasklets.
In nicht verbundenen Warteschlangen Arbeiten können auf jedem Kern ausgeführt werden.

Ein wichtiges Merkmal der Workqueue-Implementierung im Linux-Kernel ist die zusätzliche Organisation der parallelen Ausführung, die in gebundenen Warteschlangen vorhanden ist. Im Folgenden wird es ausführlicher beschrieben, aber jetzt möchte ich sagen, dass es so durchgeführt wird, dass möglichst wenig Speicher verwendet wird und der Prozessor nicht im Leerlauf steht. Dies alles wird unter der Annahme umgesetzt, dass eine Arbeit nicht zu viele Prozessorzyklen beansprucht.
Dies ist bei nicht angehängten Warteschlangen nicht der Fall. Im Wesentlichen stellen solche Warteschlangen den Mitarbeitern lediglich Kontext zur Verfügung und starten sie so früh wie möglich.
Daher sollten unattached Queues verwendet werden, wenn eine CPU-intensive Arbeitslast zu erwarten ist, da in diesem Fall der Scheduler für die parallele Ausführung auf mehreren Kernen sorgt.

Analog zu Tasklets kann den Werken eine Ausführungspriorität zugewiesen werden, normal oder hoch. Die Priorität gilt für die gesamte Warteschlange. Standardmäßig hat die Warteschlange normale Priorität, und wenn Sie das Flag setzen WQ_HIGHPRI, dann entsprechend hoch.

Flagge WQ_CPU_INTENSIVE ist nur für gebundene Warteschlangen sinnvoll. Dieses Flag ist eine Verweigerung der Teilnahme an einer zusätzlichen Organisation zur parallelen Ausführung. Dieses Flag sollte verwendet werden, wenn erwartet wird, dass die Arbeit viel CPU-Zeit beansprucht. In diesem Fall ist es besser, die Verantwortung auf den Scheduler zu verlagern. Dies wird im Folgenden näher beschrieben.

Flaggen WQ_FREEZABLE Und WQ_MEM_RECLAIM sind spezifisch und gehen über den Rahmen des Themas hinaus, daher werden wir nicht im Detail darauf eingehen.

Manchmal ist es sinnvoll, keine eigenen Warteschlangen zu erstellen, sondern gemeinsame zu verwenden. Die wichtigsten:

  • system_wq – gebundene Warteschlange für schnelles Arbeiten
  • system_long_wq – eine gebundene Warteschlange für Arbeiten, deren Ausführung voraussichtlich lange dauern wird
  • system_unbound_wq – ungebundene Warteschlange

Über Arbeit und ihre Planung

Kommen wir nun zu den Arbeiten. Schauen wir uns zunächst die Initialisierungs-, Deklarations- und Vorbereitungsmakros an:
DECLARE(_DELAYED)_WORK(name, void (*function)(struct work_struct *work)); /* zur Kompilierungszeit */ INIT(_DELAYED)_WORK(_work, _func); /* während der Ausführung */ PREPARE(_DELAYED)_WORK(_work, _func); /* um die ausgeführte Funktion zu ändern */
Werke werden mit den folgenden Funktionen zur Warteschlange hinzugefügt:
bool queue_work(struct workqueue_struct *wq, struct work_struct *work); bool queue_delayed_work(struct workqueue_struct *wq, struct verzögert_work *dwork, vorzeichenlose lange Verzögerung); /* Arbeit wird erst nach Ablauf der Verzögerung zur Warteschlange hinzugefügt */
Es lohnt sich, näher darauf einzugehen. Obwohl wir eine Warteschlange als Parameter angeben, werden Arbeiten tatsächlich nicht in der Arbeitswarteschlange selbst platziert, wie es scheinen könnte, sondern in einer völlig anderen Entität – in der Warteschlangenliste der Struktur worker_pool. Struktur worker_pool ist in der Tat die wichtigste Einheit bei der Organisation des Arbeitswarteschlangenmechanismus, obwohl sie für den Benutzer hinter den Kulissen bleibt. Mit ihnen arbeiten die Arbeiter, und in ihnen sind alle grundlegenden Informationen enthalten.

Sehen wir uns nun an, welche Pools im System vorhanden sind.
Zunächst Pools für gebundene Warteschlangen (im Bild). Für jede CPU werden statisch zwei Worker-Pools zugewiesen: einer für Arbeiten mit hoher Priorität, der andere für Arbeiten mit normaler Priorität. Das heißt, wenn wir vier Kerne haben, gibt es nur acht angeschlossene Pools, obwohl die Arbeitswarteschlange beliebig viele sein kann.
Wenn wir eine Arbeitswarteschlange erstellen, wird dieser jeder CPU ein Dienst zugewiesen pool_workqueue(pwq). Jede dieser pool_workqueues ist einem Worker-Pool zugeordnet, der derselben CPU zugeordnet ist und in der Priorität dem Warteschlangentyp entspricht. Über sie interagiert die Arbeitswarteschlange mit dem Worker-Pool.
Worker führen wahllos Arbeiten aus dem Worker-Pool aus, ohne zu unterscheiden, zu welcher Arbeitswarteschlange sie ursprünglich gehörten.

Für nicht angehängte Warteschlangen werden Worker-Pools dynamisch zugewiesen. Alle Warteschlangen können entsprechend ihrer Parameter in Äquivalenzklassen eingeteilt werden, und für jede dieser Klassen wird ein eigener Worker-Pool erstellt. Der Zugriff erfolgt über eine spezielle Hash-Tabelle, wobei der Schlüssel ein Parametersatz und der Wert jeweils der Worker-Pool ist.
Tatsächlich ist bei ungebundenen Warteschlangen alles etwas komplizierter: Wenn für gebundene Warteschlangen pwq und Warteschlangen für jede CPU erstellt wurden, werden sie hier für jeden NUMA-Knoten erstellt, aber dies ist eine zusätzliche Optimierung, die wir nicht im Detail betrachten werden.

Allerlei Kleinigkeiten

Um das Bild zu vervollständigen, werde ich auch einige Funktionen der API nennen, aber nicht im Detail darauf eingehen:
/* Abschluss erzwingen */ bool flush_work(struct work_struct *work); bool Flush_delayed_work(struct verzögert_work *dwork); /* Ausführung der Arbeit abbrechen */ bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work(struct verzögert_work *dwork); bool cancel_delayed_work_sync(struct verzögert_work *dwork); /* Eine Warteschlange löschen */ void destroy_workqueue(struct workqueue_struct *wq);

Wie Arbeiter ihre Arbeit machen

Nachdem wir nun mit der API vertraut sind, versuchen wir, genauer zu verstehen, wie alles funktioniert und verwaltet wird.
Jeder Pool verfügt über eine Gruppe von Mitarbeitern, die Aufgaben erledigen. Darüber hinaus ändert sich die Zahl der Arbeitnehmer dynamisch und passt sich der aktuellen Situation an.
Wie wir bereits herausgefunden haben, handelt es sich bei Workern um Threads, die im Kontext des Kernels Arbeiten ausführen. Der Worker ruft sie der Reihe nach nacheinander aus dem zugehörigen Worker-Pool ab, und die Worker können, wie wir bereits wissen, zu verschiedenen Quellwarteschlangen gehören.

Worker können sich bedingt in drei logischen Zuständen befinden: Sie können sich im Leerlauf befinden, ausgeführt werden oder in der Verwaltung sein.
Arbeiter kann ruhen und nichts tun. Dies ist beispielsweise dann der Fall, wenn alle Arbeiten bereits ausgeführt werden. Wenn ein Worker in diesen Zustand wechselt, geht er in den Ruhezustand und wird dementsprechend erst ausgeführt, wenn er aufgeweckt wird.
Wenn keine Poolverwaltung erforderlich ist und die Liste der geplanten Arbeiten nicht leer ist, beginnt der Arbeiter mit der Ausführung. Wir werden solche Arbeiter herkömmlicherweise nennen läuft.
Bei Bedarf übernimmt der Arbeiter die Rolle Manager Schwimmbad. Ein Pool kann entweder nur einen leitenden Arbeiter oder überhaupt keinen Arbeiter haben. Seine Aufgabe besteht darin, die optimale Anzahl von Arbeitern pro Pool aufrechtzuerhalten. Wie macht er das? Zunächst werden Worker gelöscht, die längere Zeit im Leerlauf waren. Zweitens entstehen neue Arbeitskräfte, wenn drei Bedingungen gleichzeitig erfüllt sind:

  • Es sind noch Aufgaben zu erledigen (funktioniert im Pool)
  • keine untätigen Arbeiter
  • es gibt keine arbeitenden Arbeiter (d. h. aktive und nicht schlafende)
Die letzte Bedingung hat jedoch ihre eigenen Nuancen. Wenn die Poolwarteschlangen nicht angehängt sind, werden laufende Worker nicht berücksichtigt; für sie ist diese Bedingung immer wahr. Das Gleiche gilt für den Fall, dass ein Arbeiter eine Aufgabe von einer verknüpften ausführt, jedoch mit der Flagge WQ_CPU_INTENSIVE, Warteschlangen. Darüber hinaus stellt es sich bei gebundenen Warteschlangen heraus, dass einige von ihnen als arbeitend gezählt werden, andere jedoch nicht, da Arbeiter mit Werken aus dem gemeinsamen Pool arbeiten (der in der Abbildung oben einer von zwei für jeden Kern ist). Daraus folgt auch die Ausführung von Arbeiten WQ_CPU_INTENSIVE Warteschlangen werden möglicherweise nicht sofort gestartet, beeinträchtigen jedoch nicht die Ausführung anderer Arbeiten. Jetzt sollte klar sein, warum dieses Flag so heißt und warum es verwendet wird, wenn wir damit rechnen, dass die Arbeit lange dauern wird.

Die Abrechnung der Arbeitskräfte erfolgt direkt über den Haupt-Linux-Kernel-Scheduler. Dieser Kontrollmechanismus gewährleistet ein optimales Parallelitätsniveau und verhindert, dass die Arbeitswarteschlange zu viele Arbeiter erstellt, aber auch nicht dazu führt, dass die Arbeit unnötig lange wartet.

Interessierte können sich die Worker-Funktion im Kernel ansehen, sie heißt worker_thread().

Alle beschriebenen Funktionen und Strukturen finden Sie im Detail in den Dateien include/linux/workqueue.h, kernel/workqueue.c Und kernel/workqueue_internal.h. Es gibt auch eine Dokumentation zur Arbeitswarteschlange in Documentation/workqueue.txt.

Es ist auch erwähnenswert, dass der Workqueue-Mechanismus im Kernel nicht nur für die verzögerte Interrupt-Verarbeitung verwendet wird (obwohl dies ein ziemlich häufiges Szenario ist).

Daher haben wir uns die Mechanismen für die verzögerte Interrupt-Behandlung im Linux-Kernel angeschaut – Tasklet und Workqueue, die eine besondere Form des Multitaskings darstellen. Über Interrupts, Tasklets und Workqueues können Sie im Buch „Linux Device Drivers“ von Jonathan Corbet, Greg Kroah-Hartman, Alessandro Rubini nachlesen, allerdings sind die Informationen dort teilweise veraltet.

LABORARBEIT Nr. 3

MULTITASKING-PROGRAMMIERUNG INLINUX

1. Ziel der Arbeit: Machen Sie sich mit dem gcc-Compiler, Programm-Debugging-Techniken und Funktionen für die Arbeit mit Prozessen vertraut.

2. Kurze theoretische Informationen.

Der Mindestsatz an gcc-Compiler-Schaltern sind - Wall (alle Fehler und Warnungen anzeigen) und - o (Ausgabedatei):

gcc – Wall – o print_pid print_pid. C

Der Befehl erstellt eine ausführbare Datei print_pid.

Die C-Standardbibliothek (libc, in Linux in glibc implementiert) nutzt die Multitasking-Fähigkeiten von Unix System V (im Folgenden SysV). In libc ist der Typ pid_t als Ganzzahl definiert, die eine PID enthalten kann. Die Funktion, die die PID des aktuellen Prozesses meldet, hat einen Prototyp von pid_t getpid(void) und ist zusammen mit pid_t in unistd definiert. h und sys/types. H).

Um einen neuen Prozess zu erstellen, verwenden Sie die Fork-Funktion:

pid_t fork(void)

Durch Einfügen einer Verzögerung zufälliger Länge mithilfe der Sleep- und Rand-Funktionen können Sie den Effekt von Multitasking deutlicher erkennen:

Dadurch wird das Programm für eine zufällige Anzahl von Sekunden „schlafen“: von 0 bis 3.

Um eine Funktion als untergeordneten Prozess aufzurufen, rufen Sie sie einfach nach der Verzweigung auf:

// Wenn ein untergeordneter Prozess ausgeführt wird, rufen Sie die Funktion auf

pid=process(arg);

// Verlasse den Prozess

Oft ist es notwendig, ein anderes Programm als Kindprozess auszuführen. Verwenden Sie dazu die Funktionen der Exec-Familie:

// wenn ein untergeordneter Prozess läuft, dann rufe das Programm auf


if (execl("./file","file",arg, NULL)<0) {

printf("FEHLER beim Startvorgang\n");

else printf("Prozess gestartet (pid=%d)\n", pid);

// Verlasse den Prozess

Oftmals muss ein übergeordneter Prozess Informationen mit seinen untergeordneten Prozessen austauschen oder sich zumindest mit ihnen synchronisieren, um Vorgänge zum richtigen Zeitpunkt auszuführen. Eine Möglichkeit, Prozesse zu synchronisieren, sind die Funktionen „wait“ und „waitpid“:

#enthalten

#enthalten

pid_t wait(int *status) – unterbricht die Ausführung des aktuellen Prozesses, bis einer seiner untergeordneten Prozesse beendet wird.

pid_t waitpid (pid_t pid, int *status, int options) – unterbricht die Ausführung des aktuellen Prozesses, bis der angegebene Prozess abgeschlossen ist, oder prüft, ob der angegebene Prozess abgeschlossen ist.

Wenn Sie den Status des untergeordneten Prozesses beim Beenden und den zurückgegebenen Wert ermitteln müssen, verwenden Sie das Makro WEXITSTATUS und übergeben ihm den Status des untergeordneten Prozesses als Parameter.

status=waitpid(pid,&status, WNOHANG);

if (pid == Status) (

printf("PID: %d, Ergebnis = %d\n", pid, WEXITSTATUS(status)); )

Um die Prioritäten erzeugter Prozesse zu ändern, werden setpriority und Funktionen verwendet. Die Prioritäten liegen im Bereich von -20 (höchste) bis 20 (niedrigste), der Normalwert ist 0. Beachten Sie, dass nur ein Superuser die Priorität über den Normalwert hinaus erhöhen kann!

#enthalten

#enthalten

int Prozess(int i) (

setpriority(PRIO_PROCESS, getpid(),i);

printf("Prozess %d ThreadID: %d arbeitet mit Priorität %d\n",i, getpid(),getpriority(PRIO_PROCESS, getpid()));

return(getpriority(PRIO_PROCESS, getpid()));

Um einen Prozess zu beenden, verwenden Sie die Kill-Funktion:

#enthalten

#enthalten

int kill(pid_t pid, int sig);

Wenn pid > 0, dann gibt es die PID des Prozesses an, an den das Signal gesendet wird. Wenn pid = 0, dann wird das Signal an alle Prozesse der Gruppe gesendet, zu der der aktuelle Prozess gehört.

sig - Signaltyp. Einige Arten von Signalen unter Linux:

SIGKILL Dieses Signal bewirkt, dass der Prozess sofort beendet wird. Der Prozess kann dieses Signal nicht ignorieren.

SIGTERM Dieses Signal ist eine Aufforderung, den Prozess zu beenden.

SIGCHLD Das System sendet dieses Signal an einen Prozess, wenn einer seiner untergeordneten Prozesse beendet wird. Beispiel:

if (pid[i] == Status) (

printf("ThreadID: %d mit Status %d\n beendet", pid[i], WEXITSTATUS(status));

sonst kill(pid[i],SIGKILL);

3. Methodische Anleitung.

3.1. Um sich mit den gcc-Compileroptionen und Beschreibungen der C-Sprachfunktionen vertraut zu machen, verwenden Sie die Anweisungen man und info.

3.2. Zum Debuggen von Programmen kann bequem der integrierte Editor des Dateimanagers Midnight Commander (MC) verwendet werden, der verschiedene Sprachkonstrukte farblich hervorhebt und in der obersten Zeile die Position des Cursors in der Datei (Zeile, Spalte) anzeigt des Bildschirms.

3.3. Der Dateimanager Midnight Commander verfügt über einen Befehlspuffer, der über eine Tastenkombination aufgerufen werden kann - H, das mit den Cursorpfeilen (nach oben und unten) verschoben werden kann. Um einen Befehl aus dem Puffer in die Befehlszeile einzufügen, verwenden Sie die Taste , um einen Befehl über die Puffertasten zu bearbeiten<- и ->, Und .


3.4. Denken Sie daran, dass das aktuelle Verzeichnis nicht im Pfad enthalten ist, also von Befehlszeile Sie müssen das Programm als „./print_pid“ ausführen. Bewegen Sie in MC einfach den Mauszeiger über die Datei und klicken Sie .

3.5. Um das Ergebnis der Programmausführung anzuzeigen, verwenden Sie die Tastenkombination - O. Sie funktionieren auch im Dateibearbeitungsmodus.

3.6. Um die Ergebnisse der Programmausführung zu protokollieren, empfiehlt es sich, die Ausgabe von der Konsole in eine Datei umzuleiten: ./test > Ergebnis. txt

3.7. Um auf Dateien zuzugreifen, die am erstellt wurden Linux-Server, verwenden Sie das FTP-Protokoll, Client-Programm das in Windows 2000 verfügbar und integriert ist Dateimanager WEIT. Dabei Konto und das Passwort ist das gleiche wie bei der Verbindung über SSH.

4.1. Machen Sie sich mit den GCC-Compileroptionen und -methoden zum Debuggen von Programmen vertraut.

4.2. Für Varianten von Aufgaben aus Laborarbeit Nr. 1 schreiben und debuggen Sie ein Programm, das den generierten Prozess implementiert.

4.3. Für Aufgabenoptionen von Labor arbeit Nr. 1: Schreiben und debuggen Sie ein Programm, das einen übergeordneten Prozess implementiert, der untergeordnete Prozesse aufruft und deren Status überwacht – Programme (die auf deren Abschluss warten oder sie zerstören, je nach Option).

4.4. Schreiben und debuggen Sie für Varianten von Aufgaben aus Laborarbeit Nr. 1 ein Programm, das einen übergeordneten Prozess implementiert, der den Status von untergeordneten Prozessen – Funktionen – aufruft und überwacht (je nach Variante auf deren Abschluss wartet oder sie zerstört).

5. Optionen für Aufgaben. Siehe Optionen für Aufgaben aus Laborarbeit Nr. 1

6. Inhalt des Berichts.

6.1. Ziel der Arbeit.

6.2. Aufgabenoption.

6.3. Programmlisten.

6.4. Programmausführungsprotokolle.

7. Kontrollfragen.

7.1. Funktionen zum Kompilieren und Ausführen von C-Programmen unter Linux.

7.2. Was ist PID, wie kann man es im Betriebssystem und Programm ermitteln?

7.3. Fork-Funktion – Zweck, Anwendung, Rückgabewert.

7.4. Wie führe ich eine Funktion in einem erzeugten Prozess aus? Programm?

7.5. Möglichkeiten zur Synchronisierung von übergeordneten und untergeordneten Prozessen.

7.6. Wie kann ich den Status des erzeugten Prozesses bei seiner Beendigung und den zurückgegebenen Wert herausfinden?

7.7. Wie verwaltet man Prozessprioritäten?

7.8. Wie beendet man einen Prozess im Betriebssystem und Programm?

Geben Sie den folgenden Befehl in Ihre Shell ein:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Auf dem Bildschirm wird eine Liste aller im System laufenden Prozesse angezeigt. Wenn Sie die Anzahl der Prozesse zählen möchten, geben Sie etwa Folgendes ein:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ ps -e --no-headers | nl | tail -n 1

74 4650 Punkte/0 00:00:00 Schwanz

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Die erste Zahl ist die Anzahl der im System laufenden Prozesse. KDE-Benutzer können das Programm kpm verwenden und Gnome-Benutzer können das Programm gnome-system-monitor verwenden, um Informationen über Prozesse zu erhalten. Dafür ist Linux da, um dem Benutzer die Möglichkeit zu geben, dasselbe zu tun verschiedene Wege.

Es stellt sich die Frage: „Was ist ein Prozess?“ Prozesse in Linux sind wie Dateien axiomatische Konzepte. Manchmal wird der Prozess mit identifiziert laufendes Programm Dies ist jedoch nicht immer der Fall. Nehmen wir an, dass ein Prozess eine Arbeitseinheit eines Systems ist, die etwas tut. Unter Multitasking versteht man die Fähigkeit, dass mehrere Prozesse gleichzeitig in einem System koexistieren.

Linux ist ein Multitasking-Betriebssystem. Das bedeutet, dass die Prozesse darin gleichzeitig ablaufen. Dies ist natürlich eine bedingte Formulierung. Der Linux-Kernel wechselt ständig Prozesse, das heißt, er gibt jedem von ihnen von Zeit zu Zeit etwas Prozessorzeit. Die Umschaltung erfolgt recht schnell, sodass es für uns so aussieht, als würden die Prozesse gleichzeitig ablaufen.

Einige Prozesse können andere Prozesse generieren und eine Baumstruktur bilden. Produzierende Prozesse werden Eltern oder übergeordnete Prozesse genannt, und untergeordnete Prozesse werden Kinder oder untergeordnete Prozesse genannt. An der Spitze dieses „Baums“ befindet sich der Init-Prozess, der vom Kernel während des Systemstartvorgangs automatisch gestartet wird.

Jeder Prozess im System ist einem Paar nicht negativer Ganzzahlen zugeordnet: einer Prozesskennung PID (Process IDentifier) ​​und einer übergeordneten Prozesskennung PPID (Parent Process IDentifier). Für jeden Prozess ist die PID eindeutig (zu einem bestimmten Zeitpunkt) und die PPID entspricht der ID des übergeordneten Prozesses. Wenn Sie den Befehl ps -ef in die Shell eingeben, wird eine Liste der Prozesse mit den Werten ihrer PID und PPID (zweite bzw. dritte Spalte) angezeigt. Ein Beispiel für diesen Befehl:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

UID PID PPID C STIME TTY TIME CMD

root 1 0 0 17:08? 00:00:00 /sbin/init

root 2 0 0 17:08? 00:00:00

root 3 2 0 17:08? 00:00:00

root 4 2 0 17:08? 00:00:00

root 5 2 0 17:08? 00:00:00

root 6 2 0 17:08? 00:00:00

root 7 2 0 17:08? 00:00:00

root 8 2 0 17:08? 00:00:00

root 9 2 0 17:08? 00:00:00

root 10 2 0 17:08? 00:00:00

root 11 2 0 17:08? 00:00:00

root 12 2 0 17:08? 00:00:00

root 13 2 0 17:08? 00:00:00

root 14 2 0 17:08? 00:00:00

root 15 2 0 17:08? 00:00:00

root 16 2 0 17:08? 00:00:00

root 17 2 0 17:08? 00:00:00

root 18 2 0 17:08? 00:00:00

root 19 2 0 17:08? 00:00:00

df00 16389 16387 0 20:10 pts/1 00:00:00 /bin/bash

df00 17446 2538 0 20:26? 00:00:00

df00 18544 2932 0 20:41 pts/2 00:00:00 /bin/bash -l

df00 19010 18544 0 20:48 pts/2 00:00:00 ps -ef

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Es ist zu beachten, dass der Init-Prozess immer einen Bezeichner von 1 und eine PPID von 0 hat. Obwohl es in Wirklichkeit keinen Prozess mit einem Bezeichner von 0 gibt. Der Prozessbaum kann auch mit der Option --forest des ps-Programms visualisiert werden:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

2? 00:00:00 kthreadd

3 ?00:00:00 \_ migration/0

4 ?00:00:00 \_ ksoftirqd/0

5 ?00:00:00 \_ watchdog/0

6 ?00:00:00 \_ Migration/1

7 ?00:00:00 \_ ksoftirqd/1

8 ?00:00:00 \_ watchdog/1

9 ?00:00:00 \_ Ereignisse/0

10 ?00:00:00 \_ Ereignisse/1

11 ?00:00:00 \_ cpuset

12 ?00:00:00 \_ khelper

13 ?00:00:00 \_ netns

14 ?00:00:00 \_ async/mgr

15 ?00:00:00 \_ kintegrityd/0

16 ?00:00:00 \_ kintegrityd/1

18544 Punkte/2 00:00:00 \_ bash

16388 ?00:00:00 \_ gnome-pty-helpe

16389 Punkte/1 00:00:00 \_ bash

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Wenn Sie das ps-Programm ohne Argumente aufrufen, wird eine Liste der Prozesse angezeigt, die zur aktuellen Gruppe gehören, also unter dem aktuellen Terminal laufen.

Verwendung von getpid() und getppid()

Ein Prozess kann seine PID sowie seine übergeordnete PPID mithilfe der Systemaufrufe getpid() und getppid() ermitteln.

Die Systemaufrufe getpid() und getppid() haben die folgenden Prototypen:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

pid_t getpid(void);

pid_t getppid(void);

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Um getpid() und getppid() zu verwenden, müssen die Header-Dateien unistd.h und sys/types.h (für den Typ pid_t) mithilfe der #include-Direktive in das Programm eingebunden werden. Der Aufruf von getpid() gibt die aktuelle Prozess-ID (PID) zurück und getppid() gibt die übergeordnete ID (PPID) zurück. pid_t ist ein ganzzahliger Typ, dessen Dimension vom jeweiligen System abhängt. Werte dieses Typs können als reguläre Ganzzahlen vom Typ int verarbeitet werden.

Lassen Sie uns nun überlegen ein einfaches Programm, das die PID und PPID anzeigt und dann einfriert, bis der Benutzer drückt .

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

#enthalten

#enthalten

#enthalten

pid_t pid, ppid;

pid = getpid();

ppid = getppid();

printf("PID: %d\n", pid);

printf("PPID: %d\n", ppid);

fprintf(stderr, „Drücken Sie beenden...");

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Schauen wir uns nun an, wie dieses Programm funktioniert. Dazu kompilieren wir es und führen es aus:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ gcc -o getpid getpid.c

Drücken Sie beenden...

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

Jetzt, ohne zu drücken , öffnen Sie ein anderes Terminalfenster und überprüfen Sie, ob die Systemaufrufe getpid() und getppid() ordnungsgemäß funktionieren:

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

$ ps -ef | grep getpid

df00 19724 19702 0 20:58 pts/3 00:00:00 ./main

df00 19856 18544 0 21:00 pts/2 00:00:00 grep --colour=auto main

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−



 


Lesen:



HTC Sync Manager: Verwendung, Download und Problemlösung

HTC Sync Manager: Verwendung, Download und Problemlösung

HTC Sync ist ein Tool zum Verbinden von HTC-Smartphones mit einem Computer, zum Datenaustausch und zur Datensynchronisierung zwischen ihnen. Das Programm wird installiert auf...

Xperia Z3 und Xperia Z3 Compact: Test und Vergleich Ein Webbrowser ist eine Softwareanwendung für den Zugriff auf und die Anzeige von Informationen im Internet

Xperia Z3 und Xperia Z3 Compact: Test und Vergleich Ein Webbrowser ist eine Softwareanwendung für den Zugriff auf und die Anzeige von Informationen im Internet

Anlass für die Erstellung dieses Menüvergleichs war eine so häufige Frage, was man wählen soll – Sony Xperia Z3 oder Xperia Z3 Compact? Am häufigsten...

Neue Kyivstar-Tarife Verbindung nicht verfügbar

Neue Kyivstar-Tarife Verbindung nicht verfügbar

Das Unternehmen Kiewtsar bietet seinen Abonnenten auf vertraglicher Basis den Anschluss an verschiedene Tarifpakete an. Zu den vielen Vorteilen dieser...

Das Fly-Phone-Internet funktioniert nicht

Das Fly-Phone-Internet funktioniert nicht

Anleitung Finden Sie es heraus – ist es möglich, mit Ihrem Telefonmodell eine solche Verbindung zum Internet herzustellen? Dies können Sie der Liste der technischen Spezifikationen entnehmen...

Feed-Bild RSS