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:
- 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+write
optimizar .sendfile
splice
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.
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 sendfile
para simplificar las operaciones. sendfile
El método puede ser reemplazado por el mmap/write
método anterior para una mayor optimización.
sendfile
Haz lo siguiente:
mmap();
write();
替换为:
sendfile();
Esto reduce el cambio de contexto, porque hay una aplicación menos para iniciar write
la operación, para iniciar sendfile
la operación directamente.
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/gather
la operación de archivo de envío con , que puede CPU COPY
eliminar 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:
empalme
La diferencia sendfile
es que splice
se permite que dos archivos se conecten entre sí, no solo los archivos y socket
la transferencia de datos.
socket
Para el caso especial de enviar datos hacia y desde un descriptor de archivo , sendfile
siempre se han utilizado llamadas al sistema;
Y splice
siempre ha sido solo un mecanismo, no se limita a sendfile
la 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