2010-12-12 2 views
2

Ich implementiere gerade ein Producer/Consumers Problemprogramm. Ich habe einen Elternteil und mehrere Kindprozesse. Alles funktioniert, aber jetzt muss ich mein Programm jeden k Millisekunden den Fortschritt der Aufgabe, die mein Programm macht, machen.Funktionieren signal() und alarm() auch, wenn der Prozess, in dem sie ausgeführt werden, ausgelastet ist? Oder sollte ich es in einem anderen dedizierten Prozess ausführen?

Zuerst dachte ich, dass es vielleicht nur um die signal() und alarm() Funktionen geht, aber von einigen vorbereitenden Tests habe ich es gemacht scheint nicht genug. Ich habe mehrere Protokolldateien überwacht und es scheint onAlarm() wird nicht aufgerufen. Ich nehme an, es hat damit zu tun, dass sowohl Eltern als auch Kinder "beschäftigt" sind, weil sie die Ereignisse nicht erhalten. Oder selbst wenn sie beschäftigt sind, sollten sie in der Lage sein, Anrufe bei onAlarm() zu erhalten? Die einzige Problemumgehung, die ich dafür sehe, ist, einen weiteren Prozess zu erstellen, der eine einzige Verantwortlichkeit hat. Dies ist

meine „events“ Code:

void onAlarm() { 
    signal(SIGALRM, onAlarm); 
    alarm(0.01); 

     fprintf(outputFile, "ALAAAAAAAAAAAAAARMMMMMMMMMMM: %d\n", numberOfBytesRead); 
} 

int main() { 
    signal(SIGALRM, onAlarm); 
    alarm(0.01); 
     ... 
} 

Antwort

4

Ihr Hauptproblem ist, dass alarm() eine ganze Anzahl von Sekunden in Anspruch nimmt - und alarm(0) verwendet wird, alle noch ausstehenden Alarme abzubrechen.

Die natürliche Folgefrage ist:

Wie kann ich warten Sub-Second tun?

Ich bin mir nicht sicher, was der genehmigte Weg ist. Auf einigen Systemen gibt es einen Micro-Sleep() Aufruf, aber das ist nicht Teil von POSIX 2008. Das direkte Analogon von in POSIX scheint nanosleep() zu sein.

Es gibt eine sigtimewait(), die wahrscheinlich verwendet werden könnte, um den Effekt zu erzielen. (Sie könnten in der Lage sein setitimer() und getitimer() anstelle von usleep() zu verwenden.)

Die Schwierigkeit bei all diesen ist, dass Sie entweder synchron sind (Sie nicht mit der Arbeit vorankommen kann während des Wartens auf ein Signal ankommen) oder kein Signal gesendet. Ich sehe nicht sofort einen POSIX-Subsekunden-Alarmmechanismus, mit dem Sie weiterarbeiten können, während Sie auf das Timeout warten.

+0

in diesem Projekt in einem übersetzbar Zustand sehen Wie kann ich es 500ms machen akzeptieren, zum Beispiel? –

+0

Verwenden Sie 'setitimer' mit dem' ITIMER_REAL'. Suchen Sie anhand dieser Funktion nach Beispielen. – nategoose

1

Wenn Sie unter einer Sekunde Auflösung auf einem Timer benötigen, können Sie einen zweiten Posix Thread verwenden und ein usleep

+0

Früher gab es einen "Alarm", der das hätte tun können, aber POSIX mag es nicht mehr. – nategoose

2

Ich bin nicht sicher, ob dies Ihr Problem ist, aber es ist nicht sicher fprintf innen ein Signalhandler. Zum Beispiel könnte das Signal innerhalb von fprintf selbst ausgelöst werden, was zu unerwartetem Verhalten führen könnte. Oder vielleicht fprintf reserviert etwas Speicher, und das Signal wurde abgefangen, während malloc lief. Diese Art von Dingen kann scheinbar zufällige Deadlocks und heftige Abstürze verursachen.

Der sichere Weg, komplexe Berechnungen in einem Signal-Handler durchzuführen, besteht darin, dass Ihr Signal-Handler den Zustand einer bereits laufenden Ereignisschleife oder etwas Ähnliches ändert. Oder, für Ihr spezifisches Problem, wie andere vorschlagen, vermeiden Sie die Verwendung von Signalen dafür und verwenden Sie einen einfacheren Schlafanruf.

+0

Die 'man'-Seiten sagen oft, ob eine Funktion in einem Signal-Handler sicher ist und manchmal, wenn es nicht ist. In der Regel sind die extrem einfachen Systemaufrufe (kein IO) und Rechenfunktionen sicher. Wenn Sie wissen, was Sie tun, können Sie manchmal mit IO in Signalhandlern durchkommen, aber es ist kompliziert. – nategoose

+0

@nategoose - Sie können definitiv mit I/O durchkommen, wenn nur durch syscalls, aber das Aufrufen von 'stdio' ist eine andere Geschichte. Es verwaltet Lese-/Schreibpuffer im Benutzermodus und wie ich vor ein paar Jahren in diesem Beitrag gesagt habe, könnte das ein inkonsistenter Zustand sein, wenn Sie ein Signal aus einer 'stdio'-Funktion erhalten. Und es könnte Speicher reservieren, der aus ähnlichen Gründen nicht sicher ist. – asveikau

+0

@asveikay: "Wenn Sie wissen, was Sie tun" umfasst viel. 'fprintf (stderr,' funktioniert möglicherweise, je nachdem, wie es implementiert ist, außer dass der Signalhandler es aufruft, kann seine Ausgabe in die Mitte der anderen Ausgabe plopsen. Dies ist normalerweise tolerierbar für die Debugausgabe. Zusätzlich, wenn Sie die Verwendung von Heap vermeiden können und komplexe IO-Funktionen aus dem normalen Code, können Sie sie normalerweise frei innerhalb der Signalhandler verwenden. Sie können auch tun, wenn Sie Signale um diese Funktionen innerhalb des normalen Codes blockieren/entsperren. Für kleine Spielzeug-/Testprogramme ist es machbar, aber für Bei großen Programmen wird es hart. – nategoose

0

Für einen Subsekunden-Timer, der ein Signal sendet, müssen Sie die POSIX-Setitimer (2) -Funktion verwenden.

1

Die kurze Antwort auf Ihre Frage ist ja.Sie arbeiten, obwohl der Prozess, an den sie sich anschliessen, mit Berechnungen oder Wartezeiten beschäftigt ist. Die einzige Einschränkung ist, dass der Prozess sich nicht ändert und mit SIGALRM selbst spielt, was Probleme verursachen würde. Wenn ein Signal ankommt, wird die normale Ausführung eines Prozesses/Threads unterbrochen und der Signal-Handler aufgerufen. Das ist die ganze Idee der Signalverarbeitung und warum sie asynchronous heißt.

Die längere Antwort auf Ihre Frage ist nein. Sie möchten den Fortschrittsbericht nicht über den Signal-Handler-Mechanismus implementieren, da die API, die Sie in einem Signal-Handler verwenden können, sehr begrenzt ist. Um genau zu sein das Beispiel, das Sie falsch bezeichnet haben, da es fprintf(3) verwendet. Wie in einer der Antworten angegeben, sind die Signale asynchron. Dies bedeutet, dass, wenn das Signal in der Mitte des Hauptcodes anruft, beispielsweise malloc(3), und Ihr Code malloc(3) ebenfalls anruft (Sie können nie wissen, printf(3) kann malloc(3) für Pufferung und andere Bedürfnisse anrufen), dann werden Sie mallocs eigene interne Daten korrumpieren Strukturen und verursachen das Programm zu Fehler. Möglicherweise haben Sie sogar Probleme, Ihre eigenen Funktionen aufzurufen, die nicht asynchron sicher sind. Sie haben eine Liste sicherer Funktionen, die Sie in einem Signal-Handler aufrufen können. Sie finden diese unter man 7 signal unter Async-signal-safe functions. Also ja, technisch können Sie Fortschrittsberichterstattung über alarm(3) implementieren, solange Sie bereit sind, mit dieser reduzierten API zu leben, und das ist der Grund, warum ich es nicht tun würde, wenn das Programm single threaded by design ist und es keine Möglichkeit gibt Ich sehe den Fortschrittsberichtscode als zukünftigen Erweiterungen unterworfen, die es schwer machen, innerhalb eines Signalhandlers zu schreiben.

Ein weiteres Problem, das zu Ihrem Beispiel angegeben wurde, ist, dass alarm(2) keine Subsekunden-Argumente akzeptiert und das obige Beispiel sollte die Kompilierung nicht vollständig bestanden haben oder zumindest einige Warnungen über diese Tatsache angezeigt haben.

Für die Mikrosekundenauflösung können Sie setitimer(2) mit ITIMER_REAL wie angegeben verwenden.

Für Nanosekunde Auflösung auf Linux können Sie timer_create(2) verwenden, CLOCK_REALTIME, SIGEV_SIGNAL und timer_settime(2) dass viele weitere Funktionen.

Hier ist ein Beispielcode. Beachten Sie, dass dies eigene Fehlerbehandlungsmakros verwendet. Sie können demos-linux

#include <signal.h> // for signal(2), SIG_ERR 
#include <unistd.h> // for alarm(2), write(2) 
#include <stdlib.h> // for EXIT_SUCCESS 
#include <err_utils.h> // for CHECK_NOT_M1(), CHECK_NOT_SIGT() 
#include <stdio.h> // for snprintf(3), STDERR_FILENO 
/* 
* This is an example of doing progress reports via the SIGALRM signal every second. 
* The main code does a tight calculation loop and the progress reporting to stderr 
* (file descriptor 2) is done via the alarm signal handler. 
*/ 

/* 
* The variables are global to allow the signal handler to access them easily 
* You DONT need to initialize them to 0 since that is the default. 
* The 'volatile' on i is *critical* since it will be accessed asynchronously 
* and the compiler needs to know not to put it in a register since that 
* will mean that we cannot report it's value correctly from the signal 
* handler. 
*/ 
volatile unsigned long i; 
/* 
* Remember that this is a signal handler and calls to fprintf(3) or the like 
* are forbidden so we are forced to use async-safe function (see man 7 signal). 
* That is the reason for the cumbersome code. Hopefully snprintf(3) is safe enough 
* to use. 
*/ 
static void handler(int sig) { 
    // we have to reschedule the SIGALRM every time since the alarm(2) 
    // is a one time deal. 
    CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR); 
    // no error code from alarm(2) 
    alarm(1); 
    char buf[100]; 
    int len=snprintf(buf, sizeof(buf), "did [%ld] units of work...\n", i); 
    CHECK_NOT_M1(write(STDERR_FILENO, buf, len)); 
} 

int main(int argc, char** argv, char** envp) { 
    CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR); 
    // no error code from alarm(2) 
    alarm(1); 
    // a very long calculation 
    while(true) { 
     /* Do some real work here */ 
     i++; 
    } 
    return EXIT_SUCCESS; 
} 
Verwandte Themen