cndaqiang Web Linux DFT

Fortran/C函数库的使用

2019-02-01
cndaqiang
RSS

shell+gfortran

参考

《Fortran实用编程》系列视频教程 - Fortran Coder 研讨团队

静态库(.a) 动态库(.so)

不同的编译器编译的函数库不能混用

编译.o时不用链接其他需要的对象

编译主程序时,要在后面放上所有的对象

#-c编译,不用编入其他.o
m_matradd.o : m_matradd.f90 $(MPI)
               $(FC) $(FCFLAGE) -c -o $@  m_matradd.f90

#生成主程序,要所有对象
test:$(TEST) $(MPI)
        $(FC)  $(FCFLAGE)   -o $@  $(TEST) $(MPI)  $(LIB)

lib静态库(.a)

静态库 lib,实际上,就是 obj 文件的集合。可以认为是打包在一起的若干 obj

编译使用

因此它的编译过程是:

  • 1.编译子程序源代码,得到若干 obj 文件 gfortran -c xxx.f90 xx.f90

  • 2.打包这些 obj 文件,成为 lib 静态库ar rv xxx.a xxx.o xxxx.o xx.o

它的使用过程也比较简单:

编译主程序(或其他子程序),链接时,带上 lib 文件即可,与多文件编译链接一样

使用静态库编译的程序,运行时不依赖静态库

当然,有些编译器提供动态库,还是动态链接的,用ldd a.out可以查看,所以编译程序时,要设置好编译器的动态库,如export LD_LIBRARY_PATH=/home/cndaqiang/soft/gcc-4.8.4/lib64:$LD_LIBRARY_PATH

示例

cndaqiang@DESKTOP-N5I64SI:lib$ ls
fun.f90  main.f90  sub2.f90
cndaqiang@DESKTOP-N5I64SI:lib$ cat main.f90
program main
      use fun
      implicit none
      call sub1()
      call sub2()
end program main
cndaqiang@DESKTOP-N5I64SI:lib$ cat fun.f90
Module fun
      implicit none
      contains
              subroutine sub1()
                      implicit none
                      write(*,*) "this is sub1"
              end subroutine sub1
end module fun
cndaqiang@DESKTOP-N5I64SI:lib$ cat sub2.f90
subroutine sub2()
      implicit none
      write(*,*) "this is sub2"
end subroutine sub2
cndaqiang@DESKTOP-N5I64SI:lib$ gf -c fun.f90 sub2.f90
cndaqiang@DESKTOP-N5I64SI:lib$ ls
fun.f90  fun.mod  fun.o  main.f90  sub2.f90  sub2.o
!此处的fun.mod类似与为module的描述文件,需要有,才能正常调用module
cndaqiang@DESKTOP-N5I64SI:lib$ ar rv libfun.a fun.o sub2.o  !打包成静态库
ar: creating libfun.a
a - fun.o
a - sub2.o
cndaqiang@DESKTOP-N5I64SI:lib$ gf main.f90 libfun.a    !链接程序和静态库
cndaqiang@DESKTOP-N5I64SI:lib$ ./a.out
 this is sub1
 this is sub2
 cndaqiang@DESKTOP-N5I64SI:lib$ ldd a.out
        linux-vdso.so.1 (0x00007ffffd2af000)
        libgfortran.so.4 => /usr/lib/x86_64-linux-gnu/libgfortran.so.4 (0x00007fbecf830000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbecf430000)
        libquadmath.so.0 => /usr/lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007fbecf1f0000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fbecee50000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fbecec30000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbed0000000)

dll动态库(.so)

动态库 DLL,实际上也是可执行文件,所以intel的mkl库里面,.so文件是绿色可执行属性

只不过dll通常没有主程序而已,它必须由其他程序调用后才能运行

程序运行时,需要能找到动态库如export LD_LIBRARY_PATH=动态库目录:$LD_LIBRARY_PATH

编译使用

 gfortran fun.f90 sub2.f90 -shared -fPIC -o libfun.so
 gfortran main.f90 libfun.so   !动态库要放到后面

示例

cndaqiang@DESKTOP-N5I64SI:lib$ ls
fun.f90  main.f90  sub2.f90
cndaqiang@DESKTOP-N5I64SI:lib$ gf fun.f90 sub2.f90 -shared -fPIC -o libfun.so
cndaqiang@DESKTOP-N5I64SI:lib$ ls
fun.f90  fun.mod  libfun.so  main.f90  sub2.f90
cndaqiang@DESKTOP-N5I64SI:lib$ gfortran libfun.so main.f90 !把动态库写前面报错
/tmp/ccD5FpLx.o: In function `MAIN__':
main.f90:(.text+0x5): undefined reference to `__fun_MOD_sub1'
main.f90:(.text+0xf): undefined reference to `sub2_'
collect2: error: ld returned 1 exit status
cndaqiang@DESKTOP-N5I64SI:lib$ gfortran  main.f90 libfun.so
cndaqiang@DESKTOP-N5I64SI:lib$ ./a.out !找不到动态库目录报错
./a.out: error while loading shared libraries: libfun.so: cannot open shared object file: No such file or directory
cndaqiang@DESKTOP-N5I64SI:lib$ export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
cndaqiang@DESKTOP-N5I64SI:lib$ ./a.out
 this is sub1
 this is sub2
 cndaqiang@DESKTOP-N5I64SI:lib$ ldd a.out
        linux-vdso.so.1 (0x00007fffd4cca000)
        libfun.so => /mnt/c/Storage/code/siesta/siesta4_1/siesta-4.1-b1/Fortran/bianyi/lib/libfun.so
(0x00007f6274330000)
        libgfortran.so.4 => /usr/lib/x86_64-linux-gnu/libgfortran.so.4 (0x00007f6273f50000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6273b50000)
        libquadmath.so.0 => /usr/lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007f6273910000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6273570000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6273350000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6274800000)

第三方库调用

背景知识

内容 通常所在的文件夹 开源函数库 闭源函数库
文档 document,docs,help,notes,man 可能提供 提供
源代码 src,source,code **提供 ** 不提供
接口文件 src,source,interface 不提供 可能提供
包含文件 include 不提供(编译后可能产生),使用-Iinclude所在目录告诉编译器 提供
库文件 lib 不提供(编译后产生) 提供
范例代码 test,examples,demo 可能提供 可能提供
其他工具 bin,tools 可能提供 可能提供
运行时库 redist 不提供 可能提供

例fftw编译后的目录

[cndaqiang@managernode fftw-3.3.4]$ ls
bin  include  lib  share
[cndaqiang@managernode fftw-3.3.4]$ ls include/
fftw3.f  fftw3.f03  fftw3.h  fftw3l.f03  fftw3l-mpi.f03  fftw3-mpi.f03  fftw3-mpi.h  fftw3q.f03
[cndaqiang@managernode fftw-3.3.4]$ ls lib
libfftw3.a  libfftw3.la  libfftw3_mpi.a  libfftw3_mpi.la  pkgconfig

所有函数库的使用,归纳起来,总是离不开这么五个内容

  include (mod) 编译时用到 lib 链接时用到 DLL Runtime Library 运行时用到
路径(在哪儿?) 一般只设置一次 把include的路径 告知编译器 把lib的路径 告知编译器 把运行时库加入 系统目录 或path目录
文件名(哪个?) 一般每个工程都需要设置 把所需的 module告知编译器 把所需的lib文件 告知编译器  

调用语法

gfortran main.f90 xxx.o libxxx.so libxxx.a
gfortran main.f90 -lxxx -L/libxxx.so(a)所在目录

详细参数

  • -I    指明头文件路径(include)
  • -L    link的时候,gcc会先从-L指定的目录去找库
    也可将库所在目录加入环境变量LD_LIBRARY_PATH(运行时目录,动态库目录,编译运行都需要加入环境变量)或LIBRARY_PATH(静态库目录,编译时加入环境变量就行),就不用写-L目录
  • -l    指定库文件(库名)
    注:-l 紧接着就是库名,比如库文件名是libm.so的库名是m:把库文件名的头lib和尾.so去掉就是库名
  • 第三方库如果编译sudo make默认到系统,则安装在/usr/local/LIBCURL_CFLAGS='-I/usr/local/include' LIBCURL_LIBS='-L/usr/local/lib -lcurl'

编译vasp时,调用函数库示例

OBJECTS    = fftmpiw.o fftmpi_map.o fftw3d.o fft3dlib.o \
           /opt/fftw/lib/libfftw3_mpi.a
OBJECTS    +=-L$(MKL_PATH) -lmkl_intel_lp64 
INCS       =-I/opt/fftw/include
!略
MKLROOT    = /opt/intel/compilers_and_libraries/linux/mkl
FC         = mpif90  -m64 -I${MKLROOT}/include
FCL        = mpif90  -m64 -I${MKLROOT}/include

编译siesta时

BLAS_LIBS=/home/chendq/soft/scalapack/lib/librefblas.a
LAPACK_LIBS=/home/chendq/soft/scalapack/lib/libreflapack.a
BLACS_LIBS=
SCALAPACK_LIBS=/home/chendq/soft/scalapack/lib/libscalapack.a
COMP_LIBS=dc_lapack.a

NETCDF_LIBS=
NETCDF_INTERFACE=

LIBS=$(SCALAPACK_LIBS) $(BLACS_LIBS) $(LAPACK_LIBS) $(BLAS_LIBS) $(NETCDF_LIBS)

!省略

siesta: check-siesta what version $(MPI_INTERFACE) $(FDF) $(WXML) $(XMLPARSER) \
                $(COMP_LIBS) $(ALL_OBJS) 
	$(FC) -o siesta \
	       $(LDFLAGS) $(ALL_OBJS) $(FDF) $(WXML) $(XMLPARSER) $(MPI_INTERFACE)\
               $(COMP_LIBS) $(FoX_LIBS) $(LIBS) 
#

lapack使用示例

主程序所在文件lib.f90

我们传入了 A 和 b 及其大小,最终得到的结果覆盖了b,注意:A也被覆盖了因此我们用aa保存原来的值

最后,我们用 matmul 来检查计算是否正确。

program libla
        implicit none
        real:: a(3,3),aa(3,3),b(3)
        integer::v(3),iflag
        external sgesv !调用lapack中的sgesv用来求解 Ax=b 的线性方程组

        aa=reshape([2.0,1.0,3.0,6.0,2.0,-4.0,4.0,3.0,-6.0],[3,3])
        a=aa
        b=[998.0,999.0,1000.0]
        write(*,*) 'a=',a
        write(*,*) 'b=',b
        call sgesv(3,1,a,3,v,b,3,iflag)
        write(*,*) 'solve=',b
        write(*,*) matmul(aa,reshape(b,[3,1]))
end program libla

编译,运行

[cndaqiang@managernode fortran]$ ls $MATHDIR
librefblas.a  libreflapack.a  libscalapack.a  libtmg.a
[cndaqiang@managernode fortran]$ gfortran lib.f90 -lreflapack  -lrefblas -L$MATHDIR
!lapack依赖于blas库,需要调用两个库
[cndaqiang@managernode fortran]$ ./a.out 
 a=   2.00000000       1.00000000       3.00000000       6.00000000       2.00000000      -4.00000000       4.00000000       3.00000000      -6.00000000    
 b=   998.000000       999.000000       1000.00000    
 solve=   599.599915      -220.119980       279.879974    
   997.999878       998.999878       1000.00000  

关于lapack函数的更多介绍Lapack中文帮助手册手册.pdf

其他

-库名 -L目录 当静态库动态库同时存在时,vasp默认调用的是动态库

makefile

MKL_PATH   =/opt/intel/compilers_and_libraries_2018.3.222/linux/mkl/lib/intel64/
BLAS       =-L$(MKL_PATH) -lmkl_intel_lp64 -lmkl_sequential -lmkl_core -lpthread
LAPACK     =-L$(MKL_PATH) -lmkl_intel_lp64 -lmkl_sequential -lmkl_core -lpthread
BLACS      =-L$(MKL_PATH) -lmkl_blacs_intelmpi_lp64
SCALAPACK  = $(MKL_PATH)/libmkl_scalapack_lp64.a $(BLACS)

编译结果

[cndaqiang@managernode bin]$ ldd vasp_std 
	linux-vdso.so.1 =>  (0x00007ffe978fd000)
	libmkl_intel_lp64.so => /opt/intel/compilers_and_libraries_2018.3.222/linux/mkl/lib/intel64_lin/libmkl_intel_lp64.so (0x00002b38d432e000)
	libmkl_cdft_core.so => /opt/intel/compilers_and_libraries_2018.3.222/linux/mkl/lib/intel64_lin/libmkl_cdft_core.so (0x00002b38d4e41000)
	libmkl_scalapack_lp64.so => 

工具

  • readelf -c fun.a查看静态库包含的函数模块
  • objdump -t fun.so查看静态库内容
  • ar -x fun.a解压静态库为.o文件
(python37) cndaqiang@mommint:/tmp$ !cat
cat fun.f90
SUBROUTINE cndaqiang_fun()

END SUBROUTINE

MODULE cndaqiang_mod
        INTEGER :: cndaqiang_i
        REAL :: cndaqiang_r
        CONTAINS
                SUBROUTINE cndaqiang_mfun()
                END SUBROUTINE
END MODULE
#编译
(python37) cndaqiang@mommint:/tmp$ gfortran -c fun.f90 -o fun.o
#打包静态库
(python37) cndaqiang@mommint:/tmp$ ar rv fun.a fun.o
r - fun.o
#查看静态库包含的内容
(python37) cndaqiang@mommint:/tmp$ readelf -c fun.a
Index of archive fun.a: (4 entries, 0x72 bytes in the symbol table)
Contents of binary fun.a(fun.o) at offset 0xca
	__cndaqiang_mod_MOD_cndaqiang_i
	__cndaqiang_mod_MOD_cndaqiang_r
	__cndaqiang_mod_MOD_cndaqiang_mfun
	cndaqiang_fun_
(python37) cndaqiang@mommint:/tmp$ gfortran fun.o -shared -fPIC -o fun.so
(python37) cndaqiang@mommint:/tmp$ readelf -c fun.so
readelf: Error: File fun.so is not an archive so its index cannot be displayed.
(python37) cndaqiang@mommint:/tmp$ objdump -t fun.so

fun.so:     file format elf64-x86-64

SYMBOL TABLE:
00000000000001c8 l    d  .note.gnu.build-id	0000000000000000 .note.gnu.build-id
00000000000001f0 l    d  .gnu.hash	0000000000000000 .gnu.hash
0000000000000238 l    d  .dynsym	0000000000000000 .dynsym
0000000000000388 l    d  .dynstr	0000000000000000 .dynstr
0000000000000478 l    d  .rela.dyn	0000000000000000 .rela.dyn
0000000000000520 l    d  .init	0000000000000000 .init
0000000000000540 l    d  .plt	0000000000000000 .plt
0000000000000550 l    d  .plt.got	0000000000000000 .plt.got
0000000000000560 l    d  .text	0000000000000000 .text
0000000000000648 l    d  .fini	0000000000000000 .fini
0000000000000654 l    d  .eh_frame_hdr	0000000000000000 .eh_frame_hdr
0000000000000680 l    d  .eh_frame	0000000000000000 .eh_frame
0000000000200e80 l    d  .init_array	0000000000000000 .init_array
0000000000200e88 l    d  .fini_array	0000000000000000 .fini_array
0000000000200e90 l    d  .dynamic	0000000000000000 .dynamic
0000000000200fe0 l    d  .got	0000000000000000 .got
0000000000201000 l    d  .got.plt	0000000000000000 .got.plt
0000000000201018 l    d  .data	0000000000000000 .data
0000000000201020 l    d  .bss	0000000000000000 .bss
0000000000000000 l    d  .comment	0000000000000000 .comment
0000000000000000 l    df *ABS*	0000000000000000 crtstuff.c
0000000000000560 l     F .text	0000000000000000 deregister_tm_clones
00000000000005a0 l     F .text	0000000000000000 register_tm_clones
00000000000005f0 l     F .text	0000000000000000 __do_global_dtors_aux
0000000000201020 l     O .bss	0000000000000001 completed.7698
0000000000200e88 l     O .fini_array	0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000000630 l     F .text	0000000000000000 frame_dummy
0000000000200e80 l     O .init_array	0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l    df *ABS*	0000000000000000 fun.f90
0000000000000000 l    df *ABS*	0000000000000000 crtstuff.c
0000000000000718 l     O .eh_frame	0000000000000000 __FRAME_END__
0000000000000000 l    df *ABS*	0000000000000000
0000000000200e90 l     O .dynamic	0000000000000000 _DYNAMIC
0000000000201020 l     O .data	0000000000000000 __TMC_END__
0000000000201018 l     O .data	0000000000000000 __dso_handle
0000000000000654 l       .eh_frame_hdr	0000000000000000 __GNU_EH_FRAME_HDR
0000000000201000 l     O .got.plt	0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000  w      *UND*	0000000000000000 __cxa_finalize
0000000000000641 g     F .text	0000000000000007 cndaqiang_fun_
0000000000201028 g     O .bss	0000000000000004 __cndaqiang_mod_MOD_cndaqiang_r
0000000000000520 g     F .init	0000000000000000 _init
0000000000000000  w      *UND*	0000000000000000 _ITM_registerTMCloneTable
0000000000201024 g     O .bss	0000000000000004 __cndaqiang_mod_MOD_cndaqiang_i
0000000000000000  w      *UND*	0000000000000000 _ITM_deregisterTMCloneTable
0000000000201020 g       .bss	0000000000000000 __bss_start
0000000000000648 g     F .fini	0000000000000000 _fini
0000000000201020 g       .data	0000000000000000 _edata
0000000000201030 g       .bss	0000000000000000 _end
000000000000063a g     F .text	0000000000000007 __cndaqiang_mod_MOD_cndaqiang_mfun
0000000000000000  w      *UND*	0000000000000000 __gmon_start__

本文首发于我的博客@cndaqiang.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!


目录

访客数据