Explicación detallada del uso de fwrite y write en linux multiproceso

Para operaciones de archivos en Linux, a algunas personas les gusta usar la operación de flujo de archivos de la biblioteca C, y a algunas personas les gusta usar las llamadas al sistema nativo de Linux. En términos generales, la operación de archivos de la biblioteca C será más eficiente, porque la propia biblioteca C realiza el procesamiento de la caché de archivos. Hoy, estudio principalmente fwrite y escribo bajo multi-threading. Cada hilo escribe en el mismo FILE * o fd para ver si el resultado es el comportamiento esperado.

El primer caso: usando el fwrite de la biblioteca C, la implementación del hilo es la siguiente:
Inserte la descripción de la imagen aquí

El segundo caso: usando la llamada al sistema write, la implementación del hilo es la siguiente:
Inserte la descripción de la imagen aquí

Veamos la implementación del hilo principal:

Inserte la descripción de la imagen aquí

Entre ellos, LOOPS se define como 1000000. En otras palabras, los subprocesos 1 ~ 3 escriben "aaaaaa \ n", "bbbbbb \ n" y "cccccc \ n" un millón de veces respectivamente. Si la operación de escritura del archivo es "segura para subprocesos", entonces el número final de líneas de archivo debe ser de 3 millones de líneas, y cada línea solo puede ser una de "aaaaaa", "bbbbbb" y "cccccc".

[Beneficios del artículo] Materiales de aprendizaje para arquitectos de servidores C / C ++ Linux más el grupo 812855908 (datos que incluyen C / C ++, Linux, tecnología golang, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, medios de transmisión, CDN, P2P, K8S, Docker, TCP / IP, corrutina, DPDK, ffmpeg, etc.)
Inserte la descripción de la imagen aquí

A continuación, observe los resultados de la prueba:

1. Se define la macro USE_CLIB, es decir, se utiliza el fwrite de la biblioteca C. Los resultados son los siguientes:
Inserte la descripción de la imagen aquí

2. Comente el USE_CLIB rojo, es decir, use directamente la escritura de llamada al sistema, los resultados son los siguientes:
Inserte la descripción de la imagen aquí

A partir de los resultados de la prueba anterior, ya sea la escritura de la biblioteca C o la escritura de la llamada al sistema, puede garantizar que la salida no se mezclará, es decir, la salida de varios subprocesos no se mezclará, pero cuando el Se utiliza escritura de llamada al sistema, el número de línea de archivo final es incorrecto Como se esperaba, es mucho más pequeño que el número total de 3 millones de filas. También demuestra que la llamada al sistema de escritura no es "segura para subprocesos". En el subproceso múltiple, la salida se cubrirá entre sí. La escritura de la biblioteca C es una función segura para subprocesos.

¿Por qué el resultado es así? Primero miramos la implementación de fwrite:
Inserte la descripción de la imagen aquí

Dentro de fwrite, utiliza un bloqueo para garantizar la serialización de las operaciones, logrando así la seguridad de los subprocesos.

Y la realización de la escritura, ver la siguiente figura:
Inserte la descripción de la imagen aquí

Antes de escribir, use file_pos_read para obtener el desplazamiento. Si en el caso de multi-core y multi-threading, los dos núcleos pueden caer en el estado de kernel al mismo tiempo y obtener el desplazamiento actual del archivo al mismo tiempo, los valores deben ser iguales. Entonces, los dos subprocesos escriben datos en el mismo desplazamiento. Al final, el tamaño real del archivo no es el esperado.

Resumen final:
La escritura de la biblioteca C es una función segura para subprocesos, y la escritura de llamada al sistema requiere un bit de marca adicional O_APPEND para escritura adicional para garantizar que el desplazamiento no se superponga y logre la escritura concurrente esperada; puede modificar lo siguiente Pruebe el código, pruebe en su propio entorno.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//#define USE_CLIB

#define TEST_FILE	"./tmp.txt"

#define LOOPS		(1000000)


#ifdef USE_CLIB
struct thr_data {
    
    
	FILE *fp;
	const char *data;
};

static void * write_data(void *data)
{
    
    
	struct thr_data *d;
	size_t len;
	int i;

	d = data;
	len = strlen(d->data);
	for (i = 0; i < LOOPS; ++i) {
    
    
		fwrite(d->data, len, 1, d->fp);
	}

	return NULL;
}

#else
struct thr_data {
    
    
	int fd;
	const char *data;
};

static void *write_data(void *data)
{
    
    
	struct thr_data *d;
	int i;
	size_t len;

	d = data;
	len = strlen(d->data);
	for (i = 0; i < LOOPS; ++i) {
    
    
		write(d->fd, d->data, len); 
	}

	return NULL;
}
#endif



int main(void)
{
    
    
	pthread_t t1, t2, t3;
	struct thr_data d1, d2, d3;

#ifdef USE_CLIB
	FILE *fp = fopen(TEST_FILE, "w");
	d1.fp = d2.fp = d3.fp = fp;
#else
	//int fd = open(TEST_FILE, O_WRONLY|O_TRUNC);
	int fd = open(TEST_FILE, O_WRONLY|O_TRUNC|O_APPEND);
	d1.fd = d2.fd = d3.fd = fd;
#endif

	d1.data = "aaaaaa\n";
	d2.data = "bbbbbb\n";
	d3.data = "cccccc\n";

	pthread_create(&t1, NULL, write_data, &d1);
	pthread_create(&t2, NULL, write_data, &d2);
	pthread_create(&t3, NULL, write_data, &d3);

	pthread_join(t1, NULL);
	pthread_join(t2, NULL);
	pthread_join(t3, NULL);

#ifdef USE_CLIB
	fclose(fp);
#else
	close(fd);
#endif

	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_40989769/article/details/110927256
Recomendado
Clasificación