2012-06-05 11 views
19

Estoy viendo un par de cosas extrañas con un par de tomas AF_UNIX creados por una llamada tales como:Toma de corriente AF_UNIX?

socketpair(AF_UNIX, SOCK_STREAM, 0, sfd); 

Dónde SFD es un int [2] matriz para los descriptores de fichero.

En primer lugar, el tamaño del búfer predeterminado parece ser exactamente 122K (124928 bytes), en lugar de nada de/proc/sys/net (como wmem_default que se establece en 128K). ¿Alguien sabe la causa de este extraño tamaño de búfer?

En segundo lugar, al escribir mensajes pequeños a través del zócalo (8 bytes). Solo puedo escribir 423 de ellos antes de los bloques de escritura, que son solo 8 * 423 = 3384 bytes, otro tamaño impar. Los mensajes actúan como si tuvieran más de 295 bytes cada uno. ¿Cuál es la fuente de esta sobrecarga?

Correr en RHEL6 (2.6.32, 64 bits)

me escribió un programa para probar diferentes tamaños de datos para comparar los gastos generales:

#include <errno.h> 
#include <stdio.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

#define DATA_SIZE 4 

void run(size_t size) { 
    int sfd[2]; 
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) == -1) { 
     perror("error"); 
    } 


    int sndbuf, sbsize = sizeof(sndbuf); 
    getsockopt(sfd[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, (socklen_t*)&sbsize); 

    printf("Data Size: %zd\n", size); 
    char buff[size]; 
    size_t wrote=0; 
    for (size_t ii=0; ii < 32768; ii++) { 
     if ((send(sfd[0], buff, size, MSG_DONTWAIT) == -1) && (errno == EAGAIN)) { 
      wrote = ii; 
      break; 
     } 
    } 

    printf("Wrote:  %zd\n", wrote); 

    if (wrote != 0) { 
     int bpm = sndbuf/wrote; 
     int oh = bpm - size; 

     printf("Bytes/msg: %i\n", bpm); 
     printf("Overhead: %i\n", oh); 
     printf("\n"); 
    } 

    close(sfd[0]); close(sfd[1]); 
} 

int main() { 
    int sfd[2]; 
    socketpair(AF_UNIX, SOCK_STREAM, 0, sfd); 

    int sndbuf, sbsize = sizeof(sndbuf); 
    getsockopt(sfd[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, (socklen_t*)&sbsize); 

    printf("Buffer Size: %i\n\n", sndbuf); 
    close(sfd[0]); close(sfd[1]); 

    for (size_t ii=4; ii <= 4096; ii *= 2) { 
     run(ii); 
    } 
} 

que da:

Buffer Size: 124928 

Data Size: 4 
Wrote:  423 
Bytes/msg: 295 
Overhead: 291 

Data Size: 8 
Wrote:  423 
Bytes/msg: 295 
Overhead: 287 

Data Size: 16 
Wrote:  423 
Bytes/msg: 295 
Overhead: 279 

Data Size: 32 
Wrote:  423 
Bytes/msg: 295 
Overhead: 263 

Data Size: 64 
Wrote:  423 
Bytes/msg: 295 
Overhead: 231 

Data Size: 128 
Wrote:  348 
Bytes/msg: 358 
Overhead: 230 

Data Size: 256 
Wrote:  256 
Bytes/msg: 488 
Overhead: 232 

Data Size: 512 
Wrote:  168 
Bytes/msg: 743 
Overhead: 231 

Data Size: 1024 
Wrote:  100 
Bytes/msg: 1249 
Overhead: 225 

Data Size: 2048 
Wrote:  55 
Bytes/msg: 2271 
Overhead: 223 

Data Size: 4096 
Wrote:  29 
Bytes/msg: 4307 
Overhead: 211 

En comparación con el uso de una tubería definitivamente hay una gran cantidad de gastos generales:

Data Size: 4 
Wrote:  16384 
Bytes/msg: 4 
Overhead: 0 

Data Size: 8 
Wrote:  8192 
Bytes/msg: 8 
Overhead: 0 

Data Size: 16 
Wrote:  4096 
Bytes/msg: 16 
Overhead: 0 

Data Size: 32 
Wrote:  2048 
Bytes/msg: 32 
Overhead: 0 

Data Size: 64 
Wrote:  1024 
Bytes/msg: 64 
Overhead: 0 

Data Size: 128 
Wrote:  512 
Bytes/msg: 128 
Overhead: 0 

Data Size: 256 
Wrote:  256 
Bytes/msg: 256 
Overhead: 0 

Data Size: 512 
Wrote:  128 
Bytes/msg: 512 
Overhead: 0 

Data Size: 1024 
Wrote:  64 
Bytes/msg: 1024 
Overhead: 0 

Data Size: 2048 
Wrote:  32 
Bytes/msg: 2048 
Overhead: 0 

Data Size: 4096 
Wrote:  16 
Bytes/msg: 4096 
Overhead: 0 
+0

send() devuelve la cantidad de bytes realmente escritos. Debe totalizar estos, no solo suponiendo que todo esté escrito. – EJP

+1

Peor caso, he escrito menos de lo que afirmo, lo que empeoraría la sobrecarga del dominio. –

Respuesta

1

¿Ha mirado el valor de net.unix.max_dgram_qlen sysctl?

El kernel impone un límite en el número máximo de datagramas AF_UNIX en vuelo. En mi sistema, el límite es realmente muy bajo: solo 10.

+0

No era consciente de eso, no. ¿Eso se aplica aquí, sin embargo, ya que estoy cantando un tipo SOCK_STREAM? –

+1

No, eso solo debería aplicarse a los sockets de datagramas, al menos en la versión del kernel que estoy viendo. –

+1

De hecho, no puedo ver por qué un socket de datagrama Unix escribiría alguna vez corto si golpea wmem_max. –

5

Mire la página de comando man (7). Hay una sección que dice:

SO_SNDBUF Establece u obtiene el búfer máximo de envío de socket en bytes. El kernel dobla este valor (para dejar espacio para gastos generales de contabilidad) cuando se establece usando setsockopt (2), y este valor duplicado es devuelto por getsockopt (2). El valor predeterminado se establece mediante el archivo /proc/sys/net/core/wmem_default y el valor máximo permitido se establece mediante el archivo /proc/sys/net/core/wmem_max. El valor mínimo (duplicado) para esta opción es 2048.

por lo que parece que la sobrecarga es simplemente para mantener la contabilidad de información para el núcleo.

+0

Ni siquiera estoy seguro de que se aplique a los sockets locales, y una simple mitad del espacio disponible en el búfer aún no daría cuenta de todos los gastos generales que estoy viendo. –

+1

La página man no distingue entre AF_UNIX o los dominios no locales, por lo que supongo que se aplica de manera general. Esa es toda la documentación que he podido encontrar con respecto a la situación. Sospecho que si necesita saber "exactamente" para qué se usa la sobrecarga, tendrá que echarle un vistazo al código de red del kernel. – Chimera

+1

No acepté esta respuesta porque creo que incluso con la sobrecarga del factor de dos, sigo viendo demasiado por mensaje. Incluso si el kernel solo me dejara usar 62464 bytes, entonces debería poder escribir más de 15000 mensajes antes de llenar el buffer, y solo estoy viendo 1/30 de eso. –

Cuestiones relacionadas