bifurcaciones - punto de bifurcación definicion
tenedor() con fugas? Tomando más y más tiempo para bifurcar un proceso simple (3)
¿Tal vez podrías intentar usar la llamada genérica wait () en lugar de waitpid ()? Es solo una suposición, pero escuché que era mejor de un profesor de licenciatura. Además, ¿has intentado usar desinfectante de direcciones
Además, también puede usar GDB para depurar un proceso secundario (si aún no lo ha intentado). Puede utilizar el modo de seguimiento de horquilla:
set follow-fork-mode child
pero eso solo es capaz de depurar al padre. Puede depurar ambos obteniendo el pid del proceso hijo, llamando a sleep () después de bifurcar y luego:
attach <child process pid>
luego llame:
detach
Esto es útil porque puede volcar las pérdidas de memoria en valgrind. Solo llama a valgrind con
valgrind --vgdb-error=0...<executable>
luego establezca algunos puntos de interrupción relevantes y continúe a través de su programa hasta que los alcance y luego busque fugas:
monitor leak_check full reachable any
entonces:
monitor block_list <loss_record_nr>
Tengo un sistema en el que se ejecutan dos procesos idénticos (llamémoslos réplicas). Cuando se señala, una réplica se duplicará utilizando la llamada de fork()
. Un tercer proceso selecciona uno de los procesos para matar al azar, y luego señala al otro para crear un reemplazo. Funcionalmente, el sistema funciona bien; puede matar / reaparecer réplicas todo el día, excepto por el problema de rendimiento.
La llamada de fork()
está tomando más y más tiempo. La siguiente es la configuración más sencilla que aún muestra el problema. El tiempo se muestra en el gráfico siguiente:
El código de la réplica es el siguiente:
void restartHandler(int signo) {
// fork
timestamp_t last = generate_timestamp();
pid_t currentPID = fork();
if (currentPID >= 0) { // Successful fork
if (currentPID == 0) { // Child process
timestamp_t current = generate_timestamp();
printf("%lld/n", current - last);
// unblock the signal
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGUSR1);
sigprocmask(SIG_UNBLOCK, &signal_set, NULL);
return;
} else { // Parent just returns
waitpid(-1, NULL, WNOHANG);
return;
}
} else {
printf("Fork error!/n");
return;
}
}
int main(int argc, const char **argv) {
if (signal(SIGUSR1, restartHandler) == SIG_ERR) {
perror("Failed to register the restart handler");
return -1;
}
while(1) {
sleep(1);
}
return 0;
}
Cuanto más tiempo se ejecuta el sistema, peor se pone.
Lamento no tener una pregunta específica, pero ¿alguien tiene alguna idea / pista sobre qué está pasando? Me parece que hay una fuga de recursos en el kernel (por lo tanto, la etiqueta linux-kernel), pero no sé por dónde empezar a buscar.
Lo que he intentado:
- kmemleak , que no atrapó nada. Esto implica que si hay alguna "pérdida" de memoria, todavía es posible acceder a él.
-
/proc/<pid>/maps
no está creciendo. - Actualmente ejecutando el kernel 3.14 con parche RT (tenga en cuenta que esto ocurre con procesos no rt y rt), y también probé 3.2.
- Los procesos zombie no son un problema. He probado una versión en la que configuro otro proceso como subreaper que usa prctl
- Noté por primera vez esta desaceleración en un sistema en el que las mediciones de tiempo se están reduciendo fuera del proceso reiniciado; mismo comportamiento
¿Alguna pista? ¿Algo que pueda proporcionar para ayudar? ¡Gracias!
La ralentización se debe a una acumulación de vmas anónimas y es un problema conocido. El problema es evidente cuando hay un gran número de llamadas a fork()
y el padre sale antes que el hijo. El siguiente código recrea el problema ( fuente Daniel Forrest ):
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
while (1) {
pid = fork();
if (pid == -1) {
/* error */
return 1;
}
if (pid) {
/* parent */
sleep(2);
break;
}
else {
/* child */
sleep(1);
}
}
return 0;
}
El comportamiento se puede confirmar marcando anon_vma
en /proc/slabinfo
.
Hay un parche ( source ) que limita la longitud de anon_vma_chain
copiado a cinco. Puedo confirmar que el parche soluciona el problema.
En cuanto a cómo finalmente encontré el problema, finalmente comencé a poner llamadas printk
a lo largo del código de la fork
, verificando los tiempos mostrados en dmesg
. Finalmente, vi que era la llamada a anon_vma_fork
que llevaba más y más tiempo. Entonces fue una cuestión rápida de búsqueda de google.
Tomó un tiempo bastante largo, por lo que todavía agradecería cualquier sugerencia para una mejor manera de solucionar el problema. Y a todos aquellos que ya pasaron tiempo tratando de ayudarme, Gracias.
Solo una idea: ¿tal vez está relacionado con MMU o caché? Por lo que entiendo, en fork (), el kernel llena las entradas apropiadas de la tabla con referencias a las mismas páginas RAM físicas. Usted escribió: está haciendo escrituras ficticias, pero ¿las está haciendo con segmens ejecutables (en caso afirmativo, cómo, debido a que estas deberían estar protegidas contra escritura)? Del gráfico parece que el rendimiento aumenta en algunos puntos (512? 512 * 3? 512 * 4?). Me hace sospechar que el sistema (¿kernel ?, ¿hardware?) Está al tanto del problema y usa alguna solución (¿duplicados en la MMU para la misma página física? ¿Hay alguna estructura de datos dividida?).