sisoputnfrba / foro

Foro de consultas para el trabajo práctico
151 stars 7 forks source link

[Consulta] Uso de mmap() #1132

Closed lausoffici closed 6 years ago

lausoffici commented 6 years ago

Sumo otra consulta, no entendemos como usar el mmap(). Leimos en un issue que nos:

" va a ahorrar horas y dolores de cabeza al momento de persistir las claves en los archivos. "

Entonces confiamos y creemos que es util utilizarlo, lo que pasa es que no entendemos que parametros hay que pasarle a la funcion ya que hay que pasarle flags de proteccion y demas...

  1. Mas que nada igual es que, segun leimos en el man mmap y en varias paginas, ya hay que asignarle un tamaño al archivo y recien ahi poder mapearlo con ese length, tiene sentido esto en el TP o no? Ahora que pienso, no recuerdo si los valores, al igual que las claves, tienen un maximo de 40 chars o no?

  2. Y despues, si o si, hay que usar open() en vez de fopen() y demas para usarlo?

  3. Concluyendo que terminamos pudiendolo usar, cada vez que recibo un nuevo valor, primero deberia "vaciar" los bytes que hay en el mmap() y despues asignar con memcpy()? Para vaciarlo como se haria?

Saludos

santiluccadeguevara commented 6 years ago

Hola. Ya se que esperabas un ayudante, pero bueno. Leí mmap() y quería ayudar, porque en algún momento me pasó igual que a vos XD

mmap() te devuelve un puntero que está vincula/mapea un cacho de memoria con un archvio: es decir, si vos escribís (o "memcopiás") ese puntero que te devuelve el mmap(), eso se va a reflejar en ese archivo (Cuando el SO tenga ganas o se lo indiques con la función msync() creo). Ej: mapeas un archivo vacío que ocupa 5 bytes, y le memcopiás un "hola", y en el archivo (suponiendo que es un .txt) te va a aprecer un "hola".

Para empezar, esta es la declaración/firma de mmap()

void* mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

1º parámetro: void* addr Con esto le indicas al SO en qué posición de memoria querés que se mapee el archivo a memoria. Yo te sugiero que lo dejes en NULL, a menos que estés haciendo algo muy específico. Es lo más sencillo (y portable). Podrías llegar a mapear la dirección que tengas del valor de una clave en el Storage, con lo cual el SO podría llegar a guardarte cosas en un archivo sin que te des cuenta, y tipo, tenés un backup más por las dudas, pero no sé, medio fulero. Creo que podés alterar el cacho de memoria mapeado en tiempo de ejecución y esas cosas, pero no te quiero llenar el cerebro de dudas. Dejá que el SO elija el cacho de memoria XD

2º parámetro: size_t length Con esto indicás cuanto espacio ocupa el puntero que te devuelve mmap(). Seguramente vas a querer que sea el tamaño del valor que querés bajar a disco.

3º parámetro: int prot Con esto indicás los permisos que tienen las páginas de memoria que pedís para mapear el archivo. En el man de mmap() creo que están las flags, y son las mismas de un archivo común: lectura, escritura, y ejecución. Para el TP, te conviene tener las tres (creo...), porque si no, no las podés leer ni escribir cuando le hagas memcpy() al puntero de memoria mapeada, ni ver en tu VM si no tenés permisos de ejecución. Acordate que los flags son a nivel bits, así que los tenés que "sumar" con el operador | (El ó lógico) para poner varios permisos.

4º parámetro: int flags Con esto indicás la visibilidad que otros procesos tienen de los archivos mapeados a memoria. Te recomiendo MAP_SHARED porque le da visibilidad pública. Calculo que si querés abrir el archivo mientras ejecutás el TP, vas a poder leer lo último que actualizó un STORE o dump.

5º parámetro: int fd Este es el descriptor de archivo (cómo los de los sockets) que vas a querer mapear a memoria. Es re importante porque ahí es donde vas a poder guardar los valores de un STORE. Lo podés obtener con la función open().

6º parámetro: off_t offset Esto te indica en qué posición del archivo querés empezar a mapear. Vas a quererlo en el principio seguro, así que ponele 0, a menos que vos quieras hacer algo medio loco y chequear que si una clave que antes tenía el valor 'AB' y después le querés setear 'ABC', ahí a lo mejor vas a querer que empiece en 2 para sólo agregar la 'C', pero es complicarse para nada. Creo que tiene que ser múltiplo del tamaño de página, así que no, ponelo en 0 y listo XD

En síntesis: pasale NULL al addr, el tamaño de tu valor al length, y el fd del archivo a fd, offset 0, y al resto, lo que te pinte, y va a funcionar masomenos bien (Lo probé, así que sí, anda XD)

Ahora, con tus preguntas:

1) Creo que el archivo no tiene por qué tener el mismo tamaño que el cacho de memoria mapeado, y no recuerdo que los valores tengan tamaño máximo. Pero, mirate la función ftruncate, que te puede servir.

2) Claro. Como te mencioné, vas a tener que usar open() para obtener un fd y no un FILE*, así podes "mmapear".

3) Si usas bien ftruncate() con mmap(), no hace falta que te preocupes por vaciar la memoria mapeada. La pisas con memcpy() y el tamaño del nuevo valor y listo XD

Espero que te sirva. Si algo no cierra, bueno. Ya te ayudará alguien con más cancha XD Saludos.

iago64 commented 6 years ago

Bueno bueno bueno... me parece que tenemos candidato para ayudante 2c2018 con @santiluccadeguevara ? 😄 @lausoffici Ciertamente no tengo nada para agregar a lo que dijo santi que dejo una explicación mas que excelente de como funcionan los parametros de mmap y como tenes que resolver las dudas que tenias :)

Cualquier otra duda no tenes mas que dejarla y veo si no me ganan de mano de nuevo 😆

Abrazo!

nachozullo commented 6 years ago

Bueno ahí siguiendo un poco lo que decía @santiluccadeguevara intentamos hacerlo. Pero nos tira segmentation fault y no sabemos de donde.

entrada_t* entrada;
    data_t* storage;
    int fd;
    char* mem_ptr;
    char* path = "/home/nacho/UTN/3ero/Sistemas Operativos/tp-2018-1c-7LOKO/Instancias/clave.txt";
    char* directorioMontaje = malloc(strlen(path) + 1);

    strcpy(directorioMontaje, path);

    claveBuscada = malloc(strlen(clave)+ 1);
    strcpy(claveBuscada, clave);

    entrada = buscarEnTablaDeEntradas(clave);   // BUSCO LA ENTRADA POR LA CLAVE
    storage = buscarEnStorage(entrada->numero); // BUSCO EL STORAGE DE ESE NUM DE ENTRADA

    if((fd = open(directorioMontaje, O_CREAT, S_IRWXU )) < 0){
        log_error(logger, ANSI_COLOR_BOLDRED"No se pudo realizar el open "ANSI_COLOR_RESET);
    }

    size_t tamanio = strlen(storage->valor) + 1;

    lseek(fd, tamanio-1, SEEK_SET);
    write(fd, "", 1);

    mem_ptr = mmap(NULL, tamanio , PROT_WRITE | PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);

    memcpy(mem_ptr, storage->valor, tamanio);

    msync(mem_ptr, tamanio, MS_SYNC);

    munmap(mem_ptr, tamanio);

    free(path);
    free(directorioMontaje);
iago64 commented 6 years ago

@nachozullo lo corrieron con valgrind a ver que les dice?

nachozullo commented 6 years ago

Me habia olvidado de adjuntar eso

[]( captura de pantalla de 2018-06-08 21-28-52

iago64 commented 6 years ago

Fijate en el archivo instancia.c linea 222 que referencias a memoria tenes ahi, ya que ahí es donde estas intentando hacer una escritura de 1 byte en la direccion 0xfffffffff...

Saludos.-

nachozullo commented 6 years ago

Si dice que esta en el memcpy pero no encontramos el error. Ahi hicimos el store sin mmap y hoy seguro les consultemos bien. Gracias!

iago64 commented 6 years ago

Buenas buenas!! @nachozullo después de ponerme a recordar algunas cosillas de mmap() y de ponerme a ver donde podía ser que estuviera el error, encontré algo que capaz pueda ser de ayuda :) Cuando estas haciendo el open del archivo lo haces con el flag O_CREAT, el problema es que ahí vos simplemente le decis ok macho, crea el archivo, peeero, no le estas dando permisos para que con ese FD puedas escribir en el archivo con lo cual deberias agregar el flag de O_RDWR junto al flag anterior y esto te daría la siguiente línea: fd = open(directorioMontaje, O_RDWR | O_CREAT, S_IRWXU ) Ya con esto, cuando crees el archivo te va a dejar escribir adentro, ya que ese segfault que te deriva a 0xfffff es la salida de mmap para decirte que hubo un error, que lo podes ver haciendo printf("%s", strerror(errno)); y muy posiblemente te tire un permission denied

Espero que este wall of text te sirva para poder resolver el problema y si, si lo que pensas es que lo tuvimos delante la vista todo el tiempo y no nos dimos cuenta, es algo que suele pasar muuuchas veces cuando te quemas con un error :( Cualquier cosa avisame. Saludos.-

lausoffici commented 6 years ago

Muchas gracias, efectivamente era eso el problema. Cierro el issue. 👍🏼👍🏼