2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Signal (Signal) ist ein Software-Interrupt. Es handelt sich um eine Methode zum Übertragen von Nachrichten zwischen Prozessen. Es wird verwendet, um den Prozess darüber zu informieren, dass ein Ereignis aufgetreten ist, es kann jedoch keine Daten an den Prozess weitergeben.
Es gibt viele Gründe, warum Signale in der Shell generiert werdenkill
Undkillall
Befehl zum Senden eines Signals:
kill -信号的类型 进程编号
killall -信号的类型 进程名
Signalname | Signalwert | Standardverarbeitungsaktion | Grund für die Signalisierung |
---|---|---|---|
SEUFZEND | 1 | A | Das Terminal hängt oder der Steuerungsprozess wird beendet |
ZEICHEN | 2 | A | Tastaturunterbrechung Strg+c |
SIGQUIT | 3 | C | Die Escape-Taste der Tastatur wird gedrückt |
SIEGEL | 4 | C | Illegale Anweisung |
SIGTRAP | 5 | C | Haltepunktanweisungen |
SIGABRT | 6 | C | Abbruchsignal ausgegeben von abort(3) |
SIGBUS | 7 | C | Busfehler |
SIGFPE | 8 | C | Gleitkomma-Ausnahme |
SIGKILL | 9 | A | kill -9 beendet den Prozess, dieses Signal kann nicht abgefangen und ignoriert werden |
SIGUSR1 | 10 | A | Benutzerdefiniertes Signal 1 |
SIGSEGV | 11 | C | Ungültige Speicherreferenz (Array außerhalb der Grenzen, Nullzeigeroperation) |
SIGUSR2 | 12 | A | Benutzerdefiniertes Signal 2 |
SIGPIPE | 13 | A | Daten ohne Lesevorgang in eine Pipe schreiben |
SIGALRM | 14 | A | Weckersignal, Signal, das von der Funktion alarm() gesendet wird |
SIGTERM | 15 | A | Beendigungssignal, das standardmäßig gesendete Signal |
SIGSTKFLT | 16 | A | Stapelfehler |
SIGCHLD | 17 | B | Wird ausgegeben, wenn der untergeordnete Prozess endet |
SIGCONT | 18 | D | Setzen Sie einen gestoppten Prozess fort |
SIGSTOP | 19 | D | Prozess stoppen |
SIGTSTP | 20 | D | Terminal drücken Sie die Stopp-Taste |
SIGTTIN | 21 | D | Hintergrundprozessanforderungen zum Lesen des Terminals |
SIGTTOU | 22 | D | Der Hintergrundprozess fordert zum Schreiben auf das Terminal auf |
SIGURG | 23 | B | Notfallzustandserkennung (Steckdose) |
SIGXCPU | 24 | C | CPU-Zeitlimit überschritten |
SIGXFSZ | 25 | C | Dateigrößenbeschränkung überschritten |
SIGVTALRM | 26 | A | virtuelles Taktsignal |
SIGPROF | 27 | A | Analysieren Sie Taktsignale |
ZEICHENWINCH | 28 | B | Fenstergröße ändert sich |
SIGPOLL | 29 | B | Abfrage (Sys V) |
SIGPWR | 30 | A | Stromausfall |
SIGSYS | 31 | C | Unzulässiger Systemaufruf |
Die Standardaktion von A besteht darin, den Prozess zu beenden.
Die Standardaktion von B besteht darin, dieses Signal zu ignorieren.
Die Standardaktion von C besteht darin, den Prozess zu beenden und einen Kernel-Image-Dump zu erstellen.
Die Standardaktion von D besteht darin, den Prozess zu stoppen, und das Programm, das in den gestoppten Zustand wechselt, kann weiter ausgeführt werden.
Es gibt drei Möglichkeiten für Prozesse, Signale zu verarbeiten:
signal()
Funktionen können festlegen, wie das Programm mit Signalen umgeht.
Funktionsdeklaration:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Parameterbeschreibung:
sig
:Geben Sie das zu erfassende Signal an.func
: Zeiger auf Signalverarbeitungsfunktion. Die Verarbeitungsfunktion muss einen ganzzahligen Parameter empfangen, der die erfasste Signalnummer ist.SIG_DFL
Das Makro :SIG_DFL stellt die Standard-Signalverarbeitungsmethode dar.verwendenSIG_DFL
alssignal
Der zweite Parameter der Funktion gibt an, dass die Standardverarbeitungsmethode des Systems für das Signal verwendet wird.SIG_IGN
Das Makro :SIG_IGN bedeutet, das Signal zu ignorieren.verwendenSIG_IGN
alssignal
Der zweite Parameter der Funktion gibt an, dass der Prozess, wenn er das Signal empfängt, es ignoriert und keine Verarbeitung durchführt. Dadurch kann verhindert werden, dass Prozesse unter bestimmten Umständen unerwartet beendet oder unterbrochen werden.SIG_ERR
:SIG_ERR
Makros werden verwendet, um Fehler anzuzeigen.es ist nicht sosignal
Der zweite Parameter der Funktion wird stattdessen als verwendetsignal
Der Rückgabewert der Funktion zeigt an, dass der Aufruf fehlgeschlagen ist.Wennsignal
Wenn der Aufruf der Funktion fehlschlägt, wird sie zurückgegebenSIG_ERR
.Dies wird typischerweise zur Erkennung und Verarbeitung verwendetsignal
Fehler beim Funktionsaufruf.Wenn Sie das Dienstprogramm im Hintergrund ausführen möchten, ist es keine gute Idee, es zu beenden, denn wenn der Prozess beendet wird, wird er plötzlich beendet und es werden keine Nacharbeiten veranlasst.
Wenn Sie ein Signal an das Dienstprogramm senden, ruft das Dienstprogramm nach dem Empfang des Signals eine Funktion auf und schreibt den Folgecode in die Funktion, und das Programm kann auf geplante Weise beendet werden.
Durch Senden eines Signals von 0 an das Dienstprogramm kann festgestellt werden, ob das Programm aktiv ist.
Das Linux-Betriebssystem bietet kill
Undkillall
Der Befehl sendet ein Signal an das Programm, das Sie verwenden könnenkill()
Bibliotheksfunktionen senden Signale an andere Prozesse.
Funktionsdeklaration:
int kill(pid_t pid, int sig);
kill()
Die Funktion übernimmt die Parametersig
Das angegebene Signal wird an den Parameter übergebenpid
angegebenen Prozess.
Parameter pid
Es gibt mehrere Situationen:
pid > 0
Übergeben Sie das Signal an den Prozess alspid
Verfahren.pid = 0
Leiten Sie das Signal an alle Prozesse in derselben Prozessgruppe wie der aktuelle Prozess weiter. Es wird häufig vom übergeordneten Prozess verwendet, um Signale an den untergeordneten Prozess zu senden.pid < -1
Übergeben Sie das Signal an die Prozessgruppen-ID von|pid|
aller Prozesse.pid = -1
Leitet das Signal an alle Prozesse weiter, die die Berechtigung zum Senden des Signals haben, jedoch nicht an den Prozess, der das Signal gesendet hat.Es gibt 8 Möglichkeiten, einen Prozess zu beenden, davon sind 5 normale Beendigungen:
main()
Für Funktionenreturn
zurückkehren;exit()
Funktion;_exit()
oder_Exit()
Funktion;return
zurückkehren;pthread_exit()
zurückkehren;Es gibt drei Möglichkeiten, den Vorgang abnormal zu beenden:
abort()
Funktionsabbruch;existieren main()
In der Funktion,return
Der zurückgegebene Wert ist der Beendigungsstatus, falls nichtreturn
Erklärung oder Anrufexit()
, dann ist der Beendigungsstatus des Prozesses 0.
Sehen Sie sich in der Shell den Status der Prozessbeendigung an:
echo $?
3 Funktionen zum normalen Beenden des Prozesses (exit()
Und_Exit()
wird durch ISO C spezifiziert,_exit()
wird durch POSIX angegeben):
void exit(int status);
void _exit(int status);
void _Exit(int status);
status
Der Status der Prozessbeendigung.
return
Gibt an, dass bei der Rückkehr der Funktion der Destruktor des lokalen Objekts aufgerufen wird.main()
in Funktionreturn
Der Destruktor des globalen Objekts wird ebenfalls aufgerufen.exit()
Gibt an, dass der Prozess beendet werden soll. Der Destruktor des lokalen Objekts wird nicht aufgerufen, sondern nur der Destruktor des globalen Objekts._exit()
Und_Exit()
Beenden Sie den Vorgang direkt und es werden keine Aufräumarbeiten durchgeführt.Der Prozess ist verfügbar atexit()
Durch die Funktionsregistrierung werden Funktionen (bis zu 32) beendet. Diese Funktionen werden ausgeführtexit()
Automatisch aufgerufen.
int atexit(void (*function)(void));
exit()
Die Reihenfolge, in der die Terminierungsfunktionen aufgerufen werden, ist ab dem Zeitpunkt der Registrierung umgekehrt.
system()
Die Funktion stellt eine einfache Methode zum Ausführen des Programms bereit und übergibt das Programm und die Parameter, die ausgeführt werden müssen, als Zeichenfolge.system()
Funktioniert einfach.
Funktionsdeklaration:
int system(const char * string);
system()
Der Rückgabewert der Funktion ist problematischer.
system()
Die Funktion gibt einen Wert ungleich Null zurück;system()
Die Funktion gibt 0 zurück;system()
Die Funktion gibt einen Wert ungleich Null zurück.exec
Funktionsfamilien bieten eine weitere Möglichkeit, Programme (Binärdateien oder Shell-Skripte) innerhalb eines Prozesses aufzurufen.
exec
Die Funktionsfamilie wird wie folgt deklariert:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
Beachten:
errno
Mitte.exec
Danach ersetzt das aufgerufene Programm das aufrufende Programm, d.exec
Kein Code, nachdem die Funktion ausgeführt wird.execl()
Undexecv()
, andere werden selten verwendet.Alle Prozesse im gesamten Linux-System liegen in einer Baumstruktur vor.
verwendenpstree
Sie können den Prozessbaum mit dem folgenden Befehl anzeigen:
pstree -p 进程编号
Jeder Prozess verfügt über eine eindeutige Prozess-ID, die durch eine nicht negative Ganzzahl dargestellt wird. Obwohl eindeutig, können Prozess-IDs wiederverwendet werden. Wenn ein Prozess beendet wird, wird seine Prozess-ID zu einem Kandidaten für die Wiederverwendung. Linux verwendet einen verzögerten Wiederverwendungsalgorithmus, sodass sich die ID eines neu erstellten Prozesses von der ID des kürzlich beendeten Prozesses unterscheidet. Dadurch wird verhindert, dass neue Prozesse mit einem beendeten Prozess mit derselben ID verwechselt werden.
Funktion zum Abrufen der Prozess-ID:
pid_t getpid(void); // 获取当前进程的ID。
pid_t getppid(void); // 获取父进程的ID。
Ein vorhandener Prozess kann aufgerufen werdenfork()
Funktion erstellt einen neuen Prozess.
Funktionsdeklaration:
pid_t fork(void);
Darauf ankommenfork()
Der neu erstellte Prozess wird als untergeordneter Prozess bezeichnet.
fork()
Die Funktion wird einmal aufgerufen, kehrt aber zweimal zurück. Der Unterschied zwischen den beiden Rückgaben besteht darin, dass der Rückgabewert des untergeordneten Prozesses 0 ist, während der Rückgabewert des übergeordneten Prozesses die Prozess-ID des neu erstellten untergeordneten Prozesses ist.
Der untergeordnete Prozess und der übergeordnete Prozess werden weiterhin ausgeführtfork()
Der Code danach, Der untergeordnete Prozess ist eine Kopie des übergeordneten Prozesses. Der untergeordnete Prozess verfügt über eine Kopie des Datenraums, des Heaps und des Stapels des übergeordneten Prozesses (Hinweis: Der untergeordnete Prozess besitzt eine Kopie, die nicht mit dem übergeordneten Prozess geteilt wird).
fork()
Danach ist die Ausführungsreihenfolge der übergeordneten und untergeordneten Prozesse undefiniert.
fork()
Lassen Sie den untergeordneten Prozess diese Anforderungen verarbeiten, während der übergeordnete Prozess weiterhin auf die nächste Verbindungsanforderung wartet.fork()
Gleich nach der Rückkehr angerufenexec
。fork()
Eine Funktion besteht darin, dass im übergeordneten Prozess geöffnete Dateideskriptoren in den untergeordneten Prozess kopiert werden und der übergeordnete Prozess und der untergeordnete Prozess denselben Dateioffset verwenden.
Wenn ein übergeordneter Prozess und ein untergeordneter Prozess ohne jegliche Synchronisation in eine Datei schreiben, auf die derselbe Deskriptor verweist, kann es zu einer Vermischung ihrer Ausgaben kommen.
An diesem Punkt können Sie sehen, dass es nur 100.000 Datenzeilen gibt.
Zu diesem Zeitpunkt sollten 200.000 Datenzeilen vorhanden sein, da der Dateischreibvorgang nicht atomar ist. Wenn kein Synchronisierungsmechanismus vorhanden ist, können zwei Prozesse gleichzeitig versuchen, verschiedene Teile der Datei zu schreiben Die Daten stören sich gegenseitig.
vfork()
Funktionsaufrufe und Rückgabewerte sind die gleichen wiefork()
Gleich, aber ihre Semantik ist unterschiedlich.
vfork()
Die Funktion wird verwendet, um einen neuen Prozess zu erstellen, dessen Zweck es istexec
Ein neues Programm, das den Adressraum des übergeordneten Prozesses nicht kopiert, da der untergeordnete Prozess sofort aufgerufen wirdexec
, sodass der Adressraum des übergeordneten Prozesses nicht verwendet wird. Wenn der untergeordnete Prozess den Adressraum des übergeordneten Prozesses verwendet, können unbekannte Ergebnisse auftreten.
vfork()
Undfork()
Ein weiterer Unterschied ist:vfork()
Stellen Sie sicher, dass der untergeordnete Prozess zuerst ausgeführt wird, und rufen Sie ihn im untergeordneten Prozess aufexec
oderexit
Anschließend nimmt der übergeordnete Prozess den Betrieb wieder auf.
Im Betriebssystem bezieht sich ein Zombie-Prozess auf einen untergeordneten Prozess, der beendet wurde, dessen übergeordneter Prozess jedoch seinen Beendigungsstatus noch nicht gelesen hat. Obwohl der Zombie-Prozess nicht mehr ausgeführt wird, belegt er immer noch einen Eintrag in der Prozesstabelle, sodass der Kernel die Exit-Statusinformationen des Prozesses (z. B. Prozess-ID, Exit-Status usw.) speichern kann, bis der übergeordnete Prozess diese Informationen liest.
Wenn der übergeordnete Prozess vor dem untergeordneten Prozess beendet wird, wird der untergeordnete Prozess von Prozess 1 gehostet (dies ist auch eine Möglichkeit, den Prozess im Hintergrund laufen zu lassen).
Wenn der untergeordnete Prozess vor dem übergeordneten Prozess beendet wird und der übergeordnete Prozess die Exit-Informationen des untergeordneten Prozesses nicht verarbeitet, wird der untergeordnete Prozess zu einem Zombie-Prozess.
Der Kernel behält eine Datenstruktur für jeden untergeordneten Prozess bei, einschließlich Prozessnummer, Beendigungsstatus, verbrauchter CPU-Zeit usw. Wenn der übergeordnete Prozess die Exit-Informationen des untergeordneten Prozesses verarbeitet, gibt der Kernel diese Datenstruktur frei. Wenn der übergeordnete Prozess die Exit-Informationen des untergeordneten Prozesses nicht verarbeitet, gibt der Kernel diese Datenstruktur nicht frei und die Prozessnummer des untergeordneten Prozesses ist immer belegt. Die im System verfügbaren Prozessnummern sind begrenzt. Wenn eine große Anzahl von Zombie-Prozessen generiert wird, kann das System keine neuen Prozesse generieren, da keine Prozessnummern verfügbar sind.
signal(SIGCHLD, SIG_IGN)
Benachrichtigen Sie den Kernel darüber, dass er nicht am Beenden des untergeordneten Prozesses interessiert ist, und seine Datenstruktur wird sofort nach dem Beenden des untergeordneten Prozesses freigegeben.wait()
/waitpid()
Funktion: Der übergeordnete Prozess wartet auf das Ende des untergeordneten Prozesses, indem er diese Funktionen aufruft, und erhält seinen Exit-Status, wodurch die vom untergeordneten Prozess belegten Ressourcen freigegeben werden.pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
pid_t wait3(int *status, int options, struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
Der Rückgabewert ist die Nummer des Kindprozesses.
stat_loc
Ist die Information über die Beendigung des untergeordneten Prozesses:
a) Bei normaler Beendigung wird das Makro WIFEXITED(stat_loc)
Gibt true zurück, MakroWEXITSTATUS(stat_loc)
Der Kündigungsstatus kann abgerufen werden;
b) Wenn es abnormal beendet wird, wird das Makro WTERMSIG(stat_loc)
Ruft das Signal zum Beenden des Prozesses ab.
Wenn der übergeordnete Prozess ausgelastet ist, können Sie ihn erfassen SIGCHLD
Signal, wird in der Signalverarbeitungsfunktion aufgerufenwait()
/waitpid()
。
[Signale zwischen Prozessen senden](##1.5 Signale senden)
Wenn in einem Multiprozess-Dienstprogramm der Unterprozess ein Exit-Signal empfängt, wird der Unterprozess von selbst beendet.
Wenn der übergeordnete Prozess ein Exit-Signal empfängt, sollte er Exit-Signale an alle untergeordneten Prozesse senden und sich dann selbst beenden.
Multithreads teilen sich den Adressraum eines Prozesses,Wenn mehrere Threads auf denselben Speicher zugreifen müssen, verwenden Sie einfach globale Variablen.。
Bei mehreren Prozessen ist der Adressraum jedes Prozesses unabhängig und wird nicht gemeinsam genutzt.Wenn mehrere Prozesse auf denselben Speicher zugreifen müssen, können keine globalen Variablen verwendet werden, sondern nur gemeinsam genutzter Speicher.。
Shared Memory ermöglicht den Zugriff mehrerer Prozesse (keine Blutbeziehung zwischen Prozessen erforderlich) auf denselben Speicherplatz. Dies ist die effektivste Möglichkeit, Daten zwischen mehreren Prozessen zu teilen und zu übertragen. Prozesse können Shared Memory mit ihrem eigenen Adressraum verbinden. Wenn ein Prozess die Daten im Shared Memory ändert, ändern sich auch die von anderen Prozessen gelesenen Daten.
Der gemeinsam genutzte Speicher bietet keinen Sperrmechanismus. Das heißt, wenn ein Prozess den gemeinsam genutzten Speicher liest/schreibt, verhindert er nicht, dass andere Prozesse ihn lesen/schreiben.Wenn Sie das Lesen/Schreiben des gemeinsam genutzten Speichers sperren möchten, können Sie ein Semaphor verwenden . Linux bietet eine Reihe von Funktionen zum Betreiben von Shared Memory.
Mit dieser Funktion wird Shared Memory erstellt/erworben.
int shmget(key_t key, size_t size, int shmflg);
typedef unsigned int key_t
), im Allgemeinen beispielsweise hexadezimal 0x5005
, die Schlüssel verschiedener gemeinsamer Erinnerungen können nicht gleich sein.0666|IPC_CREAT
Gibt an, dass der gemeinsam genutzte Speicher erstellt werden soll, wenn er nicht vorhanden ist.verwenden ipcs -m
Sie können den gemeinsam genutzten Speicher des Systems anzeigen, einschließlich: Schlüssel (key), ID des gemeinsam genutzten Speichers (shmid), Besitzer (owner), Berechtigungen (perms) und Größe (bytes).
verwenden ipcrm -m 共享内存id
Shared Memory kann wie folgt manuell gelöscht werden:
Hinweis: Container können nicht für Datentypen im gemeinsam genutzten Speicher verwendet werden, es können nur Basisdatentypen verwendet werden.
Diese Funktion wird verwendet, um gemeinsam genutzten Speicher mit dem Adressraum des aktuellen Prozesses zu verbinden.
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmget()
Der von der Funktion zurückgegebene Shared-Memory-Bezeichner.Gibt die Startadresse des gemeinsam genutzten Speichers zurück, wenn der Aufruf erfolgreich ist, und kehrt zurück, wenn er fehlschlägt. (void *)-1
。
Diese Funktion wird verwendet, um gemeinsam genutzten Speicher vom aktuellen Prozess zu trennen, was äquivalent ist zu shmat()
Die Umkehroperation einer Funktion.
int shmdt(const void *shmaddr);
shmat()
Die von der Funktion zurückgegebene Adresse.Gibt 0 zurück, wenn der Aufruf erfolgreich ist, und -1, wenn er fehlschlägt.
Diese Funktion wird zum Betreiben des gemeinsam genutzten Speichers verwendet. Der am häufigsten verwendete Vorgang ist das Löschen des gemeinsam genutzten Speichers.
int shmctl(int shmid, int command, struct shmid_ds *buf);
shmget()
Die von der Funktion zurückgegebene Shared-Memory-ID.IPC_RMID
。Gibt 0 zurück, wenn der Aufruf erfolgreich ist, und -1, wenn er fehlschlägt.
Hinweis, Verwendung root
Der erstellte gemeinsame Speicher kann von normalen Benutzern unabhängig von den erstellten Berechtigungen nicht gelöscht werden.
Gibt 0 zurück, wenn der Aufruf erfolgreich ist, und -1, wenn er fehlschlägt.
Diese Funktion wird zum Betreiben des gemeinsam genutzten Speichers verwendet. Der am häufigsten verwendete Vorgang ist das Löschen des gemeinsam genutzten Speichers.
int shmctl(int shmid, int command, struct shmid_ds *buf);
shmget()
Die von der Funktion zurückgegebene Shared-Memory-ID.IPC_RMID
。Gibt 0 zurück, wenn der Aufruf erfolgreich ist, und -1, wenn er fehlschlägt.
Hinweis, Verwendung root
Der erstellte gemeinsame Speicher kann von normalen Benutzern unabhängig von den erstellten Berechtigungen nicht gelöscht werden.
[Externer Link Bilder werden übertragen...(img-v6qW3XRA-1720711279572)]
[Externer Link Bilder werden übertragen...(img-CG0tGAne-1720711279572)]