2009-05-19 26 views
43

Estoy trabajando en un proyecto escolar donde tuve que escribir un servidor multiproceso, y ahora lo estoy comparando con Apache ejecutando algunas pruebas en su contra. Estoy usando autobench para ayudar con eso, pero después de ejecutar algunas pruebas, o si le doy una tasa demasiado alta (alrededor de 600+) para hacer las conexiones, aparece un error de "Demasiados archivos abiertos".Socket accept - "Demasiados archivos abiertos"

Después de terminar con la solicitud, siempre hago un close() en el zócalo. También intenté usar la función shutdown(), pero nada parece ayudar. De cualquier forma alrededor de esto?

Respuesta

47

Hay varios lugares donde Linux puede tener límites en la cantidad de descriptores de archivo que puede abrir.

Usted puede comprobar lo siguiente:

cat /proc/sys/fs/file-max 

qué le dará el sistema de amplios límites de descriptores de archivo.

En el nivel de la cáscara, esto le dirá a su límite personal:

ulimit -n 

Esto se puede cambiar en /etc/security/limits.conf - es el parámetro nofile.

Sin embargo, si cierra correctamente los enchufes, no debería recibir esto a menos que esté abriendo muchas conexiones simulaneas. Parece que algo impide que sus enchufes se cierren adecuadamente. Verificaría que estén siendo manejados correctamente.

+0

Edito /etc/security/limits.conf con: – linjunhalida

+0

nombre de usuario hard nofile 20000 – linjunhalida

4

que puede tomar un poco de tiempo antes de una toma cerrada es realmente liberado

lsof para listar los archivos abiertos

cat /proc/sys/fs/file-max para ver si hay un límite del sistema

1

Cuando su programa tiene más descriptores abiertos que los archivos abiertos ulimit (ulimit -a hará una lista de esto), el núcleo se negará a abrir más descriptores de archivos. Asegúrese de no tener ninguna fuga en el descriptor de archivo, por ejemplo, ejecutando por un tiempo, luego deténgase y vea si todavía hay otros fds abiertos cuando está inactivo, y si todavía hay un problema, cambie el ulimit nofile para su usuario en /etc/security/limits.conf

12

TCP tiene una función llamada "TIME_WAIT" que asegura que las conexiones se cierren limpiamente. Se requiere un extremo de la conexión para seguir escuchando durante un tiempo después de que se cerró la toma.

En un servidor de alto rendimiento, es importante que sean los clientes los que entren en TIME_WAIT, no en el servidor. Los clientes pueden permitirse tener un puerto abierto, mientras que un servidor ocupado puede quedarse sin puertos rápidamente o tener demasiados FD abiertos.

Para lograr esto, el servidor nunca debe cerrar la conexión primero, siempre debe esperar a que el cliente la cierre.

+2

No. TCP TIME_WAIT mantendrá los sockets abiertos en el nivel del sistema operativo y eventualmente hará que el servidor rechace las conexiones entrantes. Cuando cierra el identificador de archivo, se cierra. http://stackoverflow.com/questions/1803566/what-is-the-cost-of-many-time-wait-on-the-server-side –

+0

Es cierto que el identificador del archivo se cierra de inmediato y me equivoqué. Pero mi punto principal sigue en pie, porque a pesar de que el FD se libera, el puerto TCP permanece asignado durante TIME_WAIT, y un servidor ocupado puede quedarse sin puertos TCP o gastar demasiada memoria del kernel siguiéndolos. – Ed4

5

Uso lsof -u `whoami` | wc -l para encontrar el número de archivos abiertos el usuario tiene

15

tuve un problema similar. solución rápida es:

ulimit -n 4096 

explicación es la siguiente: - cada conexión de servidor es un descriptor de archivo.En CentOS, Redhat y Fedora, probablemente otros, el límite de usuarios de archivos es 1024, no se sabe por qué. Se puede ver fácilmente cuando escribe: ulimit -n

Tenga en cuenta que esto no tiene mucha relación con los archivos máx. Del sistema (/ proc/sys/fs/file-max).

En mi caso se trataba de un problema en esta Redis, así que lo hice:

ulimit -n 4096 
redis-server -c xxxx 

en su caso, en lugar de Redis, es necesario iniciar el servidor.

+2

Y la respuesta a una pérdida de memoria es ... ¿comprar más memoria? No se corrigió la fuga de archivos. –

+2

Parece que no entiende el problema (o coloca el comentario en la respuesta incorrecta? Tiene que ver con el límite del descriptor de archivo, y nada que ver con la memoria o la pérdida de memoria – Nick

+1

El límite del archivo es 1024 porque de lo contrario se ejecuta en un [problema fundamental con 'select()'] (http://beesbuzz.biz/blog/e/2013/10/10-the_problem_with_select_vs_poll.php). – fluffy

1

Tuve el mismo problema y no me molestaba comprobar los valores devueltos de las llamadas close(). Cuando comencé a verificar el valor de retorno, el problema desapareció misteriosamente.

Solo puedo suponer que un error de optimización del compilador (gcc en mi caso), supone que las llamadas close() no tienen efectos secundarios y pueden omitirse si no se utilizan sus valores de retorno.

+2

Lamento que no sea posible. Si un cambio muy leve en tu código hizo que el error "desapareciera", tu probablemente tenga un error grave en su código que el cambio escondió. Use 'valgrind' u otras herramientas similares para rastrearlo. Un compilador que optimiza una llamada' close' sería catastrophi do. – Mat

5

Tuve este problema también. Tiene una fuga de identificador de archivo. Puede depurar esta imprimiendo una lista de todos los identificadores de archivos abiertos (en sistemas POSIX):

void showFDInfo() 
{ 
    s32 numHandles = getdtablesize(); 

    for (s32 i = 0; i < numHandles; i++) 
    { 
     s32 fd_flags = fcntl(i, F_GETFD); 
     if (fd_flags == -1) continue; 


     showFDInfo(i); 
    } 
} 

void showFDInfo(s32 fd) 
{ 
    char buf[256]; 

    s32 fd_flags = fcntl(fd, F_GETFD); 
    if (fd_flags == -1) return; 

    s32 fl_flags = fcntl(fd, F_GETFL); 
    if (fl_flags == -1) return; 

    char path[256]; 
    sprintf(path, "/proc/self/fd/%d", fd); 

    memset(&buf[0], 0, 256); 
    ssize_t s = readlink(path, &buf[0], 256); 
    if (s == -1) 
    { 
     cerr << " (" << path << "): " << "not available"; 
     return; 
    } 
    cerr << fd << " (" << buf << "): "; 

    if (fd_flags & FD_CLOEXEC) cerr << "cloexec "; 

    // file status 
    if (fl_flags & O_APPEND ) cerr << "append "; 
    if (fl_flags & O_NONBLOCK) cerr << "nonblock "; 

    // acc mode 
    if (fl_flags & O_RDONLY ) cerr << "read-only "; 
    if (fl_flags & O_RDWR ) cerr << "read-write "; 
    if (fl_flags & O_WRONLY ) cerr << "write-only "; 

    if (fl_flags & O_DSYNC ) cerr << "dsync "; 
    if (fl_flags & O_RSYNC ) cerr << "rsync "; 
    if (fl_flags & O_SYNC ) cerr << "sync "; 

    struct flock fl; 
    fl.l_type = F_WRLCK; 
    fl.l_whence = 0; 
    fl.l_start = 0; 
    fl.l_len = 0; 
    fcntl(fd, F_GETLK, &fl); 
    if (fl.l_type != F_UNLCK) 
    { 
     if (fl.l_type == F_WRLCK) 
     cerr << "write-locked"; 
     else 
     cerr << "read-locked"; 
     cerr << "(pid:" << fl.l_pid << ") "; 
    } 
} 

Por volcar todos los archivos abiertos se darán cuenta rápidamente donde su pérdida de identificador de archivo es.

Si su servidor genera subprocesos. P.ej. si se trata de un servidor de estilo "fork", o si está generando otros procesos (por ejemplo, a través de cgi), debe asegurarse de crear sus manejadores de archivo con "cloexec", tanto para los archivos reales como para los sockets.

Sin cloexec, cada vez que realiza un fork o spawn, todos los identificadores de archivos abiertos se clonan en el proceso secundario.

También es muy fácil cerrar las tomas de red, p. Ej. simplemente abandonándolos cuando la parte remota se desconecta. Esto se escapa maneja como loco.

4

Esto significa que la cantidad máxima de archivos abiertos simultáneamente.

resuelto:

Al final del archivo /etc/security/limits.conf es necesario agregar las siguientes líneas:

* soft nofile 16384 
* hard nofile 16384 

En la consola actual desde la raíz (sudo no funciona) hacer:

ulimit -n 16384 

Aunque esto es opcional, si es posible reiniciar el servidor.

En /etc/nginx/nginx.conf archivo para registrar el nuevo valor igual a worker_connections16384 dividir por el valor worker_processes.

Si no lo hizo ulimit -n 16384, necesita reiniciar, entonces el problema retrocederá.

PS:

Si después de la reparación es visible en los registros de error accept() failed (24: Too many open files):

En la configuración nginx, propevia (por ejemplo):

worker_processes 2; 

worker_rlimit_nofile 16384; 

events { 
    worker_connections 8192; 
} 
0

Sólo otra información sobre CentOS . En este caso, al usar "systemctl" para iniciar el proceso. Tiene que modificar el archivo del sistema ==> /usr/lib/systemd/system/processName.service .Had esta línea en el archivo:

LimitNOFILE=50000 

Y justo volver a cargar el sistema de conf:

systemctl daemon-reload 
Cuestiones relacionadas