Tecnología de copia cero (DMA, MMAP, sendfile)

mmap de copia cero, archivo de envío

definición

La tecnología de copia cero resuelve principalmente el problema de rendimiento del envío de archivos en operaciones de E/S de red tradicionales: la siguiente figura muestra las operaciones de CPU involucradas en E/S tradicionales durante una lectura y escritura:

inserte la descripción de la imagen aquí

  • Se trata de 4 conmutadores de contexto de modo de usuario↔modo kernel, de los cuales los conmutadores de lectura dos veces y los conmutadores de escritura dos veces;
  • Implica 4 copias de datos. Entre ellos, el DMA copia dos veces y la CPU copia dos veces;

Los múltiples cambios de contexto y las copias de las operaciones anteriores afectarán el rendimiento.

Puede utilizar la tecnología de copia cero y mmap+writeoptimizar .sendfilesplice

DMA

DMA (Direct Memory Access), es decir, acceso directo a la memoria , es un mecanismo para transferir datos rápidamente . No requiere la participación de la CPU cuando se utiliza para la transferencia de datos .

El uso de DMA para copiar datos obtendrá una parte de los recursos del bus de datos del sistema para la transferencia de datos sin parámetros de CPU. Las lecturas de E/S tampoco provocan interrupciones. La CPU lee la operación de E/S y las llamadas al sistema provocarán una interrupción.

Mapa mm

mmap (mapa de memoria) utiliza la memoria virtual y el mapeo de direcciones para reducir una copia. Puede reducir el consumo de rendimiento de la copia de datos del modo kernel al modo usuario.

inserte la descripción de la imagen aquí

Como se muestra en la figura anterior, los datos del esclavo no se copian del estado del núcleo al estado del usuario, pero la dirección de memoria virtual del archivo que se va a transmitir se obtiene directamente a través del mapeo de memoria.Al enviar, el archivo que se va a enviar puede ser se transfiere a través de la dirección de memoria virtual compartida.La información se copia en el búfer del socket y se envía.

#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>

int main() {
    
    
    // 打开文件并将其映射到内存中
    int fd = open("file.txt", O_RDONLY);
    size_t size = lseek(fd, 0, SEEK_END);
    char* data = (char*) mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);

    // 创建套接字并连接到目标地址
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(1234);
    connect(sock, (sockaddr*) &addr, sizeof(addr));

    // 将内存中的数据直接写入套接字
    write(sock, data, size);//

    // 关闭套接字和文件,并解除内存映射
    close(sock);
    munmap(data, size);
    close(fd);

    return 0;
}

mmap aquí solo reduce la copia, pero aún requiere 4 cambios de contexto. Entonces, ¿hay alguna forma de reducir el cambio de contexto? Entonces aparecerá el archivo de envío.

sendfile

sendfile puede reducir el cambio de contexto al enviar archivos.

A partir de la versión 2.1 de Linux, se introdujo Linux sendfilepara simplificar las operaciones. sendfileEl método puede ser reemplazado por el mmap/writemétodo anterior para una mayor optimización.

sendfileHaz lo siguiente:

  mmap();
  write();

替换为:

 sendfile();

Esto reduce el cambio de contexto, porque hay una aplicación menos para iniciar writela operación, para iniciar sendfilela operación directamente.

inserte la descripción de la imagen aquí

Copie directamente los datos del disco en el área del búfer a través de DMA y copie los datos del búfer en el área del búfer del socket en el modo kernel, sin la participación del modo de usuario.

#include <sys/socket.h>
#include <fcntl.h>
#include <cstring>

int main() {
    
    
    // 打开文件并获取文件描述符
    int fd = open("file.txt", O_RDONLY);

    // 创建套接字并连接到目标地址
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(1234);
    connect(sock, (sockaddr*) &addr, sizeof(addr));

    // 使用sendfile函数将文件信息发送到套接字
    off_t offset = 0;
    struct stat stat_buf;
    fstat(fd, &stat_buf);
    sendfile(sock, fd, &offset, stat_buf.st_size);//直接使用sendfile发送文件,避免上下文切换

    // 关闭套接字和文件
    close(sock);
    close(fd);

    return 0;
}

Se puede ver que sendfile ha experimentado 3 acciones de copia y no hay un cambio de estado frecuente entre el modo de usuario y el modo kernel.

Entonces, sendfile es perfecto, ¿también puede guardar una copia de la CPU?

enviar archivo con dispersión/recopilación

El kernel de Linux 2.4 está optimizado y proporciona scatter/gatherla operación de archivo de envío con , que puede CPU COPYeliminar el último. El principio es que el búfer de lectura y el búfer de zócalo no copian datos en el espacio del núcleo, sino que registran la dirección de memoria y el desplazamiento del búfer de lectura en el búfer de zócalo correspondiente, por lo que no hay necesidad de copiar. Su esencia es consistente con la solución de la memoria virtual, que es el registro de la dirección de la memoria.

La siguiente figura muestra el principio del archivo de envío de dispersión/recopilación:

inserte la descripción de la imagen aquí

empalme

La diferencia sendfilees que splicese permite que dos archivos se conecten entre sí, no solo los archivos y socketla transferencia de datos.

socketPara el caso especial de enviar datos hacia y desde un descriptor de archivo , sendfilesiempre se han utilizado llamadas al sistema;

Y splicesiempre ha sido solo un mecanismo, no se limita a sendfilela función. Es decir, sendfile es un subconjunto de splice.

A diferencia de sendfile, splice no requiere soporte de hardware.

Para obtener más información, consulte:
https://juejin.cn/post/6995519558475841550
https://juejin.cn/post/7126195733471952910

Supongo que te gusta

Origin blog.csdn.net/weixin_44477424/article/details/131774100
Recomendado
Clasificación