¿Cómo compilar programas CUDA en Linux?
Existen los siguientes pasos:
1. Compilación del programa fuente
En Linux, si queremos compilar un programa fuente en lenguaje C, debemos use el compilador gcc de GNU. A continuación
Usamos un ejemplo para ilustrar cómo usar el compilador gcc.
Supongamos que tenemos el siguiente programa fuente muy simple (hello.c): p>
int main(int argc,char **argv)
{
printf("Hola Linux\n");
}
Para compilar este programa, sólo necesitamos ejecutarlo desde la línea de comando:
gcc -o hola hola.c
El compilador gcc generará un saludo código para nosotros Ejecute el archivo. Ejecute ./hello y podrá ver el resultado del programa
. En la línea de comando, gcc significa que usamos gcc para compilar nuestro programa fuente, y el -o. opción significa
El archivo ejecutable que le pedimos al compilador que nos envíe se llama hello y hello.c es nuestro archivo de programa fuente.
El compilador gcc tiene muchas opciones. solo necesitamos conocer algunos de ellos. Uno es suficiente. Ya conocemos la opción -o, que indica el nombre del archivo ejecutable que necesitamos generar. requerir que el compilador genere
código objeto. No es necesario generar un archivo ejecutable. La opción -g indica que requerimos que el compilador proporcione información para que podamos depurar el programa en el futuro durante la compilación.
Conozca estas tres opciones, podemos compilar el programa fuente simple que escribimos nosotros mismos. Si desea conocer más opciones, puede consultar el documento de ayuda de gcc, que tiene muchos detalles. descripciones de otras opciones.
Ming.
2. Preparación del Makefile
Supongamos que tenemos un programa como el siguiente, el código fuente es el siguiente:
p>
#include "mytool1.h"
#include "mytool2.h"
int main(int argc,char **argv)
{
mytool1_print("hola");
mytool2_print("hola");
}
#ifndef _MYTOOL_1_H p>
#define _MYTOOL_1_H
void mytool1_print(char *print_str);
#endif
#include "mytool1.h"
void mytool1_print(char *print_str)
{
printf("Esta es mytool1 print %s\n",print_str);
}
# ifndef _MYTOOL_2_H
#define _MYTOOL_2_H
void mytool2_print(char *print_str);
#endif
#include "mytool2.h"
void mytool2_print(char *print_str)
{
printf("Esta es mytool2 print %s\n",print_str );
}
Por supuesto, dado que este programa es muy corto, podemos compilarlo así
gcc -c main.c
gcc -c miherramienta1.c
gcc -c mit
ool2.c
gcc -o main main.o mytool1.o mytool2.o
En este caso, también podemos generar el programa principal, lo cual también es muy problemático. Si consideramos
Si un día modificamos uno de los archivos (por ejemplo, mytool1.c), ¿tenemos que volver a ingresar el comando anterior? Quizás dirás, esto es fácil de resolver. Puedo escribir un script SHELL y pedirle que me ayude a completarlo. Sí, puede funcionar para este programa, pero cuando ponemos las cosas, pensamos de manera más complicada. Un programa tiene cientos de programas fuente, ¿necesita el compilador recompilarlos?
uno por uno?
Por esta razón, programadores inteligentes han creado una buena herramienta para hacer esto, que es make.
Siempre que ejecutemos el siguiente make, podemos resolver el problema anterior. Antes de ejecutar make, primero debemos
escribir un archivo muy importante.-- Makefile. Para el programa anterior, un posible
Makefile es:
# Este es el Makefile del programa anterior
main:main.o mytool1.o mytool2.o
gcc -o main main.o mytool1. o mytool2.o
main.o:main.c mytool1.h mytool2.h
gcc -c main.c
mytool1.o:mytool1. c mytool1.h
gcc -c mytool1.c
mytool2.o:mytool2.c mytool2.h
gcc -c mytool2.c
Con este Makefile, cuando modificamos cualquier archivo en el programa fuente,
Siempre que ejecutemos Con el comando make, nuestro compilador solo compilará los archivos que modificamos. Archivos relacionados, otros
Ella ni siquiera quiere tratar con otros archivos.
Aprendamos cómo se escribe Makefile. .
En Makefile, las líneas que comienzan con # son todas líneas de comentarios. Lo más importante en Makefile es la descripción de la relación de dependencia
del archivo. :
objetivo: componentes
Regla TAB
La primera línea representa la relación de dependencia. La segunda línea es la regla.
Para. Por ejemplo, la segunda línea del Makefile anterior
main:main.o mytool1.o mytool2.o
Indica que los objetos dependientes (componentes) de nuestro objetivo (objetivo) principal son main.o mytool1.o
mytool2. o Cuando el objeto dependiente se modifica después de modificar el objetivo, el comando especificado en la línea de regla
debe ejecutarse como el tercero. línea de nuestro Makefile mencionada anteriormente, gcc -o debe ejecutarse main main.o
mytool1.o mytool2.o Tenga en cuenta que el TAB en la línea de regla indica que hay una tecla TAB
.Makefile tiene tres variables muy útiles: $@, $. Los significados de ^ y $< son:
$@--archivo de destino, $^--todos los archivos dependientes, $<-. -el primer archivo dependiente.
Si usamos las tres variables anteriores, entonces podemos simplificar nuestro Makefile como:
# Este es el Makefile simplificado
principal :main.o miherramienta1.o miherramienta2.o
g
cc -o $@ $^
main.o:main.c miherramienta1.h miherramienta2.h
gcc -c $<
miherramienta1.o: mytool1.c mytool1.h
gcc -c $<
mytool2.o:mytool2.c mytool2.h
gcc -c $<
Después de la simplificación, nuestro Makefile es un poco más simple, pero a veces la gente todavía quiere ser más simple. Aquí
Aprendemos las reglas predeterminadas de un Makefile
.c.o: <. /p>
gcc -c $<
Esta regla indica que todos los archivos .o dependen del archivo .c correspondiente. Por ejemplo, mytool.o depende de
mytool. .c, este Makefile también puede convertirse en:
# Este es otro Makefile simplificado
main:main.o mytool1.o mytool2.o
gcc -o $@ $^
.c.o:
gcc -c $<
Está bien, nuestro Makefile ya casi está disponible. Si desea saber más sobre las reglas de Makefile. , puedes consultar
la documentación correspondiente.
3. Enlace a la biblioteca del programa
Intenta compilar el siguiente programa
#include
int main(int argc,char **argv)
{
valor doble;
printf ("Valor:%f\ n",value);
}
Este programa es bastante simple, pero cuando lo compilamos con gcc -o temp temp.c, aparecerá lo siguiente Error mostrado
.
/tmp/cc33Kydu.o: En función `main':
/tmp/cc33Kydu.o(.text+0xe): referencia no definida a `log '
collect2: ld devolvió 1 estado de salida
Este error se produce porque el compilador no puede encontrar la implementación específica de log. Aunque incluimos el encabezado correcto
Archivo. , pero aún necesitamos conectarnos a una determinada biblioteca al compilar. En Linux, para poder usar funciones matemáticas, debemos conectarnos a la biblioteca matemática, para lo cual necesitamos agregar la opción -lm. gcc -o temp temp.c -lm para que puedas compilar correctamente. Algunas personas pueden querer preguntar, ¿por qué no conectamos la biblioteca cuando usamos la función printf antes? p>
p>
Para la implementación de algunas funciones de uso común, el compilador gcc se conectará automáticamente a algunas bibliotecas comunes, por lo que no necesitamos especificarlo nosotros mismos. compilamos el programa En este momento, necesitamos especificar la ruta de la biblioteca. En este momento
Necesitamos usar la opción -L del compilador para especificar la ruta. biblioteca en /home/hoyt/mylib
, por lo que necesitamos agregar -L/home/hoyt/mylib al compilar. Para algunas bibliotecas estándar, no es necesario
. especifique la ruta, siempre que estén en la biblioteca predeterminada. La ruta de la biblioteca predeterminada del sistema es /lib
/usr/lib /usr/local/lib. para especificar una ruta.
Hay otra pregunta. A veces usamos una determinada función, pero no sabemos el nombre de la biblioteca. ¿Qué debemos hacer en este momento? Tampoco conozco este problema. La respuesta es que solo tengo una forma estúpida: primero, voy a la ruta de la biblioteca estándar.
Fui a ver si había alguna biblioteca relacionada con la función que usé y encontré el archivo de biblioteca (libpthread.a) de la función del hilo. Por supuesto, si no puedo encontrarlo, solo puedo hacerlo. Por ejemplo, si quiero encontrar la biblioteca donde se encuentra la función
sin, tengo que usar el comando nm -o /lib/*.so|grep sin>~/sin y luego buscar en el archivo ~/sin p>
, búscalo allí. En el archivo sin, encontraré una línea como libm-2.1.2.so:00009fa0
W sin. Entonces lo sé. donde está sin. En la biblioteca libm-2.1.2.so, solo uso la opción -lm (elimine la biblioteca al frente
y la marca de versión en la parte posterior, y solo queda m, por lo que es -lm).
4. Depuración del programa
Es poco probable que el programa que escribimos tenga éxito de una sola vez, habrá muchas cosas en nuestro programa
<. p>Error que no podemos pensar, en este momento tenemos que depurar nuestro programa.El software de depuración más utilizado es gdb. puedes elegir ahora
Elige xxgdb. Recuerda agregar la opción -g al compilar. En cuanto al uso de gdb, puedes ver el archivo de ayuda de gdb.
No lo he usado. Este software, por lo que no puedo decir cómo usarlo, pero no me gusta usar gdb. El seguimiento de un programa es muy molesto. Normalmente uso el valor de la variable intermedia en el programa para depurarlo. >
Por supuesto, puedes elegir tu propio método, no es necesario aprender de otros. Hoy en día, existen muchos entornos IDE que ya tienen sus propios depuradores. Puedes elegir algunos y probarlos para descubrirlo. Uno de mis usos favoritos.
5. Archivos de encabezado y ayuda del sistema
A veces solo conocemos la forma general de una función, pero no recordamos la expresión exacta, o no la recordamos. La función se explica en ese archivo de encabezado. En este momento, podemos pedirle ayuda al sistema. Por ejemplo, si queremos saber la forma exacta de la función fread, solo necesitamos ejecutar man fread. genera una explicación detallada de la función y el archivo de encabezado donde se encuentra la función. Si queremos escribir esta descripción de función, cuando ejecutamos man write, el resultado de salida no es el que necesitamos. Porque lo que queremos es la descripción de la función de escritura, pero lo que sale es la descripción del comando de escritura. Para obtener la descripción de la función de escritura, necesitamos usar man 2 write. 2 significa que estamos usando la función de escritura, que es una función de llamada al sistema. También hay un 3 de uso común, que significa que la función es una función de biblioteca c.