Manejo de e/s stdin/stdout y ficheros

Iniciado por Definitive, 14 Abril 2017, 16:23 PM

0 Miembros y 2 Visitantes están viendo este tema.

Definitive

Hola gente!!! Encantado de comenzar a conocerlos.
Antes que nada, quería contarles que soy novato tanto en lo que tiene que ver con el foro como en la programación, aunque vengo siguiéndolos desde hace un tiempo. Así que porfa, tengan paciencia si meto la pata, y no duden en señalarlo cuando pase que estamos para aprender.

Mi problema:
Hace mucho que vengo "coqueteando" con la programación, leyendo kilómetros de manuales e intentando descifrar cómo funcionan exactamente los programas, yendo línea por línea. El punto es que me he dado cuenta que a menos que empiece a poner en práctica algo de toda esa teoría, jamás voy a aprender bien. Mucho menos tratándose de c/c++.
Así que me decidí a hacer algo: quiero aprender haciendo, escribiendo algo con lo que me sienta realmente a gusto, un proyecto que me fascine, pero que a su vez sea útil y devuelva algo a la comunidad Open Source. Es aquí donde me pongo en pesado y pido ayuda ;D

Proyecto: https://github.com/schnaader/precomp-cpp/commits/master (La compresión de datos es lo mío)
Precomp es una pieza de software excepcional. Básicamente, hace posible lo imposible: comprimir información que ya se encuentra comprimida. ¿Cómo lo hace? En pocas palabras, deshace el proceso de compresión (métodos viejos y débiles como LZW, deflate o bzip2) y vuelve a empaquetar los datos con un algoritmo más nuevo y potente como LZMA. A su vez, utiliza librerías ajenas para recomprimir imágenes JPG y MP3, que de otro modo tampoco podrían comprimirse demasiado. La trampa consiste en hace todo eso de manera tal que al revertir el proceso, los datos obtenidos sean exactamente iguales a los originales (https://es.wikipedia.org/wiki/Algoritmo_de_compresi%C3%B3n_sin_p%C3%A9rdida). Sólo un pequeño número de programas hacen eso actualmente, de los cuales todos son experimentales y el más completo es este. (PowerArchiver está planeando incluir una funcionalidad de recompresión en su siguiente version)

Tarea: Francamente, precomp está en una etapa temprana de desarrollo (0.4.6) y le vendrían bien muchas mejoras. Tengo varias ideas en mente, y todas ellas son feasibles, pero creo que la más fácil de implementar es esta: Conseguir que direccione los datos procesados hacia la salida estándar, y pueda leer, al menos cuando "descomprime" o restaura, desde la entrada estándar. Para eso habría que redireccionar la interfaz hacia stderr y modificar el parser de la línea de comandos para que admita algo así como: programa entrada -
programa - salida

en lugar de la opción actual que es:programa entrada
programa -osalida entrada

Esto es necesario porque la principal utilidad de esta herramienta no es comprimir con LZMA sino "preparar" los datos para utilizar otra herramienta después. Esa herramienta puede ser cualquier compresor en línea de comandos.

Ayuda???  :rolleyes: :-*
Quiero comprender cómo es el flujo de datos en un programa c++. La estructura interna de precomp es demasiado compleja para estudiarla en detalle por ahora, porque analiza el fichero de entrada en varios niveles o "recursion depth" y utiliza librerías de terceros, pero por ahora me conformaría con entender cómo se transforma la salida de todo ese proceso en un fichero de disco rígido, y cómo lo podemos redirigir hacia stdin/out. Es decir, la última parte del proceso.

¿Por dónde empiezo a mirar? ¿Existe una función común a todo programa que se encargue de manejar su salida de datos? ¿O es cuestión de recorrer todo el código y modificar una a una las funciones? Cualquier ayudita será agradecida. Aunque sea apuntarme en la dirección correcta.
Muchas gracias desde ya! Y si pueden poner algún código de ejemplo tanto mejor ;)

aurquiel

#1
 :D

Hola te puedo hablar es desde linux y de c no de c++ y lo que te voy a decir te va a conducir a otra pregunta de como funciona, pero vas a estar mas cerca aunque la respuesta no es tan trivial se necesita tiempo para comprer como funciona.

printf() es la funcion de impresión en patalla de c, pero printf es una mascara por asi decirlo de una función del kernel de linux system call llamada write() que opera a bajo nivel

Si tomas un programa en c
#include <stdio.h>
int main(void)
{ printf("hello"); return 0; }


Lo compilas, abres la consola  
gcc -Wall -o hello hello.c
y luego ejecutas
strace ./hello

Impresionado?

execve("./book", ["./book"], [/* 48 vars */]) = 0
brk(NULL)                               = 0x1640000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd227e03000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=162022, ...}) = 0
mmap(NULL, 162022, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd227ddb000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1864888, ...}) = 0
mmap(NULL, 3967392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd227817000
mprotect(0x7fd2279d6000, 2097152, PROT_NONE) = 0
mmap(0x7fd227bd6000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bf000) = 0x7fd227bd6000
mmap(0x7fd227bdc000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd227bdc000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd227dda000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd227dd9000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd227dd8000
arch_prctl(ARCH_SET_FS, 0x7fd227dd9700) = 0
mprotect(0x7fd227bd6000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7fd227e05000, 4096, PROT_READ) = 0
munmap(0x7fd227ddb000, 162022)          = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
brk(NULL)                               = 0x1640000
brk(0x1661000)                          = 0x1661000
write(1, "hello", 5hello)                    = 5
exit_group(0)                           = ?
+++ exited with 0 +++


Todo eso son system calls del kernel de linux, mira esta linea

write(1, "hello", 5hello)                    = 5

Hasta aquí llego yo no he tenido el tiempo para saber como esta construido el kernel de linux, pero parece ser complicado.

Se que no he respondido a totalidad tu pregunta, pero hasta ahora yo solo hago aplicaciones de usuarios, tu proyecto parece ser muy ambicioso, al indagar mas a fondo te vas a encontrar cosas que requerirán mucho tiempo de análisis y estudio, mejor comenzar por algo suave y con el tiempo ir hacia aguas mas profundas.

Esto quiere decir que c esta corriendo encima de todo esto, el kernel.

Y por asi decirlo el Kernel esta construido en su propia version de c diferente, con funciones llamdas diferentes al ANSI C pero de comportamiento similar como son prinftk en ves de printf okmalloc en vez de malloc y asi otras mas.

Definitive

Muchas gracias aurquiel por tu respuesta!
Respondiendo a tu pregunta, sí, me impresiona el tema del trabajo tras bambalinas del kernel.

Aunque francamente, no es de mi interés en este momento porque como bien señalaste, es demasiado complicado para mis habilidades y conocimientos actuales y no es el campo en el que quiero indagar ahora.

Mas bien, lo que quiero comprender cae siempre dentro de la programación de aplicaciones para usuario final, no código que sea parte del S.O.
A ver, para explicarme mejor, siempre dentro de un fichero .cpp, que luego iremos a compilar y distribuir como "mi_programa.exe" o "mi_programa.deb", supongamos...

...que en lugar de escribir "Hola mundo" en una consola, decido escribirlo en un archivo. Hasta donde he podido ver, lo que necesito hacer es abrir un archivo en modo texto/escritura y usar fprintf para dirigir mi texto de salida hacia el archivo en disco.
Ahora, digamos que mi programa no escribe una frase sino un número en coma flotante. Y como no quiero que el archivo ocupe demasiado espacio, no lo guardo como texto sino como datos hexadecimales. En este caso, debo abrir un archivo en modo binario/escritura. Me sigues?
Siguiente caso, ahora quiero leer ese número, multiplicarlo por tres dentro de mi programa y escribir nuevamente el resultado. Ahora debo abrir un fichero existente en modo binario/lectura_y_escritura.
A menos que me esté equivocando, un simple fprint (datos_a_guardar) tendría que funcionar para todos los casos.

El programa que estoy editando hace algo muy parecido, sólo que en lugar de una simple multiplicación,

1)analiza la estructura del fichero de entrada,
2)lo divide en partes que envía a otras porciones de código para ser procesadas dentro del mismo programa y
3)recibe los resultados.

Esos resultados (A), mas ciertas partes que se copian textualmente del fichero de entrada (B), más ciertos datos generados para poder reconstruir nuevamente el fichero original (C), son lo que se copia a un nuevo archivo de salida como un sólo stream de datos. Así que tenemos:
< archivo de entrada | magia | otro archivo diferente de salida >
Lo que quiero saber es qué parte del código se encarga de sólo esa pequeña operación final de escribir todo al disco. Repito: no lo que hace el kernel ni nada parecido, sino dentro del mismo programa, qué linea/lineas de código están envueltas en decir "esto está listo, guardémoslo".

Gracias desde ya por cualquier respuesta y perdón por la pregunta tan obvia para aquel que sepa programar. Idealmente, espero seguir aprendiendo hasta que la respuesta sea obvia para mi también.