Funktioniert sigprocmask() nicht richtig?

Funktioniert sigprocmask() nicht richtig?

(Entschuldigen Sie den langen Beitrag, aber ich wollte so genau wie möglich sein.)

Ich habe beim Schreiben eines C-Programms versucht, die Signalmaske des Hauptthreads auszudrucken, als mir hinsichtlich der Funktionsweise der sigprocmaskFunktion etwas Merkwürdiges auffiel.

Hintergrund[Quelle: Manualpage sigprocmask(2)]
Die sigprocmaskFunktion wird verwendet, um die Signalmaske des aufrufenden Threads abzurufen und/oder zu ändern.

/* Prototype for the glibc wrapper function */
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • Wenn oldsetnicht - ist NULL, wird der vorherige Wert der Signalmaske in gespeichert oldset.
  • Wenn setist NULL, dann bleibt die Signalmaske unverändert (d. h. howwird ignoriert), aber der aktuelle Wert der Signalmaske wird trotzdem zurückgegeben oldset(wenn dies nicht der Fall ist NULL).
  • Eine Reihe von Funktionen zum Ändern und Überprüfen von Variablen des Typs sigset_t(„Signalsätze“) wird in beschrieben sigsetops(3). Im Beispiel:
    • int sigemptyset(sigset_t *set);: initialisiert das von set gegebene Signalset aufleer, wobei alle Signale aus dem Satz ausgeschlossen sind.
    • int sigfillset(sigset_t *set);: initialisiert gesetzt aufvoll, einschließlich aller Signale.
    • int sigismember(const sigset_t *set, int signum);: testet, ob Signum ein Mitglied des Sets ist.

Notiz: Beim Erstellen eines gefüllten Signalsatzes sigfillsetberücksichtigt die glibc-Funktion nicht diezweiEchtzeitsignale, die intern von der NPTL-Threading-Implementierung verwendet werden.

Systemspezifikationen
Linux-Distribution: Linux Mint 19.3Cinnamon
Glibc-Version: 2.27(Standard)
Auch überprüft für Glibc-Version:2.31.9

Ausgabe von uname -a:
Linux 5.0.0-32-generic #34~18.04.2-Ubuntu SMP Thu Oct 10 10:36:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Problemreplikation

Das folgende Programm hat mich alarmiert, dass möglicherweise etwas schief läuft:

#define _GNU_SOURCE

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

#define BUFFER_SIZE 32

#define OUTOFBOUNDS
#undef  OUTOFBOUNDS

void print_set_bin(sigset_t *setp);

int main(void)
{
    sigset_t set;

    printf("NSIG = %d\n\n", NSIG);

    printf("Empty set:\n");
    if (sigemptyset(&set))  
    {
        perror("sigemptyset");
        return -1;
    }
    print_set_bin(&set);

    printf("Filled set:\n");
    if (sigfillset(&set))   
    {
        perror("sigfillset");
        return -1;
    }
    print_set_bin(&set);

    printf("After sigprocmask():\n");
    if (sigprocmask(SIG_BLOCK, NULL, &set))
    {
        perror("sigprocmask");
        return -1;
    }
    print_set_bin(&set); // Why non-empty?

    return 0;
}


void print_set_bin(sigset_t *setp)
{
    int sig, res;
    char buff[BUFFER_SIZE];

    if (!setp)
    {
        fprintf(stderr, "print_set_bin(): NULL parameter\n");
        return;
    }

#ifdef OUTOFBOUNDS
    for (sig = 0; sig <= NSIG; sig++)
#else
    for (sig = 1; sig < NSIG; sig++)
#endif
    {
        res = sigismember(setp, sig);
        if (res == -1)
        {
            snprintf(buff, BUFFER_SIZE, "sigisimember [%d]", sig);
            perror(buff);
        }
        else
            printf("%d", res);
    }
    printf(" [%s]\n\n", sigisemptyset(setp) ? "Empty" : "Non-empty");
}

Die Funktion print_set_bingibt die Ausgabe von sigismember( 0für kein Mitglied, 1für ein Mitglied) für alle Signale aus. Die Makrodefinition NSIG(= 65) in signal.hist die größte Signalnummer plus eins (1), wie in erwähnt /usr/include/x86_64-linux-gnu/bits/signum-generic.h. In derselben Datei wird auch erwähnt, dass diese größte Signalnummer Echtzeitsignale (Nummernbereich [32, 64]) umfasst und dass die Signalnummer Null (0) für Testzwecke reserviert ist.

Als Ergebnis testet mein oben veröffentlichter Code nach Signalnummern, die in den Bereich [1, 64] gehören.

Nachfolgend finden Sie dieAusgabedes Programms:

NSIG = 65

Empty set:
0000000000000000000000000000000000000000000000000000000000000000 [Empty]

Filled set:
1111111111111111111111111111111001111111111111111111111111111111 [Non-empty]

After sigprocmask():
0000000000000000000000000000000000000000000000000000000000000000 [Non-empty]

Ausgabeerklärung
In diesem Programm wird die Variable setvom Typ sigset_tmanipuliert. Zuerst sigemptysetwerden mit der Funktion alle Bits auf Null gesetzt, dann sigfillsetwerden mit der Funktion alle Bits auf Eins gesetzt (außer zwei; sieheNotizInHintergrundAbschnitt) und sigprocmaskwird schließlich verwendet, um die aktuelle Signalmaske in derselben Variablen zu speichern. Nachdem alle Operationen am Signalsatz ausgeführt wurden, print_set_binwird die Funktion verwendet, um auszudrucken, welche Signale zum Satz gehören und ob der Satz leer ist (mit sigisemptyset()).

Das Problem scheint der letzte Aufruf von zu sein print_set_bin, bei dem kein Signal gefunden wird, das zum Set gehört, die sigisemptysetFunktion das Set aber als nicht leer charakterisiert. Das brachte mich zum Nachdenken, ob sigset_tmehr als 64 Bits enthält und mindestens eines davon ungleich Null ist.

Forschung
Beim Verfolgen der in enthaltenen Headerdateien signal.hhabe ich festgestellt, dass es sigset_tsich um eine Struktur handelt, die in /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.hals definiert __sigset_t.hund typedefin bearbeitet wird /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h:

#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
  unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;

Zuerst dachte ich, dass 1024 Bits zu viel seien, aber dann stieß ich aufdiese Antwortzu einer anderen Frage auf unix.stackexchange.com. Ich habe mich dann entschieden, die Implementierungsdetails der sigset_tStruktur zu verwenden, um alle 1024 Bits auszudrucken. Ich habe dies im folgenden Code getan, indem ich die Funktion print_set_bindurch eine Funktion ersetzt habe, die alle dem Array zugewiesenen (= 16) s print_set_wordausgibt ._SIGSET_NWORDSunsigned long int__val

void print_set_word(sigset_t *setp)
{
    int i;

    if (!setp)
    {
        fprintf(stderr, "print_set_word(): NULL parameter\n");
        return;
    }

    for (i = 0; i < 16; i++)
    {
            printf("%lu\n", setp->__val[i]);
    }
    printf("[%s]\n\n", sigisemptyset(setp) ? "Empty" : "Non-empty");
}

ProgrammAusgabe:

Empty set:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
[Empty]

Filled set:
18446744067267100671
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
[Non-empty]

After sigprocmask():
0
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
18446744073709551615
[Non-empty]

Anmerkungen:

  • 18446744067267100671== 0b1111111111111111111111111111111001111111111111111111111111111111(64 Bits auf 1 gesetzt, außer zwei; sieheNotizInHintergrundAbschnitt)
  • 18446744073709551615== 0b1111111111111111111111111111111111111111111111111111111111111111(64 Bit auf 1 gesetzt)

Ausgabeerklärung und Frage
Wie Sie sehen, sigemptyset()werden sigfillset()alle 1024 Bits des Satzes manipuliert. Der Aufruf sigemptyset()statt sigfillset()vor dem Aufruf sigprocmask()zeigt, dass sigprocmask()nur die ersten 64 Bits (eins unsigned long int) manipuliert werden und die restlichen 1024-64=960 Bits unberührt bleiben! Und hier kommt die lang erwartete Frage: Ist das nicht ein Fehler? Sollte nicht sigprocmask()in die gesamten Strukturdaten geschrieben werden?

Antwort1

Ja, sigprocmask()hat nicht richtig funktioniert!

Am 11. März 2020 habe ich einen neuen Fehlerbericht im glibc-Bugtracker eingereicht. Vor ein paar Minuten wurde der Fehlerstatus ab der glibc-Version auf „Gelöst/Behoben“ geändert 2.32.

Vielen Dank an die glibc-Entwickler, die an der Behebung dieses Fehlers beteiligt waren!

verwandte Informationen