Red de conocimiento informático - Aprendizaje de programación - ¿Cómo compilar programas CUDA en Linux?

¿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):

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

#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

, 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.