Keine der bisherigen Lösungen bietet einen Ansatz ohne die Verwendung von SIGCHLD, wie es die Frage verlangt. Hier ist eine Implementierung eines alternativen Ansatzes unter Verwendung von poll, wie in dieser Antwort dargelegt (die auch erklärt, warum Sie in Situationen wie dieser die Verwendung von SIGCHLD vermeiden sollten):
Stellen Sie sicher, dass Sie für jeden von Ihnen erstellten Kindprozess eine Pipe haben. Es kann entweder deren stdin/stdout/stderr oder einfach eine zusätzliche Dummy-Dateideskriptor sein. Wenn der Kindprozess terminiert, wird sein Ende der Pipe geschlossen, und Ihre Hauptereignisschleife erkennt die Aktivität auf diesem Dateideskriptor. Aufgrund der Tatsache, dass es geschlossen wurde, erkennen Sie, dass der Kindprozess gestorben ist, und rufen waitpid auf, um den Zombie zu bereinigen.
(Hinweis: Ich habe einige bewährte Verfahren wie Fehlerüberprüfung und Bereinigung von Dateideskriptoren der Kürze halber weggelassen)
/**
* Legt die maximale Anzahl von Clients fest, die in der Tabelle verfolgt werden sollen.
*/
#define MAX_CLIENT_COUNT 1000
/**
* Verfolgt Clients, indem ihre Prozess-IDs und Pipe-Dateideskriptoren gespeichert werden.
*/
struct process_table {
pid_t clientpids[MAX_CLIENT_COUNT];
struct pollfd clientfds[MAX_CLIENT_COUNT];
} PT;
/**
* Initialisiert die Prozesstabelle. -1 bedeutet, dass der Eintrag in der Tabelle verfügbar ist.
*/
void initialize_table() {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
PT.clientfds[i].fd = -1;
}
}
/**
* Gibt den Index des nächsten verfügbaren Eintrags in der Prozesstabelle zurück.
*/
int get_next_available_entry() {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
if (PT.clientfds[i].fd == -1) {
return i;
}
}
return -1;
}
/**
* Fügt Informationen über einen neuen Client zur Prozesstabelle hinzu.
*/
void add_process_to_table(int i, pid_t pid, int fd) {
PT.clientpids[i] = pid;
PT.clientfds[i].fd = fd;
}
/**
* Entfernt Informationen über einen Client aus der Prozesstabelle.
*/
void remove_process_from_table(int i) {
PT.clientfds[i].fd = -1;
}
/**
* Bereinigt alle toten Kindprozesse aus der Prozesstabelle.
*/
void reap_zombie_processes() {
int p = poll(PT.clientfds, MAX_CLIENT_COUNT, 0);
if (p > 0) {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
/* Hat sich die Pipe geschlossen? */
if ((PT.clientfds[i].revents & POLLHUP) != 0) {
// printf("[%d] erledigt\n", PT.clientpids[i]);
waitpid(PT.clientpids[i], NULL, 0);
remove_process_from_table(i);
}
}
}
}
/**
* Simuliert das Warten auf eine Verbindung eines neuen Clients.
*/
void accept() {
sleep((rand() % 4) + 1);
}
/**
* Simuliert die Ausführung nützlicher Arbeit durch den Kindprozess und das Beenden.
*/
void childfunction() {
sleep((rand() % 10) + 1);
exit(0);
}
/**
* Hauptprogramm
*/
int main() {
/* Initialisiere die Prozesstabelle */
initialize_table();
while (1) {
accept();
/* Erstelle die Pipe */
int p[2];
pipe(p);
/* Forken eines Kindprozesses. */
pid_t cpid = fork();
if (cpid == 0) {
/* Kindprozess */
close(p[0]);
childfunction();
}
else {
/* Elternprozess */
close(p[1]);
int i = get_next_available_entry();
add_process_to_table(i, cpid, p[0]);
// printf("[%d] gestartet\n", cpid);
reap_zombie_processes();
}
}
return 0;
}
Und hier ist eine Beispielausgabe beim Ausführen des Programms mit den printf
-Anweisungen auskommentiert:
[31066] gestartet
[31067] gestartet
[31068] gestartet
[31069] gestartet
[31066] erledigt
[31070] gestartet
[31067] erledigt
[31068] erledigt
[31071] gestartet
[31069] erledigt
[31072] gestartet
[31070] erledigt
[31073] gestartet
[31074] gestartet
[31072] erledigt
[31075] gestartet
[31071] erledigt
[31074] erledigt
[31081] gestartet
[31075] erledigt
0 Stimmen
Du kannst etwas im Signalhandler von SIGCHLD tun.
0 Stimmen
Ich habe bereits erwähnt... Wird das SIGCHLD-Signal ignoriert...?
14 Stimmen
+1 für einen wahnsinnig dramatischen Titel und den ersten Satz. D: