Linux下c和cuda混合编译,并生成动态链接库.so和使用

时间:2021-12-25 07:00:31

Linux下c和cuda混合编译,并生成动态链接库.so和使用

98人阅读 评论(0)收藏举报 Linux下c和cuda混合编译,并生成动态链接库.so和使用分类:

目录(?)[+]

梗概

如果要生成动态链接库,就需要把源码,无论是.c .cpp .cu还是其他的语言写的程序,都通过编译器变成.o文件,之后把相应的.o文件进行链接成为.so动态链接库。这样就可以直接调用其中的函数了。
形成过程: .c .cpp .cu -> .o -> .so
使用 : test.c + .so - > test
./test
但是其中还是有许多的小细节需要注意的。


现在就举个栗子:

把yolo算法,编译成动态链接库,其中需要用到opencv、cuda、cudnn。之后讲介绍怎么把这些库一起与源码进行编译,生成可以用的.so。
源码文件分为三类:.c文件 .h头文件和 .cu的cuda文件
【分析编译.c文件】

<code class="hljs lasso has-numbering">gcc <span class="hljs-attribute">-fPIC</span> <span class="hljs-attribute">-DOPENCV</span> <span class="hljs-string">`pkg-config --cflags opencv`</span> <span class="hljs-attribute">-DGPU</span> <span class="hljs-attribute">-I</span>/usr/<span class="hljs-built_in">local</span>/cuda/include<span class="hljs-subst">/</span> <span class="hljs-attribute">-DCUDNN</span> 
<span class="hljs-attribute">-Wall</span> <span class="hljs-attribute">-Wfatal</span><span class="hljs-attribute">-errors</span> <span class="hljs-attribute">-Ofast</span> <span class="hljs-attribute">-DOPENCV</span> <span class="hljs-attribute">-DGPU</span> <span class="hljs-attribute">-DCUDNN</span> <span class="hljs-attribute">-c</span> <span class="hljs-built_in">.</span>/src/image<span class="hljs-built_in">.</span>c <span class="hljs-attribute">-o</span> obj/image<span class="hljs-built_in">.</span>o</code><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul>

gcc : 编译器
-fPIC: 使用 -fPIC 选项,会生成 PIC 代码。告诉编译器产生与位置无关代码(Position-Independent Code), .so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。
`pkg-config –cflags opencv`: 根据pkg-config信息来找到opencv的include,因为源文件中包含使用了一些opencv的函数,所以需要引入include文件。对应的“ 两个反引号不要少。
-I/usr/local/cuda/include/ : 首先-I表示根据需要在/usr/local/cuda/include/目录下找到对应的文件。并包含进去一起编译。
-Wall: 表示把所有警告都展示出来。
-Wfatal-errors: 表示当发生第一个错误的时候停止编译,这样的好处就是可以找到错误的地方,防止许多无用的信息掩盖了错误信息。
-Ofast:编译所采用的优化手段。
-c: 进行源文件的编译成.o文件
-o: 输出到哪里,并且文件名是什么,只有紧跟其后的一项是输出
-DOPENCV,-DGPU,-DCUDNN:这些选项就是控制代码中的宏定义,如果带有这些选项进行编译,证明宏定义是选定的。即#ifdef OPENCV 是定义了的,否则没有定义。这也相当与vs中的预处理选项。通过这项可以根据修改Makefile文件来确定某个库是否使用,在源码中再通过宏定义来实现,非常方便。

【分析.cu文件编译】

<code class="hljs lasso has-numbering">nvcc <span class="hljs-subst">--</span>gpu<span class="hljs-attribute">-architecture</span><span class="hljs-subst">=</span>compute_52 <span class="hljs-subst">--</span>gpu<span class="hljs-attribute">-code</span><span class="hljs-subst">=</span>compute_52  <span class="hljs-attribute">-DOPENCV</span> <span class="hljs-string">`pkg-config --cflags opencv2`</span> 
<span class="hljs-attribute">-DGPU</span> <span class="hljs-attribute">-I</span>/usr/<span class="hljs-built_in">local</span>/cuda/include<span class="hljs-subst">/</span> <span class="hljs-attribute">-DCUDNN</span> <span class="hljs-subst">--</span>compiler<span class="hljs-attribute">-options</span> <span class="hljs-string">"-Wall -Wfatal-errors -Ofast
-DOPENCV -DGPU -DCUDNN -fPIC"</span> <span class="hljs-attribute">-c</span> <span class="hljs-built_in">.</span>/src/convolutional_kernels<span class="hljs-built_in">.</span>cu <span class="hljs-attribute">-o</span> obj/convolutional_kernels<span class="hljs-built_in">.</span>o</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

很多与c的编译差不多。
nvcc : cuda的编译器
–gpu-architecture=compute_52 –gpu-code=compute_52 : 这个是根据显卡计算能力来确定的,来表示gpu的架构是说明,我的显卡是GT960,所以是compute_52
–compiler-options : 在双引号中填写一些编译选项。 尤其是-fPIC选项,因为在外面写是编译不通过的,只能在双引号里面写。

把所有的.c文件和.cu文件都编译成.o文件,当然都是-fPIC编译的,即是位置无关的。
现在就开始通过这些目标文件来进行.so的生成。

<code class="hljs avrasm has-numbering">gcc -shared -o libtest<span class="hljs-preprocessor">.so</span> *<span class="hljs-preprocessor">.o</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

-shared : 因为要做成动态链接库,所以需要把该库做成可以共享的。
*.o : 即刚才生成的所有.o文件,依次添加进去。
但是只把这些.o文件进行链接,链接到libtest.so文件中,虽然也可以生成.so文件,但是在使用时会出现一些错误,如:undefined reference to `cudnnGetConvolutionBackwardDataWorkspaceSize’ 等关于cudnn的错误。 所以需要在链接的时候把cudnn的相关链接也链接进去。

<code class="hljs lasso has-numbering">gcc <span class="hljs-attribute">-shared</span> <span class="hljs-attribute">-o</span> libtest<span class="hljs-built_in">.</span>so <span class="hljs-subst">*</span><span class="hljs-built_in">.</span>o <span class="hljs-attribute">-L</span>/usr/<span class="hljs-built_in">local</span>/cuda/lib64 <span class="hljs-attribute">-lcuda</span> <span class="hljs-attribute">-lcudart</span> <span class="hljs-attribute">-lcublas</span> <span class="hljs-attribute">-lcurand</span> <span class="hljs-attribute">-lcudnn</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

-L/usr/local/cuda/lib64 : 确定lib的路径,-L后面跟的就是lib所在的路径,如果要引入其中的.so文件,只需要-l开头即可,后面跟文件名去掉lib的名字即可。
这样就生成了.so文件。

使用.so文件:

Step 1:

<code class="hljs brainfuck has-numbering"><span class="hljs-comment">gcc</span> <span class="hljs-literal">-</span><span class="hljs-comment">c</span> <span class="hljs-comment">test</span><span class="hljs-string">.</span><span class="hljs-comment">c</span> <span class="hljs-comment">$(pkg</span><span class="hljs-literal">-</span><span class="hljs-comment">config</span> <span class="hljs-literal">-</span><span class="hljs-literal">-</span><span class="hljs-comment">cflags</span> <span class="hljs-literal">-</span><span class="hljs-literal">-</span><span class="hljs-comment">libs</span> <span class="hljs-comment">opencv)</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

因为test.c中有opencv的函数,所以编译的时候需要引入opencv,生成test.o文件
Step 2:

<code class="hljs lasso has-numbering">gcc <span class="hljs-attribute">-o</span> test test<span class="hljs-built_in">.</span>o libtest<span class="hljs-built_in">.</span>so <span class="hljs-attribute">-L</span>/usr/<span class="hljs-built_in">local</span>/cuda/lib64 <span class="hljs-attribute">-lcuda</span> <span class="hljs-attribute">-lcudart</span> <span class="hljs-attribute">-lcublas</span> <span class="hljs-attribute">-lcurand</span> <span class="hljs-attribute">-lcudnn</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

把程序与cuda的库进行动态链接,生成可执行文件test。