Ich arbeite an einer Serveranwendung, die unter Linux und Mac OS X funktionieren soll. Das geht so:
- Hauptanwendung starten
- Verzweigung des Controller-Prozesses
- Rufen Sie lock_down() im Controller-Prozess auf
- Hauptanwendung beenden
- Der Controller-Prozess verzweigt sich dann erneut und erstellt einen Arbeitsprozess
- Schließlich verzweigt der Controller immer mehr Worker-Prozesse
Ich kann mich mit mehreren Methoden anmelden (z. B. Syslog oder eine Datei), aber im Moment denke ich über Syslog nach. Das “Lustige” ist, dass im Controller-Prozess niemals eine Syslog-Ausgabe zu sehen ist, es sei denn, ich füge den Abschnitt #ifdef unten ein.
Der Worker verarbeitet Protokolle fehlerfrei in Mac OS X und Linux mit oder ohne den Abschnitt ifdef’ed unten. Der Controller protokolliert auch fehlerfrei in Mac OS X ohne den Abschnitt #ifdef, aber unter Linux wird die ifdef benötigt, wenn ich eine Ausgabe in Syslog (oder die Protokolldatei für diese Angelegenheit) vom Controller-Prozess sehen möchte.
Also, warum ist das so?
static int
lock_down(void)
{
struct rlimit rl;
unsigned int n;
int fd0;
int fd1;
int fd2;
// Reset file mode mask
umask(0);
// change the working directory
if ((chdir("https://stackoverflow.com/")) < 0)
return EXIT_FAILURE;
// close any and all open file descriptors
if (getrlimit(RLIMIT_NOFILE, &rl))
return EXIT_FAILURE;
if (RLIM_INFINITY == rl.rlim_max)
rl.rlim_max = 1024;
for (n = 0; n < rl.rlim_max; n++) {
#ifdef __linux__
if (3 == n) // deep magic...
continue;
#endif
if (close(n) && (EBADF != errno))
return EXIT_FAILURE;
}
// attach file descriptors 0, 1 and 2 to /dev/null
fd0 = open("/dev/null", O_RDWR);
fd1 = dup2(fd0, 1);
fd2 = dup2(fd0, 2);
if (0 != fd0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
camh war nah dran, aber die Verwendung von closelog() war die Idee, die den Trick gemacht hat, also geht die Ehre an Jilles. Abgesehen vom Schließen eines Dateideskriptors unter Syslogs-Füßen muss jedoch noch etwas anderes passieren. Damit der Code funktioniert, habe ich kurz vor der Schleife einen Aufruf von closelog() hinzugefügt:
closelog();
for (n = 0; n < rl.rlim_max; n++) {
if (close(n) && (EBADF != errno))
return EXIT_FAILURE;
}
Ich habe mich auf ein wörtliches Verständnis der Handbuchseite verlassen und gesagt:
Die Verwendung von openlog() ist optional; es wird bei Bedarf automatisch von syslog() aufgerufen …
Ich habe dies so interpretiert, dass Syslog erkennen würde, ob der Dateideskriptor darunter geschlossen wurde. Anscheinend nicht. Ein explizites closelog() unter Linux war erforderlich, um Syslog mitzuteilen, dass der Deskriptor geschlossen wurde.
Eine weitere Sache, die mich immer noch verwirrt, ist, dass die Nichtverwendung von closelog() den ersten Fork-Prozess (den Controller) sogar daran hinderte, eine Protokolldatei zu öffnen und zu verwenden. Die folgenden gegabelten Prozesse könnten Syslog oder eine Protokolldatei ohne Probleme verwenden. Vielleicht gibt es im Dateisystem einen Caching-Effekt, der dazu führt, dass der erste gegabelte Prozess eine unzuverlässige “Idee” davon hat, welche Dateideskriptoren verfügbar sind, während der nächste Satz von gegabelten Prozessen ausreichend verzögert wird, um davon nicht betroffen zu sein?
+1 für
// deep magic...
🙂 Wer braucht Harry Potter, wenn man den Linux-Kernel hat?– David Z
20. August 2010 um 22:33 Uhr
Also, was ist zu diesem Zeitpunkt auf fd 3?
– Gilles ‘SO- hör auf, böse zu sein’
20. August 2010 um 22:42 Uhr
@David. Nichts mit dem Kernel zu tun. Nicht einmal fd 0, 1 und 2 sind speziell für den Kernel.
– camh
20. August 2010 um 23:59 Uhr
@camh: Das weiß ich. Es ist nur ein Witz. Fühlen Sie sich frei, “Standard-Dateideskriptorzuweisungen” zu ersetzen, wenn Sie sich dadurch besser fühlen.
– David Z
21. August 2010 um 0:02 Uhr