Ventajas y desventajas de los diferentes tipos de transporte. Transporte logística transporte. Transporte internacional por carretera

Para usar correctamente gcc, el compilador estándar de Linux C, debe conocer las opciones de la línea de comandos. Además, gcc extiende el lenguaje C. Incluso si tiene la intención de escribir código fuente que cumpla con el estándar ANSI de este lenguaje, algunas extensiones de gcc son simplemente necesarias para comprender los encabezados de Linux.

La mayoría de las opciones de línea de comandos son las mismas que las utilizadas por los compiladores de C. Para algunas opciones, no hay estándares. En este capítulo, cubriremos las opciones más importantes que se utilizan en la programación diaria.

Es útil esforzarse por cumplir con el estándar ISO C, pero debido al hecho de que C es un lenguaje de bajo nivel, hay situaciones en las que los medios estándar no son lo suficientemente expresivos. Hay dos áreas en las que se usan ampliamente las extensiones gcc: interactuar con el código de ensamblaje (consulte http://www.delorie.com/djgpp/doc/brennan/) y crear bibliotecas compartidas (consulte el Capítulo 8). Dado que los archivos de encabezado son parte de las bibliotecas compartidas, algunas extensiones también aparecen en los archivos de encabezado del sistema.

Por supuesto, hay muchas más extensiones que son útiles en cualquier otro tipo de programación que pueden ser muy útiles en la codificación. Puede encontrar más información sobre estas extensiones en la documentación de gcc Texinfo.

5.1. Opciones de gcc

gcc acepta muchas opciones de comando. Afortunadamente, el conjunto de opciones que realmente necesita conocer no es tan bueno, y las cubriremos en este capítulo.

La mayoría de las opciones son iguales o similares a las de otros compiladores, gcc incluye una gran documentación de sus opciones disponibles a través de info gcc (man gcc también proporciona esta información, sin embargo, las páginas man no se actualizan tan a menudo como la documentación de Texinfo).

-sobre el nombre del archivo Especifica el nombre del archivo de salida. Esto generalmente no es necesario si está compilando un archivo de objeto, es decir, por defecto, filename.c se sustituye por filename.o. Sin embargo, si crea un archivo ejecutable, de manera predeterminada (por razones históricas) se crea con el nombre a.out. Esto también es útil cuando desea colocar el archivo de salida en un directorio diferente.
-de Compila sin vincular el archivo fuente especificado para la línea de comando. Esto crea un archivo objeto para cada archivo fuente. Cuando se usa make, generalmente se llama al compilador gcc para cada archivo de objeto; por lo tanto, en caso de error, es más fácil averiguar qué archivo no se pudo compilar. Sin embargo, si escribe comandos manualmente, a menudo se enumeran varios archivos en una sola llamada gcc. Si puede surgir ambigüedad al especificar varios archivos en la línea de comando, es mejor especificar solo un archivo. Por ejemplo, en lugar de gcc -c -o a.o a.c b.c, tiene sentido usar gcc -c -o a.o b.c.
-D foo Define macros de preprocesador en la línea de comando. Es posible que deba anular los caracteres que el shell trata como especiales. Por ejemplo, al definir una cadena, evite usar los caracteres que terminan en cadena ". Las dos formas más comunes son" -Dfoo \u003d "bar" "y -Dfoo \u003d \\" bar \\ ". La primera forma funciona mucho mejor si la cadena contiene espacios, porque El caparazón trata los espacios de una manera especial.
-I directorio Agrega un directorio a la lista de directorios para buscar archivos de inclusión.
-L directorio Agrega un directorio a la lista de directorios buscados para bibliotecas, gcc favorecerá las bibliotecas compartidas sobre las estáticas, a menos que se especifique lo contrario.
-l foo Enlaces contra la biblioteca lib foo. A menos que se indique lo contrario, gcc prefiere vincular con bibliotecas compartidas (lib foo .so) sobre las estáticas (lib foo .a). El vinculador busca en todas las bibliotecas enumeradas las funciones en el orden en que aparecen. La búsqueda finaliza cuando se encuentran todas las funciones requeridas.
-estático Enlace solo con bibliotecas estáticas. Ver el capítulo 8.
-g, -ggdb Incluye información de depuración. La opción -g obliga a gcc a incluir información de depuración estándar. La opción -ggdb le dice que incluya una gran cantidad de información que solo el depurador gdb puede entender.
Si el espacio en disco es limitado, o si desea sacrificar alguna funcionalidad por la velocidad del enlace, debe usar -g. En este caso, es posible que deba usar un depurador que no sea gdb. Para la depuración más completa, debe especificar -ggdb. En este caso, gcc preparará el máximo información detallada para gdb. Cabe señalar que, a diferencia de la mayoría de los compiladores, gcc incluye información de depuración en el código optimizado. Sin embargo, el rastreo en el depurador de código optimizado puede ser complicado porque en tiempo de ejecución puede haber saltos y saltos de código que se espera ejecutar. Sin embargo, puedes obtener buena presentación sobre cómo los compiladores optimizadores cambian la forma en que se ejecuta el código.
-O, -O n Fuerza a gcc a optimizar el código. Por defecto, gcc realiza una pequeña cantidad de optimización; Al especificar el número (n), la optimización se realiza en un cierto nivel. El nivel de optimización más común es 2; actualmente el nivel más alto de optimización en el estándar gcc es 3. Recomendamos usar -O2 u -O3; -O3 puede aumentar el tamaño de la aplicación, así que si eso es importante, intente con ambos. Si la memoria y el espacio en disco son importantes para su aplicación, también puede usar la opción -Os, que minimiza el tamaño del código al aumentar el tiempo de ejecución. gcc solo habilita builtins cuando se aplica al menos una optimización mínima (-O).
-ansi Soporte en programas en C de todos los estándares ANSI (X3.159-1989) o su equivalente ISO (ISO / IEC 9899: 1990) (comúnmente llamado C89 o con menos frecuencia C90). Cabe señalar que esto no garantiza el pleno cumplimiento de la norma ANSI / ISO.
La opción -ansi deshabilita las extensiones gcc, que generalmente entran en conflicto con los estándares ANSI / ISO. (Debido al hecho de que estas extensiones son compatibles con muchos otros compiladores de C, esto no es un problema en la práctica). Esto también define la macro __STRICT_ANSI__ (como se describe más adelante en este libro) que los archivos de encabezado usan para admitir un entorno compatible con ANSI / ISO.
-pedante Muestra todas las advertencias y mensajes de error requeridos por el estándar de lenguaje ANSI / ISO C. Esto no proporciona el pleno cumplimiento de ANSI / ISO.
-Pared Permite la generación de todas las advertencias de gcc, que generalmente es útil. Pero esto no incluye opciones que pueden ser útiles en casos específicos. Se establecerá un nivel de verbosidad similar para la pelusa del analizador con respecto a su código fuente, gcc le permite habilitar y deshabilitar manualmente cada advertencia del compilador. Todas las advertencias se detallan en el manual de gcc.
5.2. Archivos de encabezado
5.2.1. largo largo

El largo largo indica que un bloque de memoria es al menos tan grande como un largo. En Intel i86 y otras plataformas de 32 bits, el largo es de 32 bits y el largo es de 64 bits. En plataformas de 64 bits, los punteros y long long son de 64 bits, mientras que long puede ser de 32 o 64 bits dependiendo de la plataforma. El tipo largo largo es compatible con el estándar C99 (ISO / IEC 9899: 1999) y es una extensión C de larga duración proporcionada por gcc.

5.2.2. Funciones integradas

Algunas partes de los encabezados de Linux (en particular las que son específicas de un sistema en particular) usan funciones integradas de manera muy extensa. Son tan rápidos como las macros (sin costo de las llamadas a funciones) y proporcionan todo tipo de validación disponible en una llamada a funciones normales. Las funciones en línea de llamada de código deben compilarse con al menos una optimización mínima (-O) habilitada.

5.2.3. Palabras clave avanzadas alternativas

En gcc, cada palabra clave extendida (palabras clave no cubiertas por el estándar ANSI / ISO) tiene dos versiones: la palabra clave en sí y la palabra clave rodeada por dos guiones bajos en ambos lados. Cuando el compilador se usa en modo estándar (generalmente cuando se usa la opción -ansi), no se reconocen las palabras clave extendidas normales. Entonces, por ejemplo, la palabra clave de atributo en el archivo de encabezado debe escribirse como __atributo__.

5.2.4. Atributos

La palabra clave de atributo extendido se utiliza para proporcionar a gcc más información sobre una función, variable o tipo declarado que lo que permitiría el código ANSI / ISO C. Por ejemplo, el atributo alineado le dice a gcc cómo alinear una variable o tipo; el atributo empaquetado indica que no se utilizará relleno; noreturn especifica que una función nunca volverá, lo que permite a gcc optimizar mejor y evitar advertencias falsas.

Los atributos de función se declaran agregándolos a la declaración de función, por ejemplo:

void die_die_die (int, char *) __attribute__ ((__noreturn__));

Una declaración de atributo se coloca entre paréntesis y un punto y coma y contiene la palabra clave de atributo seguida de los atributos entre paréntesis dobles. Si hay muchos atributos, use una lista separada por comas.

int printm (char *, ...)

Atributo __ ((const,

formato (printf, 1, 2)));

En este ejemplo, puede ver que printm no considera ningún valor distinto de los especificados, y no tiene efectos secundarios relacionados con la generación de código (const), printm indica que gcc debería verificar los argumentos de la función de la misma manera que los argumentos printf (). El primer argumento es la cadena de formato y el segundo es el primer parámetro de reemplazo (formato).

Algunos de los atributos se discutirán a medida que avancemos (por ejemplo, al describir el ensamblaje de bibliotecas compartidas en el Capítulo 8). Se puede encontrar información completa sobre los atributos en la documentación de gcc Texinfo.

De vez en cuando, puede encontrarse mirando los encabezados de Linux. Probablemente encontrará una serie de diseños que no cumplen con ANSI / ISO. Vale la pena explorar algunos de ellos. Todas las construcciones cubiertas en este libro se detallan en la documentación de gcc.

De vez en cuando, puede encontrarse mirando los encabezados de Linux. Probablemente encontrará una serie de diseños que no cumplen con ANSI / ISO. Vale la pena explorar algunos de ellos. Todas las construcciones cubiertas en este libro se detallan en la documentación de gcc.

Ahora que ha aprendido una o dos cosas sobre el estándar C, echemos un vistazo a las opciones que ofrece el compilador gcc para asegurarnos de que cumple con el estándar C en el que escribe. Hay tres formas de garantizar que su código C cumpla con los estándares y no tenga fallas: opciones para controlar la versión del estándar que desea cumplir, definiciones para controlar los archivos de encabezado y opciones de advertencia para activar revisiones de código más estrictas. ...

Gcc tiene un gran conjunto de opciones, y aquí cubriremos solo aquellas que consideramos más importantes. Puede encontrar una lista completa de opciones en las páginas del manual en línea de gcc. También discutiremos brevemente algunas de las #define opciones que puede usar; Por lo general, deben especificarse en su código fuente antes de cualquier línea #include o especificarse en la línea de comando gcc. Es posible que se sorprenda de la abundancia de opciones para elegir el estándar aplicable en lugar de una simple bandera que lo obliga a usar el estándar moderno. La razón es que muchos programas antiguos dependen del comportamiento histórico del compilador y requerirían un trabajo significativo para actualizarlos a los estándares más recientes. En raras ocasiones, si alguna vez, desea actualizar el compilador para que comience a interrumpir el código en ejecución. A medida que cambian los estándares, es importante poder trabajar contra un estándar particular, incluso si no es la versión más reciente del estándar.

Incluso si está escribiendo un pequeño programa para uso personal, cuando el cumplimiento de las normas puede no ser tan importante, a menudo tiene sentido incluir advertencias gcc adicionales para obligar al compilador a buscar errores en su código antes de que se ejecute el programa. Esto siempre es más eficiente que recorrer el código en el depurador y preguntarse dónde podría estar el problema. El compilador tiene muchas opciones que van más allá de la simple verificación de estándares, como la capacidad de detectar código que se ajusta al estándar pero que puede tener una semántica cuestionable. Por ejemplo, un programa puede tener una orden de ejecución que le permite acceder a una variable antes de que se inicialice.

Si necesita escribir un programa para uso compartido, dado el grado de conformidad con el estándar y los tipos de advertencias del compilador que considera suficientes, es muy importante esforzarse un poco más y compilar su código sin ninguna advertencia. Si permite algunas advertencias y se acostumbra a ignorarlas, un día puede haber una advertencia más seria de que se arriesga a perderse. Si su código siempre se compila sin mensajes de advertencia, una nueva advertencia inevitablemente captará su atención. Compilar código sin previo aviso es un buen hábito para tener en cuenta.

Opciones de compilación para el seguimiento de estándares

Ansi es la opción de estándares más importante y obliga al compilador a actuar de acuerdo con el estándar de lenguaje ISO C90. Deshabilita algunas extensiones de gcc que no cumplen con los estándares, deshabilita los comentarios de C ++ (//) en los programas de C y habilita el procesamiento de trigrafos ANSI (secuencias de tres caracteres). Además, contiene la macro __ STRICT_ANSI__, que deshabilita algunas extensiones en los archivos de encabezado que no son compatibles con el estándar. En versiones posteriores del compilador, el estándar adoptado puede cambiar.

Std \u003d - Esta opción proporciona un control más preciso sobre el estándar utilizado al proporcionar un parámetro que especifica con precisión el estándar requerido. Las siguientes son las principales opciones:

C89: admite el estándar C89;

Iso9899: 1999: admite la última versión del estándar ISO, C90;

Gnu89: admite el estándar C89, pero permite algunas extensiones GNU y algunas funciones C99. Este es el valor predeterminado en la versión 4.2 de gcc.

Opciones para rastrear el estándar en directivas definidas

Hay constantes (#defines) que pueden especificarse mediante opciones en la línea de comando o como definiciones en el código fuente del programa. Tendemos a suponer que están utilizando la línea de comando del compilador.

STRICT_ANSI__: te obliga a aplicar el estándar C ISO. Se determina cuando la opción -ansi se especifica en la línea de comandos del compilador.

POSIX_C_SOURCE \u003d 2 - Habilita la funcionalidad definida por IEEE Std 1003.1 y 1003.2. Volveremos a estos estándares un poco más adelante en este capítulo.

BSD_SOURCE - Habilita la funcionalidad de los sistemas BSD. Si entran en conflicto con las definiciones POSIX, las definiciones BSD tienen prioridad.

GNU_SOURCE: permite una amplia gama de propiedades y funciones, incluidas las extensiones GNU. Si estas definiciones entran en conflicto con las definiciones POSIX, esta última tiene mayor prioridad.

Opciones del compilador para advertencias

Estas opciones se pasan al compilador desde la línea de comandos. Nuevamente, solo enumeraremos los principales, puede encontrar una lista completa en el manual de referencia en línea de gcc.

Pedantic es la opción más poderosa para verificar la pureza del código C. Además de habilitar la opción para verificar el cumplimiento del estándar C, deshabilita algunas de las construcciones tradicionales de C que están prohibidas por el estándar y hace que todas las extensiones GNU del estándar no sean válidas. Esta opción debe usarse para maximizar la portabilidad de su código C. La desventaja es que el compilador está muy preocupado por la limpieza de su código de programa, y \u200b\u200ba veces tiene que estrujarse el cerebro para lidiar con algunas advertencias restantes.

Wformat: comprueba la corrección de los tipos de argumentos de las funciones de la familia printf.

Wparentheses: busca paréntesis, incluso cuando no son necesarios. Esta opción es muy útil para verificar que las estructuras complejas se inicializan según lo previsto.

Wswitch-default: comprueba la presencia de una opción predeterminada en las instrucciones de cambio, que generalmente se considera un buen estilo de codificación.

Wunused: comprueba una variedad de casos, por ejemplo, funciones estáticas declaradas pero no descritas, parámetros no utilizados, resultados descartados.

Muro: incluye la mayoría de los tipos de advertencias de gcc, incluidas todas las opciones anteriores de W (no cubiertas solo por pedante). Con su ayuda, es fácil lograr la pureza del código del programa.

Nota

Hay muchas opciones de advertencia más avanzadas disponibles, consulte las páginas web de gcc para más detalles. Generalmente recomendamos usar -Wall; Es un buen compromiso entre la verificación, que proporciona un código de programa de alta calidad, y la necesidad de que el compilador genere muchas advertencias triviales que se vuelven difíciles de anular.

GCC se incluye con cualquier distribución Linux y generalmente se instala por defecto. La interfaz GCC es una interfaz de compilador UNIX estándar, que data de finales de los años 60, principios de los 70 del siglo pasado: la interfaz de línea de comandos. No se alarme, en el pasado, el mecanismo de interacción del usuario se ha perfeccionado a la posible perfección en este caso, y trabaje con GCC (si hay varias utilidades adicionales y un servicio razonable). editor de texto) es más fácil que cualquiera de los IDEs visuales modernos. Los autores del kit intentaron automatizar el proceso de compilación y montaje de aplicaciones tanto como sea posible. El usuario abre el programa de control. gcc, interpreta los argumentos de línea de comando pasados \u200b\u200b(opciones y nombres de archivo) y para cada archivo de entrada, de acuerdo con el lenguaje de programación utilizado, inicia su compilador, luego, si es necesario, gcc llama automáticamente ensamblador y enlazador (enlazador).

Curiosamente, los compiladores son una de las pocas aplicaciones UNIX que se preocupan por las extensiones de archivo. Por extensión, GCC determina qué tipo de archivo se encuentra frente a él y qué debe (puede) hacerse con él. Archivos fuente de idioma C debe tener una extensión .c, en el idioma C ++opcionalmente .cpp, archivos de encabezado en idioma C .h, .o archivos de objetos, etc. Si usa la extensión incorrecta, gcc no funcionará correctamente (si está de acuerdo, haga algo).

Pasemos a practicar. Vamos a escribir, compilar y ejecutar algún programa simple. No seremos originales, como el archivo fuente de un programa de ejemplo en el idioma C Creemos un archivo con el siguiente contenido:

/ * Hola C * /

#incluir

Principal ( vacío )
{

Printf ("Hola Mundo \\ n");

regreso 0 ;

Ahora, en el directorio c hello.c, emita el comando:

$ gcc hello.c

Después de algunas fracciones de segundo, el archivo a.out aparecerá en el directorio:

$ ls
a.out hello.c

Este es el archivo ejecutable terminado de nuestro programa. Defecto gcc le da al ejecutable de salida el nombre a.out (alguna vez este nombre significaba salida del ensamblador).

$ archivo a.out
a.out: ELF ejecutable LSB de 64 bits, x86-64, versión 1 (SYSV), vinculado dinámicamente (usa libs compartidas), para GNU / Linux 2.6.15, no despojado

Vamos a ejecutar el resultado software:

$ ./a.out
Hola Mundo


¿Por qué es necesario especificar explícitamente la ruta al archivo en el comando para ejecutar un archivo desde el directorio actual? Si la ruta al archivo ejecutable no se especifica explícitamente, el shell, al interpretar los comandos, busca el archivo en los directorios enumerados en la variable de sistema PATH.

$ echo $ PATH
/ usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin: / usr / games

Los directorios en la lista están separados por dos caracteres. Al buscar archivos, el shell mira los directorios en el orden en que se enumeran. Por defecto, por razones de seguridad, el directorio actual. no está incluido en la lista, por lo que el shell no buscará archivos ejecutables en él.

¿Por qué no se recomienda hacer? en el camino? Se cree que en un sistema multiusuario real siempre habrá alguna persona mala que colocará en un directorio público un programa malicioso con el nombre de un archivo ejecutable que coincide con el nombre de un comando a menudo llamado por un administrador local con derechos de superusuario ... La conspiración tendrá éxito si. está al comienzo de la lista del directorio.


Utilidad expediente muestra información sobre el tipo (desde el punto de vista del sistema) transferido en la línea de comando del archivo, para algunos tipos de archivos muestra información adicional sobre el contenido del archivo.

$ archivo hello.c
hello.c: texto del programa ASCII C
$ file annotation.doc
annotation.doc: Documento CDF V2, Little Endian, Os: Windows, Versión 5.1, Página de códigos: 1251, Autor: MIH, Plantilla: Normal.dot, Último guardado por: MIH, Número de revisión: 83, Nombre de la aplicación de creación: Microsoft Word de Office, Tiempo total de edición: 09:37:00, Última impresión: jue 22 de enero 07:31:00 2009, Crear hora / fecha: Lun 12 de enero 07:36:00 2009, Última hora / fecha guardada: jue 22 de enero 07:34:00 2009, Número de páginas: 1, Número de palabras: 3094, Número de caracteres: 17637, Seguridad: 0

En realidad, eso es todo lo que se requiere del usuario para una aplicación exitosa. gcc :)

El nombre del archivo ejecutable de salida (así como cualquier otro archivo generado gcc) se puede cambiar con opciones -o:

$ gcc -o hola hola.c
$ ls
hola hola.c
$ ./hola
Hola Mundo


En nuestro ejemplo, la función main () devuelve un valor aparentemente innecesario de 0. En sistemas similares a UNIX, es habitual devolver un número entero al shell al finalizar un programa: cero si tiene éxito y cualquier otro. El intérprete de shell asignará automáticamente el valor resultante a la variable de entorno llamada? ... Puede ver su contenido usando echo $? :

$ ./hola
Hola Mundo
$ echo $?
0

Se dijo anteriormente que gcc Es un programa de control diseñado para automatizar el proceso de compilación. Veamos qué sucede realmente como resultado de la ejecución del comando gcc hello.c.

El proceso de compilación se puede dividir en 4 etapas principales: preprocesamiento, compilación en sí, ensamblaje, vinculación (vinculación).

Opciones gcc le permite interrumpir el proceso en cualquiera de estas etapas.

El preprocesador prepara el archivo fuente para la compilación: corta los comentarios, agrega el contenido de los archivos de encabezado (directiva del preprocesador #include), implementa la expansión de macros (constantes simbólicas, directiva del preprocesador #define).

Aprovechando opción -E próximos pasos gcc puede interrumpir y ver el contenido del archivo procesado por el preprocesador.

$ gcc -E -o hello.i hello.c
$ ls
hello.c hello.i
$ menos hola.i
. . .
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
. . .
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
. . .
extern int printf (__const char * __ restrict __format, ...);
. . .
# 4 "hola.c" 2
principal (nulo)
{
printf ("Hola Mundo \\ n");
devuelve 0;
}

Después de ser procesado por el preprocesador, el código fuente de nuestro programa se hinchó y adquirió una forma ilegible. El código que una vez escribimos con nuestras propias manos se redujo a unas pocas líneas al final del archivo. Motivo: incluido el archivo de encabezado de la biblioteca estándar C... El archivo de encabezado stdio.h contiene muchas cosas diferentes y también requiere la inclusión de otros archivos de encabezado.

Tenga en cuenta la extensión del archivo hello.i. Por acuerdos gcc la extensión .i corresponde a los archivos fuente del idioma C no requiere procesamiento de preprocesador. Dichos archivos se compilan sin pasar por el preprocesador:

$ gcc -o hola hola.i
$ ls
hola hola.c hola.i
$ ./hola
Hola Mundo

Después del preprocesamiento, es el turno de la compilación. El compilador convierte el código fuente de un programa de alto nivel en código de lenguaje ensamblador.

El significado de la palabra compilación es borroso. Los Wikipedia, por ejemplo, creen, refiriéndose a estándares internacionalesesa compilación es "la conversión por parte de un compilador del código fuente de un programa escrito en un lenguaje de programación de alto nivel a un lenguaje cercano al lenguaje de máquina o al código objeto". En principio, esta definición nos conviene, el lenguaje ensamblador está realmente más cerca del lenguaje máquina que C. Pero en la vida cotidiana, la compilación a menudo significa simplemente cualquier operación que convierta el código fuente de un programa en cualquier lenguaje de programación en código ejecutable. Es decir, un proceso que incluye los cuatro pasos anteriores también se puede llamar compilación. Una ambigüedad similar está presente en el presente texto. Por otro lado, la operación de convertir el código fuente de un programa en código de lenguaje ensamblador también puede describirse mediante la traducción de la palabra: "convertir un programa presentado en uno de los lenguajes de programación en un programa en otro idioma y, en cierto sentido, equivalente al primero".

Para detener el proceso de creación de un archivo ejecutable una vez completada la compilación, puede opción -S:

$ gcc -S hola.c
$ ls
hello.c hello.s
$ file hello.s
hello.s: texto del programa ensamblador ASCII
$ menos hola.s
.archivo "hola.c"
.sección .rodata
.LC0:
.string "Hola mundo"
.texto
.globl principal
.type main, @function
principal:
pushl% ebp
movl% esp,% ebp
andl $ -16,% esp
subl $ 16,% esp
movl $ .LC0, (% esp)
call pone
movl $ 0,% eax
salir
retirado
.size main, .- main


El archivo hello.s apareció en el directorio, que contiene la implementación del programa en lenguaje ensamblador. Nota estableciendo el nombre del archivo de salida con opciones -o en este caso no fue requerido, gcc se generó automáticamente al reemplazar la extensión .c con .s en el nombre del archivo fuente. Para la mayoría de las operaciones básicas. gcc El nombre del archivo de salida está formado por un reemplazo similar. La extensión .s es estándar para los archivos fuente en lenguaje ensamblador.

Por supuesto, también puede obtener el código ejecutable del archivo hello.s:

$ gcc -o hola hola.s
$ ls
hola hola.c hola.s
$ ./hola
Hola Mundo

La siguiente etapa de la operación de ensamblaje es la traducción del código del lenguaje ensamblador al código de máquina. El resultado de la operación es un archivo objeto. El archivo objeto contiene bloques de código de máquina listos para su ejecución, bloques de datos, así como una lista de funciones y variables externas definidas en el archivo ( tabla de símbolos ), pero al mismo tiempo, no especifica direcciones absolutas de referencias a funciones y datos. El archivo de objeto no se puede iniciar para su ejecución directa, pero más tarde (en la etapa de vinculación) se puede combinar con otros archivos de objeto (en este caso, de acuerdo con las tablas de símbolos, se calcularán y rellenarán las direcciones existentes entre los archivos de referencias cruzadas). Opción gcc -c, detiene el proceso al final de la fase de ensamblaje:

$ gcc -c hello.c
$ ls
hello.c hello.o
$ archivo hello.o
hola.o: ELF 64-bit LSB relocatable, x86-64, versión 1 (SYSV), no despojado

La extensión estándar para archivos de objetos es .o.

Si pasa el archivo de objeto hello.o recibido al enlazador, este último calculará las direcciones de enlace, agregará el código de inicio y finalización del programa, el código de llamada de la función de la biblioteca y, como resultado, tendremos un archivo ejecutable del programa listo para usar.

$ gcc -o hola hola.o
$ ls
hola hola.c hola.o
$ ./hola
Hola Mundo

Lo que acabamos de hacer (o más bien gcc lo hizo por nosotros) y está el contenido de la última etapa: vinculación (vinculación, vinculación).

Bueno, eso es sobre compilación y eso es todo. Ahora veamos algunas, en mi opinión, opciones importantes gcc.

Opción -I ruta / al / directorio / con / encabezados / archivos - agrega el directorio especificado a la lista de rutas de búsqueda de archivos de encabezado. Directorio agregado por opción -YO primero escaneado, luego la búsqueda continúa en los directorios del sistema estándar. Si las opciones -YO múltiples directorios especificados por ellos se escanean de izquierda a derecha a medida que aparecen las opciones.

-Opción de pared - muestra advertencias causadas por posibles errores en el código que no impiden la compilación del programa, pero que, en opinión del compilador, pueden provocar ciertos problemas durante su ejecución. Una opción importante y útil, los desarrolladores gcc Siempre recomiendo usarlo. Por ejemplo, se emitirán muchas advertencias cuando intente compilar este archivo:

1 / * comentario.c * /
2
3 estático en t k es 0;
4 estático en t l ( en t una);
5
6 principales ()
7 {
8
9 en t una;
10
11 en t antes de Cristo;
12
13 b + 1;
14
15 b \u003d c;
16
17 en t * pags;
18
19 b \u003d * p;
20
21 }


$ gcc -o comentario comentario.c
$ gcc -Wall -o comentario comment.c
comentario.c: 7: advertencia: el tipo de retorno vuelve a "int"

comentario.c: 13: advertencia: declaración sin efecto
comentario.c: 9: advertencia: variable no utilizada "a"
comentario.c: 21: advertencia: el control llega al final de la función no nula
comentario.c: en el nivel superior:
comentario.c: 3: advertencia: "k" definida pero no utilizada
comentario.c: 4: advertencia: 'l' declarado 'estático' pero nunca definido
comentario.c: En la función "principal":
comentario.c: 15: advertencia: "c" se utiliza sin inicializar en esta función
comentario.c: 19: advertencia: 'p' se usa sin inicializar en esta función

-Opción de error - Convierte todas las advertencias en errores. Anula el proceso de compilación si se produce una advertencia. Utilizado junto con -Opción de pared.

$ gcc -Werror -o comentario comentario.c
$ gcc -Werror -Wall -o comentario comentario.c
cc1: las advertencias se tratan como errores
comentario.c: 7: error: el tipo de retorno vuelve a "int"
comentario.c: En la función "principal":
comentario.c: 13: error: declaración sin efecto
comentario.c: 9: error: variable no utilizada "a"

La opción -g - coloca la información necesaria para que el depurador funcione en un objeto o archivo ejecutable gdb. Al crear un proyecto para la depuración posterior, opción -g debe incluirse tanto en tiempo de compilación como en tiempo de enlace.

Opciones -O1, -O2, -O3 - Establecer el nivel de optimización del código generado por el compilador. Con números crecientes, aumenta el grado de optimización. La acción de las opciones se puede ver en este ejemplo.

Archivo original:

/ * circle.c * /

Principal ( vacío )
{

en t yo;

para (i \u003d 0; i< 10 ; ++i)
;

regreso yo;

Compilación con nivel de optimización predeterminado:

$ gcc -S circle.c
$ menos circle.s
.archivo "círculo.c"
.texto
.globl principal
.type main, @function
principal:
pushl% ebp
movl% esp,% ebp
subl $ 16,% esp
movl $ 0, -4 (% ebp)
jmp .L2
.L3:
addl $ 1, -4 (% pb)
.L2:
cmpl $ 9, -4 (% pb)
jle .L3
movl -4 (% ebp),% eax
salir
retirado
.size main, .- main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack, "", @ progbits

Compilación con el máximo nivel de optimización:

$ gcc -S -O3 circle.c
$ menos circle.s
.archivo "círculo.c"
.texto
.p2align 4.15
.globl principal
.type main, @function
principal:
pushl% ebp
movl $ 10,% eax
movl% esp,% ebp
popl% ebp
retirado
.size main, .- main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack, "", @ progbits

En el segundo caso, el código resultante ni siquiera insinúa ningún ciclo. De hecho, el valor de i se puede calcular en la etapa de compilación, que se realizó.

Por desgracia, para proyectos reales, la diferencia en el rendimiento en varios niveles de optimización prácticamente no se nota ...

Opción -O0 - cancela cualquier optimización de código. La opción es necesaria en la etapa de depuración de la aplicación. Como se muestra arriba, la optimización puede conducir a un cambio en la estructura del programa más allá del reconocimiento, la conexión entre el ejecutable y el código fuente no será explícita y, en consecuencia, la depuración paso a paso del programa no será posible. Cuando está habilitado -gramo, se recomienda incluir y -O0.

Opción -Os - establece la optimización no por la eficiencia del código, sino por el tamaño del archivo resultante. En este caso, el rendimiento del programa debe ser comparable al rendimiento del código obtenido durante la compilación con el nivel de optimización predeterminado.

Opción -march \u003d arquitectura - establece la arquitectura del procesador de destino. La lista de arquitecturas compatibles es extensa, por ejemplo, para la familia de procesadores. Intel / AMD puede establecer i386, pentium, prescott, opteron-sse3 etc. Los usuarios de distribuciones binarias deben tener en cuenta que para que los programas funcionen correctamente con la opción especificada, es conveniente que todas las bibliotecas incluidas se compilen con la misma opción.

Las opciones pasadas al vinculador se discutirán a continuación.

Pequeña adición:

Se dijo anteriormente que gcc determina el tipo (lenguaje de programación) de los archivos transferidos por su extensión y, de acuerdo con el tipo adivinado (lenguaje), realiza acciones sobre ellos. El usuario está obligado a monitorear las extensiones de los archivos creados, eligiéndolos según lo requiera el acuerdo gcc. En realidad gcc Puede copiar archivos con nombres arbitrarios. Opción Gcc -x le permite especificar explícitamente el lenguaje de programación de los archivos compilados. Esta opción se aplica a todos los archivos posteriores enumerados en el comando (hasta que aparezca la siguiente opción -X) Posibles argumentos de opción:

c c-header c-cpp-output

c ++ c ++ - encabezado c ++ - cpp-output

objetiva-c objetiva-c-cabecera objetiva-c-cpp-salida

objective-C ++ Objective-C ++ - encabezado Objective-C ++ - salida cpp

ensamblador ensamblador-con-cpp

ada

f77 f77-cpp-input

f95 f95-cpp-input

java

El propósito de los argumentos debe ser claro a partir de su escritura (aquí cpp no tiene nada que ver con C ++, este es el archivo de código fuente preprocesado por el preprocesador). Vamos a revisar:

$ mv hello.c hello.txt
$ gcc -Wall -x c -o hello hello.txt
$ ./hola
Hola Mundo

Compilación separada

La fuerza de los idiomas. C / c ++ es la capacidad de dividir el código fuente del programa en varios archivos. Aún más se puede decir: la posibilidad de una compilación separada es la base del lenguaje, sin un uso efectivo C no concebible Es una programación de múltiples archivos que le permite implementar en C grandes proyectos como Linux (aquí debajo de la palabra Linux significa tanto el núcleo como el sistema como un todo). ¿Qué le da una compilación separada al programador?

1. Le permite hacer que el código del programa (proyecto) sea más legible. El archivo fuente de varias docenas de pantallas se vuelve casi inalcanzable. Si, de acuerdo con una cierta lógica (pre-pensada), dividiéndola en una serie de pequeños fragmentos (cada uno en un archivo separado), será mucho más fácil hacer frente a la complejidad del proyecto.

2. Le permite reducir el tiempo de recompilación del proyecto. Si los cambios se realizan en un archivo, no tiene sentido recompilar todo el proyecto, es suficiente recompilar solo este archivo modificado.

3. Le permite distribuir el trabajo en un proyecto entre varios desarrolladores. Cada programador crea y depura su parte del proyecto, pero en cualquier momento será posible recopilar (reconstruir) todos los desarrollos resultantes en el producto final.

4. Sin una compilación separada, las bibliotecas no existirían. A través de bibliotecas, reutilización y distribución de código para C / c ++, y el código es binario, lo que permite, por un lado, proporcionar a los desarrolladores un mecanismo simple para incluirlo en sus programas, por otro lado, para ocultarles detalles de implementación específicos. Al trabajar en un proyecto, ¿siempre vale la pena pensar y no necesitar algo de lo que ya se ha hecho en el futuro? ¿Quizás debería seleccionar y organizar una parte del código como biblioteca por adelantado? En mi opinión, este enfoque simplifica enormemente la vida y ahorra mucho tiempo.

Gcc, por supuesto, admite una compilación separada y no requiere ninguna instrucción especial del usuario. En general, todo es muy simple.

Aquí hay un ejemplo práctico (aunque muy, muy condicional).

Conjunto de archivos de código fuente:

/ * C Principal * /

#incluir

#incluye "first.h"
#incluye "second.h"

en t principal ( vacío )
{

Primero ();
segundo ();

Printf ("Función principal ... \\ n");

regreso 0 ;


/ * primero.h * /

vacío primero ( vacío );


/ * first.c * /

#incluir

#incluye "first.h"

vacío primero ( vacío )
{

Printf ("Primera función ... \\ n");


/ * segundo.h * /

vacío segundo ( vacío );


/ * second.c * /

#incluir

#incluye "second.h"

vacío segundo ( vacío )
{

Printf ("Segunda función ... \\ n");

En general, tenemos esto:

$ ls
first.c first.h main.c second.c second.h

Toda esta economía se puede compilar en un solo comando:

$ gcc -Wall -o main main.c first.c second.c
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Solo esto nos dará prácticamente ningún bono, bueno, con la excepción de un código más estructurado y legible, distribuido en varios archivos. Todas las ventajas enumeradas anteriormente aparecerán en el caso de este enfoque de compilación:

$ gcc -wall -c main.c
$ gcc -Wall -c first.c
$ gcc -Wall -c second.c
$ ls
first.c first.h first.o main.c main.o second.c second.h second.o
$ gcc -o main main.o first.o second.o
$ ./main
Primera función ...
Segunda función ...
Función principal ...

¿Qué hemos hecho? De cada archivo fuente (compilando con la opción -C) recibió el archivo objeto. Los archivos de objetos se vincularon al ejecutable final. Por supuesto equipos gcc se hizo más, pero nadie recolecta proyectos manualmente, para esto hay constructores de servicios públicos (los más populares hacer) Todos los beneficios de la compilación separada enumerados anteriormente se manifestarán mediante el uso de las utilidades de recopilación.

Surge la pregunta: ¿cómo se las arregla el enlazador para recopilar archivos de objetos juntos, calculando correctamente el direccionamiento de las llamadas? ¿Cómo sabe siquiera que el archivo second.o contiene el código de la segunda función () y que el código del archivo main.o contiene su llamada? Resulta que todo es simple: el archivo objeto contiene el llamado tabla de símbolos , incluidos los nombres de algunas posiciones de código (funciones y variables externas). El vinculador mira la tabla de símbolos de cada archivo de objeto, busca posiciones comunes (con nombres coincidentes), sobre la base de las cuales saca conclusiones sobre la ubicación real del código de las funciones utilizadas (o bloques de datos) y, en consecuencia, recalcula las direcciones de llamadas en el archivo ejecutable.

Puede ver la tabla de símbolos utilizando la utilidad nuevo Méjico.

$ nm main.o
tu primero
00000000 T principal
U pone
U segundo
$ nm first.o
00000000 T primero
U pone
$ nm segundo.o
U pone
00000000 T segundo

El aspecto de la llamada put se explica por el uso de la función de biblioteca estándar printf (), que se convirtió en put () en la etapa de compilación.

La tabla de símbolos se escribe no solo en el archivo objeto, sino también en el archivo ejecutable:

$ nm principal
08049f20 d _ DINÁMICO
08049ff4 d _GLOBAL_OFFSET_TABLE_
080484fc R _IO_stdin_used
w _Jv_RegisterClasses
08049f10 d __CTOR_END__
08049f0c d __CTOR_LIST__
08049f18 D __DTOR_END__
08049f14 d __DTOR_LIST__
08048538 r __FRAME_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
0804a014 A __bss_start
0804a00c D __data_start
080484b0 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
080484aa T __i686.get_pc_thunk.bx
08049f0c d __init_array_end
08049f0c d __init_array_start
08048440 T __libc_csu_fini
08048450 T __libc_csu_init
U [correo electrónico protegido]@ GLIBC_2.0
0804a014 A _edatos
0804a01c A _end
080484dc T _fini
080484f8 R _fp_hw
080482b8 T _init
08048330 T _start
0804a014 b completado 7021
0804a00c W data_start
0804a018 b dtor_idx.7023
0804840c T primero
080483c0 t frame_dummy
080483e4 T principal
U [correo electrónico protegido]@ GLIBC_2.0
08048420 T segundo

Incluir una tabla de caracteres en un ejecutable es particularmente necesario para simplificar la depuración. En principio, para ejecutar la aplicación, no es realmente necesaria. Para archivos ejecutables de programas reales, con muchas definiciones de funciones y variables externas, que utilizan un montón de bibliotecas diferentes, la tabla de símbolos se vuelve bastante extensa. Para reducir el tamaño del archivo de salida, se puede eliminar usando gcc opción -s.

$ gcc -s -o main main.o first.o second.o
$ ./main
Primera función ...
Segunda función ...
Función principal ...
$ nm principal
nm: principal: sin símbolos

Cabe señalar que durante el enlace, el vinculador no realiza ninguna verificación en el contexto de la llamada a la función, no supervisa el tipo del valor de retorno, ni el tipo y el número de parámetros recibidos (y no tiene de dónde obtener dicha información). Toda validación de llamadas debe hacerse en tiempo de compilación. En el caso de la programación de múltiples archivos, esto requiere el uso del mecanismo de archivo de encabezado de idioma C.

Bibliotecas

Biblioteca - en idioma C, un archivo que contiene código objeto que se puede adjuntar al programa que usa la biblioteca en la etapa de vinculación. De hecho, una biblioteca es una colección de archivos de objetos vinculados de una manera especial.

El propósito de las bibliotecas es proporcionar al programador un mecanismo estándar para reutilizar código que sea simple y confiable.

Desde el punto de vista del sistema operativo y el software de aplicación, las bibliotecas son estático y compartido (dinámica ).

El código de la biblioteca estática se incluye en el archivo ejecutable durante el enlace de este último. La biblioteca resulta estar "cableada" en el archivo, el código de la biblioteca se "fusiona" con el resto del código del archivo. Un programa que usa bibliotecas estáticas se vuelve independiente y puede ejecutarse en casi cualquier computadora con una arquitectura y un sistema operativo adecuados.

El sistema operativo carga y conecta el código de la biblioteca compartida al código del programa, a solicitud del programa durante su ejecución. El código de la biblioteca dinámica no está incluido en el archivo ejecutable del programa; solo el enlace a la biblioteca está incluido en el archivo ejecutable. Como resultado, el programa que usa las bibliotecas compartidas deja de ser autónomo y solo se puede ejecutar con éxito en el sistema donde están instaladas las bibliotecas involucradas.

El paradigma de la biblioteca compartida proporciona tres beneficios importantes:

1. El tamaño del archivo ejecutable se reduce considerablemente. En un sistema que incluye muchos archivos binarios que usan el mismo código, no es necesario almacenar una copia de este código para cada archivo ejecutable.

2. El código de la biblioteca compartida utilizada por varias aplicaciones se almacena en la RAM en una instancia (de hecho, no es tan simple ...), como resultado, se reduce la necesidad del sistema de RAM disponible.

3. No hay necesidad de reconstruir cada archivo ejecutable en caso de cambios en el código de la biblioteca común para ellos. Los cambios y correcciones al código de la biblioteca dinámica se reflejarán automáticamente en cada uno de los programas que lo utilizan.

Sin el paradigma de la biblioteca compartida, no habría distribuciones precompiladas (binarias) Linux (sí, no importa lo que no existía). Imagine el tamaño de una distribución, cada binario de los cuales contendría el código de la biblioteca estándar C (y todas las demás bibliotecas de complementos). Imagínense lo que habría que hacer para actualizar el sistema, después de corregir una vulnerabilidad crítica en una de las bibliotecas ampliamente utilizadas ...

Ahora un poco de práctica.

Usemos el conjunto de archivos fuente del ejemplo anterior como ilustración. Pondremos el código (implementación) de las funciones first () y second () en nuestra biblioteca propia.

En Linux, se adopta el siguiente esquema de nomenclatura de archivos de la biblioteca (aunque no siempre se observa): el nombre del archivo de la biblioteca comienza con el prefijo lib, seguido del nombre de la biblioteca, al final la extensión .a ( archivo ) - para la biblioteca estática, .so ( objeto compartido ) - para compartido (dinámico), después de la expansión a través de un punto, se enumeran los dígitos del número de versión (solo para la biblioteca dinámica). El nombre correspondiente a la biblioteca de archivos de encabezado (de nuevo generalmente) consiste en el nombre de la biblioteca (sin el prefijo y la versión) y la extensión .h. Por ejemplo: libogg.a, libogg.so.0.7.0, ogg.h.

Primero, creemos y usemos una biblioteca estática.

Las funciones first () y second () compondrán el contenido de nuestra biblioteca libhello. En consecuencia, el nombre del archivo de la biblioteca será libhello.a. La biblioteca es comparable al archivo de encabezado hello.h.

/ * Hola h * /

vacío primero ( vacío );
vacío segundo ( vacío );

Por supuesto, las líneas:

#incluye "first.h"


#incluye "second.h"

en los archivos main.c, first.c y second.c es necesario reemplazar con:

#include "hola.h"

Bueno, ahora, presentamos la siguiente secuencia de comandos:

$ gcc -Wall -c first.c
$ gcc -Wall -c second.c
$ ar crs libhello.a first.o second.o
$ archivo libhello.a
libhello.a: archivo ar actual

Como ya se mencionó, una biblioteca es una colección de archivos de objetos. Los primeros dos comandos creamos estos archivos de objetos.

A continuación, debe vincular los archivos de objetos en un conjunto. Para esto, se usa el archivador arkansas - la utilidad "une" varios archivos en uno, el archivo contiene la información requerida para restaurar (extraer) cada archivo individual (incluidos sus atributos de propiedad, acceso, tiempo). No se realiza ninguna "compresión" del contenido del archivo u otra transformación de los datos almacenados.

Opción c arname - cree un archivo, si el archivo con el nombre arname no existe, se creará, de lo contrario los archivos se agregarán al archivo existente.

Opcion r - establece el modo de actualización de archivo, si un archivo con el nombre especificado ya existe en el archivo, se eliminará y el nuevo archivo se agregará al final del archivo.

Opción S - agrega (actualiza) el índice de archivo. En este caso, el índice de archivo es una tabla en la que para cada nombre simbólico (función o nombre del bloque de datos) definido en los archivos archivados se asocia el nombre del archivo objeto correspondiente. El índice de archivo es necesario para acelerar el trabajo con la biblioteca: para encontrar la definición necesaria, no es necesario mirar las tablas de símbolos de todos los archivos, puede ir inmediatamente al archivo que contiene el nombre que está buscando. Puede ver el índice del archivo utilizando la utilidad ya familiar nuevo Méjico usándolo la opción -s (también se mostrarán las tablas de símbolos de todos los archivos de objetos del archivo):

$ nm -s libhello.a
Índice de archivo:
primero en primero
segundo en segundo. o

primero o:
00000000 T primero
U pone

segundo.o:
U pone
00000000 T segundo

Hay una utilidad especial para crear un índice de archivo ranlib. La biblioteca libhello.a podría haberse creado así:

$ ar cr libhello.a primero.o segundo.o
$ ranlib libhello.a

Sin embargo, la biblioteca funcionará bien sin un índice de archivo.

Ahora usemos nuestra biblioteca:

$ gcc -wall -c main.c
$
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Trabajos...

Bueno ahora los comentarios ... Hay dos nuevas opciones gcc:

La opción -l nombre - pasado al vinculador, indica la necesidad de conectar la biblioteca libname al archivo ejecutable. Conectar significa indicar que tales y tales funciones (variables externas) están definidas en tal y tal biblioteca. En nuestro ejemplo, la biblioteca es estática, todos los nombres simbólicos se referirán al código ubicado directamente en el archivo ejecutable. Presta atención a las opciones. -l el nombre de la biblioteca se especifica como nombre sin el prefijo lib.

Opción -L / ruta / al / directorio / con / bibliotecas - pasado al vinculador, indica la ruta al directorio que contiene las bibliotecas conectadas. En nuestro caso, se establece un punto . , el enlazador primero buscará bibliotecas en el directorio actual, luego en los directorios definidos en el sistema.

Un pequeño comentario debe hacerse aquí. El hecho es que para varias opciones gcc El orden en que aparecen en la línea de comando es importante. Entonces, el enlazador busca el código correspondiente a los nombres indicados en la tabla de símbolos del archivo en las bibliotecas que figuran en la línea de comando después El nombre de este archivo. El vinculador ignora el contenido de las bibliotecas enumeradas antes del nombre del archivo:

$ gcc -wall -c main.c
$ gcc -o main -L. -hello main.o
main.o: En la función `main":
main.c :(. text + 0xa): referencia indefinida a `first '
main.c :(. text + 0xf): referencia indefinida a `second '

$ gcc -o main main.o -L. -hello
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Este comportamiento gcc debido al deseo de los desarrolladores de proporcionar al usuario la capacidad de combinar archivos con bibliotecas de diferentes maneras, para usar nombres que se crucen ... En mi opinión, si es posible, es mejor no molestarse con esto. En general, las bibliotecas de complementos se deben enumerar después del nombre del archivo que hace referencia a ellas.

Hay una forma alternativa de especificar la ubicación de las bibliotecas en el sistema. Dependiendo de la distribución, la variable de entorno LD_LIBRARY_PATH o LIBRARY_PATH puede almacenar una lista de directorios separados por dos puntos en los que el enlazador debe buscar bibliotecas. Como regla general, esta variable no está definida en absoluto, pero nada impide su creación:

$ echo $ LD_LIBRARY_PATH

/usr/lib/gcc/i686-pc-linux-gnu/4.4.3/../../../../i686-pc-linux-gnu/bin/ld: no puede encontrar -lhello
la ejecución de collect2: ld salió con el código de retorno 1
$ export LIBRARY_PATH \u003d.
$ gcc -o main main.o -lhello
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Las manipulaciones con variables de entorno son útiles al crear y depurar sus propias bibliotecas, así como si necesita conectar alguna biblioteca compartida no estándar (obsoleta, actualizada, modificada, generalmente diferente de la incluida en la distribución) a la aplicación.

Ahora cree y use la biblioteca dinámica.

El conjunto de archivos de origen permanece sin cambios. Ingresamos los comandos, vemos lo que sucedió, leemos los comentarios:

$ gcc -Wall -fPIC -c first.c
$ gcc -Wall -fPIC -c second.c
$ gcc -shared -o libhello.so.2.4.0.5 -Wl, -soname, libhello.so.2 first.o second.o

¿Qué obtuviste como resultado?

$ file libhello.so.2.4.0.5
libhello.so.2.4.0.5: Objeto compartido LSB ELF de 64 bits, x86-64, versión 1 (SYSV), vinculado dinámicamente, no eliminado

El archivo libhello.so.2.4.0.5 es nuestra biblioteca compartida. Hablaremos sobre cómo usarlo a continuación.

Ahora comenta:

-Opción opcional - requiere que el compilador, al crear archivos de objetos, genere posicionar código independiente (PIC - Código independiente de posición ), su principal diferencia es la forma en que se presentan las direcciones. En lugar de especificar posiciones fijas (estáticas), todas las direcciones se calculan en función de las compensaciones especificadas en tabla de compensación global (tabla de compensación global - GOT ) El formato del código independiente de la posición permite conectar módulos ejecutables al código del programa principal en el momento de su carga. En consecuencia, el propósito principal del código independiente de la posición es crear bibliotecas dinámicas (compartidas).

La opción compartida - indica gccque el resultado no debe ser un archivo ejecutable, sino un objeto compartido: una biblioteca dinámica.

Opción -Wl, -soname, libhello.so.2 - conjuntos soname bibliotecas Hablaremos de soname en detalle en el siguiente párrafo. Ahora analicemos el formato de la opción. Esta vista es un diseño extraño, a primera vista, con comas diseñadas para la interacción directa del usuario con el enlazador. Durante la compilación gcc llama al enlazador automáticamente, automáticamente, a su exclusivo criterio, gcc le da las opciones necesarias para completar con éxito la tarea. Si el usuario necesita intervenir en el proceso de vinculación él mismo, puede usar una opción especial gcc -Wl, -opción, valor1, valor2 .... ¿Qué significa transferir al enlazador ( -Wl) opción -opción con argumentos valor1, valor2 etc. En nuestro caso, la opción se pasó al enlazador -soname con argumento libhello.so.2.

Ahora sobre soname. Al crear y distribuir bibliotecas, existe un problema de compatibilidad y control de versiones. Para que el sistema, específicamente el cargador dinámico de bibliotecas, tenga una idea de qué versión de la biblioteca se utilizó al compilar la aplicación y, en consecuencia, es necesaria para su funcionamiento exitoso, se proporcionó un identificador especial: soname , colocado tanto en el archivo de la biblioteca como en el archivo ejecutable de la aplicación. El identificador de soname es una cadena que incluye el nombre de la biblioteca con el prefijo lib, dot, extension so, nuevamente dot y uno o dos (separados por puntos) dígitos de la versión de la biblioteca - lib name .so. X. y. Es decir, el soname coincide con el nombre del archivo de la biblioteca hasta el primer o segundo dígito del número de versión. Deje que el nombre del archivo ejecutable de nuestra biblioteca sea libhello.so.2.4.0.5, luego el nombre de la biblioteca puede ser libhello.so.2. ¡Al cambiar la interfaz de la biblioteca, se debe cambiar su nombre! Cualquier modificación del código que resulte en incompatibilidad con versiones anteriores debe ir acompañada de un nuevo soname.

¿Cómo funciona todo? Suponga que para la ejecución exitosa de alguna aplicación, se necesita una biblioteca llamada hello, suponga que hay una en el sistema, el nombre del archivo de la biblioteca es libhello.so.2.4.0.5 y el nombre de la biblioteca libhello.so.2 escrito en él. En la etapa de compilación de la aplicación, el enlazador, de acuerdo con la opción -l hola, buscará en el sistema un archivo llamado libhello.so. En un sistema real, libhello.so es un enlace simbólico a libhello.so.2.4.0.5. Una vez que haya obtenido acceso al archivo de la biblioteca, el vinculador leerá el valor de nombre escrito en él y, entre otras cosas, lo colocará en el archivo ejecutable de la aplicación. Cuando se inicia la aplicación, el cargador de biblioteca dinámica recibirá una solicitud para conectar la biblioteca con soname leída del archivo ejecutable e intentará encontrar una biblioteca en el sistema cuyo nombre de archivo coincida con soname. Es decir, el cargador intentará encontrar el archivo libhello.so.2. Si el sistema está configurado correctamente, debe contener un enlace simbólico libhello.so.2 al archivo libhello.so.2.4.0.5, el cargador accederá a la biblioteca requerida y luego sin dudarlo (y sin verificar nada más) lo conectará a la aplicación. Ahora imagine que transferimos la aplicación compilada de esta manera a otro sistema, donde solo se implementa la versión anterior de la biblioteca con soname libhello.so.1. Intentar ejecutar el programa generará un error porque no hay ningún archivo llamado libhello.so.2 en este sistema.

Por lo tanto, en la etapa de compilación, el enlazador debe proporcionar un archivo de biblioteca (o un enlace simbólico a un archivo de biblioteca) llamado lib name .so, en tiempo de ejecución, el cargador necesita un archivo (o un enlace simbólico) llamado lib name .so. X. y. ¿Qué significa el nombre lib name .so. X. y debe coincidir con la cadena de soname de la biblioteca utilizada.

En distribuciones binarias, por regla general, el archivo de biblioteca libhello.so.2.4.0.5 y el enlace a él libhello.so.2 se colocarán en el paquete libhello, y el enlace libhello.so, necesario solo para la compilación, junto con el archivo de encabezado de la biblioteca hello.h será empaquetado en el paquete libhello-devel (el paquete devel también contendrá el archivo de la versión estática de la biblioteca libhello.a, la biblioteca estática se puede usar, también solo en la etapa de compilación). Al desempacar el paquete, todos los archivos y enlaces listados (excepto hello.h) estarán en un directorio.

Asegurémonos de que la línea de soname dada esté realmente escrita en nuestro archivo de biblioteca. Utilizaremos la mega utilidad objdump con opción -pags :

$ objdump -p libhello.so.2.4.0.5 | grep SONAME
SONAME libhello.so.2


Utilidad objdump es una herramienta poderosa que le permite obtener información completa sobre el contenido interno (y el dispositivo) de un objeto o archivo ejecutable. La página de utilidad dice que objdump En primer lugar, será útil para los programadores que crean herramientas de depuración y compilación, y no solo escriben cualquier programa de aplicación :) En particular, con la opción -re Es un desensamblador. Utilizamos la opción -pags - Muestra diversas metainformaciones sobre el archivo objeto.

En este ejemplo de creación de una biblioteca, seguimos implacablemente los principios de la compilación separada. Por supuesto, la biblioteca podría compilarse así, con una llamada gcc:

$ gcc -shared -Wall -fPIC -o libhello.so.2.4.0.5 -Wl, -soname, libhello.so.2 first.c second.c

Ahora intente usar la biblioteca resultante:

$ gcc -wall -c main.c
$
/ usr / bin / ld: no se puede encontrar -lhello
collect2: ld devolvió 1 estado de salida

El enlazador jura. Recordamos lo que se dijo anteriormente sobre los enlaces simbólicos. Cree libhello.so e intente nuevamente:

$ ln -s libhello.so.2.4.0.5 libhello.so
$ gcc -o main main.o -L. -hello -Wl, -rpath,.

Ahora todos están felices. Ejecute el binario creado:

Error ... El cargador está maldiciendo, no puede encontrar la biblioteca libhello.so.2. Asegúrese de que el enlace a libhello.so.2 esté realmente escrito en el archivo ejecutable:

$ objdump -p principal | grep NECESARIO
NECESITO libhello.so.2
NECESITO libc.so.6

$ ln -s libhello.so.2.4.0.5 libhello.so.2
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Funcionó ... Ahora comenta sobre nuevas opciones gcc.

Opción -Wl, -rpath,. - construcción ya familiar, pasa la opción al enlazador -rpath con argumento . . Mediante -rpath en el archivo ejecutable del programa, puede escribir rutas adicionales a lo largo de las cuales el cargador de la biblioteca compartida buscará los archivos de la biblioteca. En nuestro caso, la ruta está registrada . - la búsqueda de archivos de la biblioteca comenzará desde el directorio actual.

$ objdump -p principal | grep RPATH
RUTA

Gracias a esta opción, no es necesario cambiar las variables de entorno al iniciar el programa. Está claro que si transfiere el programa a otro directorio e intenta ejecutarlo, no se encontrará el archivo de la biblioteca y el cargador mostrará un mensaje de error:

$ mv main ..
$ ../main
Primera función ...
Segunda función ...
Función principal ...

También puede averiguar qué bibliotecas compartidas necesita una aplicación utilizando la utilidad ldd:

$ ldd main
linux-vdso.so.1 \u003d\u003e (0x00007fffaddff000)
libhello.so.2 \u003d\u003e ./libhello.so.2 (0x00007f9689001000)
libc.so.6 \u003d\u003e /lib/libc.so.6 (0x00007f9688c62000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9689205000)

En conclusión ldd para cada biblioteca requerida, se especifican su nombre de usuario y la ruta completa al archivo de la biblioteca, que se determina de acuerdo con la configuración del sistema.

Ahora es el momento de hablar sobre dónde debe colocar el sistema los archivos de la biblioteca, dónde el cargador intenta encontrarlos y cómo administrar este proceso.

De acuerdo a los acuerdos FHS (estándar de jerarquía del sistema de archivos) el sistema debe tener dos (al menos) directorios para almacenar archivos de biblioteca:

/ lib: aquí se recopilan las principales bibliotecas de distribución, que son necesarias para que funcionen los programas de / bin y / sbin;

/ usr / lib: las bibliotecas necesarias para las aplicaciones de / usr / bin y / usr / sbin se almacenan aquí;

Los archivos de encabezado correspondientes a las bibliotecas deben ubicarse en el directorio / usr / include.

El cargador buscará por defecto los archivos de la biblioteca en estos directorios.

Además de los enumerados anteriormente, el sistema debe contener el directorio / usr / local / lib; debe haber bibliotecas implementadas por el usuario de forma independiente, sin pasar por el sistema de administración de paquetes (no incluido en el kit de distribución). Por ejemplo, en este directorio, por defecto, habrá bibliotecas compiladas a partir de fuentes (los programas instalados desde las fuentes se ubicarán en / usr / local / bin y / usr / local / sbin, por supuesto, estamos hablando de distribuciones binarias). Los archivos de encabezado de la biblioteca en este caso se colocarán en / usr / local / include.

En una serie de distribuciones (en Ubuntu) el gestor de arranque no está configurado para mirar el directorio / usr / local / lib, respectivamente, si el usuario instala la biblioteca desde las fuentes, el sistema no la verá. Esto fue realizado por los autores del kit de distribución específicamente para capacitar al usuario a instalar software solo a través del sistema de administración de paquetes. Qué hacer en este caso se describirá a continuación.

De hecho, para simplificar y acelerar el proceso de búsqueda de archivos de la biblioteca, el cargador no mira los directorios anteriores cada vez que se accede, sino que usa la base de datos almacenada en el archivo /etc/ld.so.cache (caché de la biblioteca). Contiene información sobre dónde se encuentra en el sistema el archivo de biblioteca correspondiente al soname dado. El cargador, después de haber recibido una lista de bibliotecas requeridas para una aplicación en particular (una lista de bibliotecas de soname especificadas en el archivo ejecutable del programa), usa /etc/ld.so.cache para determinar la ruta al archivo de cada biblioteca requerida y cargarla en la memoria. Además, el cargador puede ver los directorios enumerados en las variables del sistema LD_LIBRARY_PATH, LIBRARY_PATH y en el campo RPATH del archivo ejecutable (ver arriba).

Para administrar y mantener actualizada la caché de la biblioteca, se utiliza la utilidad ldconfig. Si tu corres ldconfig sin ninguna opción, el programa escaneará los directorios especificados en la línea de comando, los directorios de confianza / lib y / usr / lib, los directorios listados en el archivo /etc/ld.so.conf. Para cada archivo de biblioteca que aparece en los directorios especificados, se leerá un soname, se creará un enlace simbólico basado en soname y se actualizará la información en /etc/ld.so.cache.

Verifique lo anterior:

$ ls
hola.h libhello.so libhello.so.2.4.0.5 main.c
$
$ sudo ldconfig / full / path / to / directory / c / example
$ ls
hola.h libhello.so libhello.so.2 libhello.so.2.4.0.5 main main.c
$ ./main
Primera función ...
Segunda función ...
Función principal ...

Primera llamada ldconfig agregamos nuestra biblioteca al caché, la excluimos con la segunda llamada. Tenga en cuenta que al compilar main, se omitió la opción -Wl, -rpath,., como resultado, el cargador buscó las bibliotecas necesarias solo en la memoria caché.

Ahora debe quedar claro qué hacer si, después de instalar la biblioteca desde la fuente, el sistema no la ve. En primer lugar, debe agregar la ruta completa al directorio con los archivos de la biblioteca en el archivo /etc/ld.so.conf (por defecto / usr / local / lib). Formato /etc/ld.so.conf: el archivo contiene una lista de directorios, separados por dos puntos, espacio, tabulación o nueva línea, para buscar bibliotecas. Luego llame ldconfig sin ninguna opción, pero con privilegios de superusuario. Todo debería funcionar.

Bueno, al final hablaremos sobre cómo las versiones estáticas y dinámicas de las bibliotecas se llevan bien. ¿Cuál es la pregunta real? Arriba, cuando se discutieron los nombres y la ubicación aceptados de los archivos de la biblioteca, se dijo que los archivos de las versiones estáticas y dinámicas de la biblioteca se almacenan en el mismo directorio. Cómo gcc descubre qué tipo de biblioteca queremos usar? La biblioteca dinámica se prefiere por defecto. Si el vinculador encuentra un archivo de biblioteca dinámica, sin dudarlo lo conecta al archivo ejecutable del programa:

$ ls
hola.h libhello.a libhello.so libhello.so.2 libhello.so.2.4.0.5 main.c
$ gcc -wall -c main.c
$ gcc -o main main.o -L. -hello -Wl, -rpath,.
$ ldd main
linux-vdso.so.1 \u003d\u003e (0x00007fffe1bb0000)
libhello.so.2 \u003d\u003e ./libhello.so.2 (0x00007fd50370b000)
libc.so.6 \u003d\u003e /lib/libc.so.6 (0x00007fd50336c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd50390f000)
$ du -h main
12K principal

Presta atención al tamaño del programa ejecutable. Es lo más pequeño posible. Todas las bibliotecas utilizadas están vinculadas dinámicamente.

Existe opción gcc -static - instruir al vinculador para que use solo versiones estáticas de todas las bibliotecas necesarias para la aplicación:

$ gcc -static -o main main.o -L. -hello
$ file main
principal: ELF ejecutable LSB de 64 bits, x86-64, versión 1 (GNU / Linux), enlazado estáticamente, para GNU / Linux 2.6.15, no eliminado
$ ldd main
no es un ejecutable dinámico
$ du -h main
728K principal

El tamaño del archivo ejecutable es 60 veces mayor que en el ejemplo anterior: las bibliotecas de idiomas estándar se incluyen en el archivo C. Ahora nuestra aplicación se puede transferir de forma segura de un directorio a otro e incluso a otras máquinas, el código de la biblioteca hola está dentro del archivo, el programa es completamente autónomo.

¿Qué sucede si necesita vincular estáticamente solo una parte de las bibliotecas utilizadas? Variante posible soluciones: haga que el nombre de la versión estática de la biblioteca sea diferente del nombre compartido, y al compilar la aplicación, especifique qué versión queremos usar esta vez:

$ mv libhello.a libhello_s.a
$ gcc -o main main.o -L. -lhello_s
$ ldd main
linux-vdso.so.1 \u003d\u003e (0x00007fff021f5000)
libc.so.6 \u003d\u003e /lib/libc.so.6 (0x00007fd0d0803000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd0d0ba4000)
$ du -h main
12K principal

Como el tamaño del código de la biblioteca libhello es insignificante,

$ du -h libhello_s.a
4.0K libhello.a

el tamaño del archivo ejecutable resultante prácticamente no difiere del tamaño de un archivo creado mediante el enlace dinámico.

Bueno, eso es probablemente todo. Muchas gracias a todos los que terminaron de leer en este momento.

Se cree ampliamente que GCC va a la zaga de otros compiladores en rendimiento. En este artículo, intentaremos averiguar qué optimizaciones básicas del compilador GCC deberían aplicarse para lograr un rendimiento aceptable.

¿Cuáles son las opciones predeterminadas en GCC?

(1) Por defecto, GCC usa el nivel de optimización "-O0". Claramente, no es óptimo en términos de rendimiento y no se recomienda para compilar el producto final.
GCC no reconoce la arquitectura en la que se ejecuta la compilación hasta que se pasa la opción "-march \u003d native". Por defecto, GCC usa la opción especificada durante su configuración. Para averiguar la configuración de GCC, simplemente ejecute:

Esto significa que GCC agregará "-march \u003d corei7" a sus opciones (a menos que se especifique una arquitectura diferente).
La mayoría de los compiladores de GCC para x86 (base para Linux de 64 bits) agregan: "-mtune \u003d generic -march \u003d x86-64" a las opciones dadas, ya que no se especificaron opciones de arquitectura en la configuración. Siempre puede encontrar todas las opciones pasadas al iniciar GCC, así como sus opciones internas, utilizando el comando:

Como resultado, de uso frecuente:

La arquitectura utilizada es importante para el rendimiento. La única excepción puede considerarse aquellos programas en los que la llamada a las funciones de la biblioteca toma casi todo el tiempo de inicio. GLIBC puede seleccionar la función óptima para una arquitectura dada en tiempo de ejecución. Es importante tener en cuenta que con la vinculación estática, algunas funciones GLIBC no tienen versiones para diferentes arquitecturas. Es decir, el ensamblaje dinámico es mejor si la velocidad de las funciones GLIBC es importante..
(2) Por defecto, la mayoría de los compiladores GCC para x86 en modo de 32 bits usan el modelo de punto flotante x87, ya que se configuraron sin "-mfpmath \u003d sse". Solo si la configuración de GCC contiene "--with-mfpmath \u003d sse":

el compilador utilizará el modelo SSE predeterminado. En todos los demás casos, es mejor agregar la opción "-mfpmath \u003d sse" al ensamblaje en modo de 32 bits.
Entonces, de uso frecuente:

¡Agregar la opción “-mfpmath \u003d sse” es importante en el modo de 32 bits! Una excepción es el compilador, en cuya configuración hay "--with-mfpmath \u003d sse".

32 bits o 64 bits?

El modo de 32 bits generalmente se usa para reducir la cantidad de memoria utilizada y como resultado de acelerar el trabajo con él (se almacenan más datos en la memoria caché).
En el modo de 64 bits (en comparación con 32 bits), el número de registros generales disponibles aumenta de 6 a 14, los registros XMM de 8 a 16. Además, todas las arquitecturas de 64 bits admiten la extensión SSE2, por lo que en el modo de 64 bits no es necesario agregar "-mfpmath \u003d sse ".
Se recomienda utilizar el modo de 64 bits para contar tareas y el modo de 32 bits para aplicaciones móviles.

¿Cómo se obtiene el mejor rendimiento?

No hay un conjunto específico de opciones para obtener el máximo rendimiento, pero hay muchas opciones en GCC que debería intentar usar. A continuación se muestra una tabla con las opciones recomendadas y las previsiones de crecimiento para los procesadores Intel Atom e Intel Core i7 de segunda generación en relación con la opción "-O2". Las predicciones se basan en la media geométrica de los resultados de un conjunto específico de problemas compilados con GCC versión 4.7. También se supone que la configuración del compilador se realizó para x86-64 genérico.
Previsión de aumento de rendimiento aplicaciones móviles en relación con "-O2" (solo en modo de 32 bits, ya que es el principal para el segmento móvil):

Pronóstico de aumento de rendimiento en tareas computacionales en relación con "-O2" (en modo de 64 bits):
-m64 -fast -flto ~17%
-m64 -Ofast -flto -march \u003d native ~21%
-m64 -Ofast -flto -march \u003d nativo -funroll-loops ~22%

La ventaja del modo de 64 bits sobre el modo de 32 bits para las tareas informáticas con las opciones "-O2 -mfpmath \u003d sse" es aproximadamente ~ 5%
Todos los datos del artículo son pronósticos basados \u200b\u200ben los resultados de un conjunto específico de puntos de referencia.
A continuación se muestra una descripción de las opciones utilizadas en el artículo. Descripción completa (en inglés): http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Optimize-Options.html "
  • "-Ofast" como "-O3 -ffast-math" incluye un mayor nivel de optimizaciones y optimizaciones más agresivas para cálculos aritméticos (por ejemplo, reasociación real)
  • Optimizaciones entre módulos "-flto"
  • Modo "-m32" de 32 bits
  • "-mfpmath \u003d sse" permite el uso de registros XMM en aritmética real (en lugar de la pila real en modo x87)
  • "-funroll-loops" permite desenrollar bucles

Logística de transporte (análisis de varios modos de transporte: ventajas, desventajas)

El transporte es una rama de la producción de materiales que transporta personas y bienes. En la estructura de la producción social, el transporte se refiere a la producción de servicios materiales.

Se observa que una parte importante de las operaciones logísticas en la ruta del flujo de material desde la fuente primaria de materias primas hasta el consumo final se lleva a cabo utilizando varios vehículos. El costo de estas operaciones es de hasta el 50% de los costos logísticos totales.

Según su propósito, se distinguen dos grupos principales de transporte: el transporte público, un sector de la economía nacional que satisface las necesidades de todos los sectores de la economía nacional y la población en el transporte de mercancías y pasajeros. Los servicios de transporte público en el ámbito de la circulación y la población. A menudo se le llama troncal (la autopista es la línea principal, principal en algunos sistemas, en este caso, en el sistema de líneas de comunicación). El concepto de transporte público abarca transporte ferroviario, transporte por agua (mar y río), transporte por carretera, aéreo y por tuberías).

Transporte no público: transporte intraindustrial, así como vehículos de todo tipo pertenecientes a organizaciones no transportistas.

La organización del movimiento de mercancías por transporte no público es objeto de estudio de logística de producción. La tarea de elegir canales de distribución se resuelve en el campo de la logística de distribución.

Por lo tanto, existen los siguientes tipos principales de transporte:

ferrocarril

río navegable

coche

aire

tubería

Cada uno de los modos de transporte tiene características específicas en términos de gestión logística, ventajas y desventajas que determinan las posibilidades de su uso en el sistema logístico. Varios tipos de transporte forman el complejo de transporte. El complejo de transporte de Rusia está formado por personas jurídicas y personas registradas en su territorio: empresarios que realizan actividades de transporte y reenvío en todo tipo de transporte, diseño, construcción, reparación y mantenimiento de ferrocarriles, carreteras y estructuras en ellos, tuberías, trabajos relacionados con mantenimiento de estructuras hidráulicas navegables, agua y vías respiratorias comunicaciones por investigación científica y capacitación del personal, que es parte del sistema de transporte, empresas que fabrican vehículos, así como organizaciones que realizan otros trabajos relacionados con el proceso de transporte. El TC de Rusia consta de más de 160 mil km de vías principales de ferrocarril y acceso, 750 mil km de carreteras de superficie dura, 1,0 millones de km de líneas marítimas, 101 mil km de vías navegables interiores, 800 mil km de líneas aéreas. A través de estas comunicaciones, solo 4,7 millones de toneladas de carga se transportan diariamente en transporte público (a partir de 2000), más de 4 millones de personas trabajan en el centro comercial y la proporción de transporte en el producto interno bruto del país es de aproximadamente el 9%. Por lo tanto, el transporte es la parte más importante de la infraestructura de la economía y todo el potencial social y productivo de nuestro país.

En la mesa. 1 (4, 295) Se dan las características logísticas comparativas de varios modos de transporte.

Tabla 1 Características de los modos de transporte.

Tipo de transporte

Ventajas

desventajas

ferrocarril

Alto rendimiento y rendimiento. Independencia de las condiciones climáticas, época del año y día.

Alta regularidad de transporte. Tasas relativamente bajas; importantes descuentos para envíos en tránsito. Entrega de mercancías a alta velocidad a largas distancias.

Número limitado de transportistas. Grandes inversiones de capital en la producción y base técnica. Alta intensidad de material y energía del transporte. Baja disponibilidad para puntos finales de ventas (consumo).

Insuficientemente alta seguridad de la carga.

La posibilidad de transporte intercontinental. Bajo costo de transporte a largas distancias. Alta capacidad de carga y transporte. Baja intensidad de capital de transporte.

Transporte limitado

Baja velocidad de entrega (largo tiempo de tránsito).

Dependencia de las condiciones geográficas, de navegación y climáticas.

La necesidad de crear una infraestructura portuaria compleja.

Aguas continentales (río)

Alta capacidad de carga en ríos profundos y cuerpos de agua.

Bajo costo de transporte. Baja intensidad de capital.

Transporte limitado Baja velocidad de entrega de carga.

Dependencia de profundidades desiguales de ríos y embalses, condiciones de navegación. Estacionalidad Insuficiente confiabilidad del transporte y seguridad de la carga.

coche

Alta disponibilidad.

Posibilidad de entrega de carga "de puerta a puerta"

Alta maniobrabilidad, flexibilidad, dinamismo. Alta velocidad de entrega. La capacidad de utilizar varias rutas y esquemas de entrega.

Alta seguridad de carga. La capacidad de enviar carga en pequeños lotes.

Bajo rendimiento. Depende del clima y las condiciones del camino. costo relativamente alto de transportar largas distancias.

Limpieza ambiental inadecuada.

Aire

La mayor velocidad de entrega de carga. Alta fiabilidad.

Máxima seguridad de carga.

Las rutas de transporte más cortas.

Alto costo de transporte, las tarifas más altas entre otros tipos de transporte. Alta intensidad de capital, material e intensidad energética del transporte. Dependencia de las condiciones climáticas. Insuficiente accesibilidad geográfica.

tubería

Precio de bajo costo. Alto rendimiento (rendimiento). Alta seguridad de carga. Baja intensidad de capital.

Tipos limitados de carga (gas, productos derivados del petróleo, emulsiones de materias primas). Inadecuada disponibilidad de pequeños volúmenes de mercancías transportadas.

Entonces, antes que nada, el gerente de logística debe decidir si crear su propia flota de vehículos o usar vehículos alquilados (públicos o privados). Al elegir una alternativa, generalmente proceden de un cierto sistema de criterios, que incluyen: El costo de crear y operar su propia flota de vehículos. El costo de pagar por los servicios de transporte, empresas de transporte y otros intermediarios logísticos en el transporte Velocidad de transporte

Calidad del transporte (fiabilidad de la entrega, seguridad de la carga, etc.)

En la mayoría de los casos, las empresas manufactureras utilizan los servicios de empresas de transporte especializadas.