Presentation is loading. Please wait.

Presentation is loading. Please wait.

Linux下的程序编译和运行 曙光信息产业股份有限公司.

Similar presentations


Presentation on theme: "Linux下的程序编译和运行 曙光信息产业股份有限公司."— Presentation transcript:

1 Linux下的程序编译和运行 曙光信息产业股份有限公司

2 提纲 Linux下常用编译器介绍 串行程序编译和执行 OpenMP多线程并行程序 MPI并行程序 Make工具介绍

3 GNU编译器 GCC(GNU Compiler Collection,GNU编译器套件),是一套由GNU开发的编程语言编译器。它是一套以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,亦是自由的类Unix及苹果电脑Mac OS X 操作系统的标准编译器。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。 GCC可处理C、C++、Fortran、Pascal、Objective-C、Java,以及Ada与其他语言。 编程语言 编译器调用名称 C gcc C++ g++ Fortran77 gfortran Fortran90/95

4 Intel编译器 Intel编译器是Intel公司发布的一款x86平台(i386/x86_64)编译器产品,支持C/C++/Fortran编程语言。 Intel编译器针对Intel处理器进行了专门优化,性能优异,在AMD处理器平台上表现同样出色。 编程语言 编译器调用名称 C icc C++ icpc Fortran77 ifort Fortran90/95

5 PGI编译器 PGI编译器是The Portland Group推出的一款编译器产品,支持C、C++和Fortran,在AMD处理器平台上 性能较好。 此外,PGI编译器还提供对HPF(High Performance Fortran,Fortran90的扩展)编程语言的支持 编程语言 编译器调用名称 C pgcc C++ pgCC Fortran77 pgf77 Fortran90/95 pgf90/pgf95 HPF pghpf

6 其它x86编译器 Open64(C、C++、Fortran77/90/95)
PathScale (C、C++、Fortran77/90/95) Absoft Fortran Compiler g95 Fortran Compiler Lahey Fortran Compiler ...

7 提纲 Linux下常用编译器介绍 串行程序 OpenMP多线程并行程序 MPI并行程序 Make工具介绍

8 程序编译流程 源文件 source 编译 目标文件 object 连接 可执行文件 exe 执行

9 源代码后缀规范 在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。
而源代码、目标文件等后缀名有统一的规范 文件类型 后缀名 C source .c C++ source .C, .cc, .cpp, .cxx, .c++ Fortran77 source .f, .for Fortran90/95 source .f90 汇编source .s 目标文件 .o 头文件 .h Fortran90/95模块文件 .mod 动态链接库 .so 静态链接库 .a

10 最简单的例子 hello.c源文件(可用vim等文本编辑器编辑) #include <stdio.h> void main()
{ printf("Hello world.\n"); } 注1:本文都以C语言示例,C++、Fortran等的编译运行流程与C语言类似 注2:示例使用的编译器为gcc,其它编译器的使用方法类似,请参见相关文档或man手册

11 最简单的例子(续1) 调用gcc编译源代码,默认在当前目录下生成可执行文件a.out 运行可执行文件
$>gcc hello.c #如果hello.c不在当前目录,需要输入其路径 $>file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.4, dynamically linked (uses shared libs), not stripped 运行可执行文件 可以在终端中输入可执行文件的相对或绝对路径: $>./a.out Hello world. $>/home/test/a.out 如果可执行文件所在目录加入了PATH环境变量,可以直接使用可执行文件名 $>export PATH=$PATH:/home/test $>a.out

12 最简单的例子(续2) 编译时,指定生成可执行文件的路径或文件名(-o参数) $>gcc -o hello hello.c
$>file hello hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.4, dynamically linked (uses shared libs), not stripped $>gcc -o /home/test/hello hello.c

13 最简单的例子(续3) 前面的例子中,gcc自动执行了编译和连接操作,这两步可以分开进行: 只执行编译,不执行连接(-c参数)
$>gcc -c hello.c 生成目标文件hello.o $>file hello.o hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped 连接目标文件,生成可执行文件(编译器实际是调用系统的ld连接器) $>gcc -o hello hello.o $>file hello hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.4, dynamically linked (uses shared libs), not stripped

14 多个源文件的例子 主程序源文件 main.c #include <stdio.h> int main() {
int sum=0,r,i; for(i=1;i<=10;i++) r=fx(i); sum=sum+r; } printf("sum is %d\n",sum); 子函数源文件 fx.c int fx(int x) { int result; result=x*x; return(result); }

15 多个源文件的例子(续) 多个源文件同时编译 源文件分别编译,再将目标文件连接成可执行文件
$>gcc -o sum main.c fx.c 生成可执行文件sum $>./sum sum is 385 源文件分别编译,再将目标文件连接成可执行文件 $>gcc -c main.c $>gcc -c fx.c $>gcc -o sum main.o fx.o

16 指定头文件 源文件中如果包含了头文件,编译器会在自动在一些头文件目录中搜索 编译器默认搜索的头文件目录一般包括(优先级由高到低):
源文件所在目录(要求源文件中用#include "..."格式指定) INCLUDE之类环境变量指定的目录 编译器自己的头文件目录 /usr/include系统头文件目录 如果想自定义头文件搜索路径,可以使用 -I<path> 参数,用 -I 指定的目录优先级高于默认搜索路径 -I参数也可以指定多个:-I<path1> -I<path2> ...

17 指定头文件(举例) 编译时使用 -I 参数指定头文件搜索路径 $>gcc -c -I/home/test/include main.c
源文件 main.c #include <stdio.h> #include "myhead.h" int main() { printf(STRING); } 头文件 /home/test/include/myhead.h #define STRING "Hello World.\n" 编译时使用 -I 参数指定头文件搜索路径 $>gcc -c -I/home/test/include main.c $>gcc -o program main.o 或者 $>gcc -o program -I/home/test/include main.c

18 Linux下的函数库文件 Linux下的函数库分为静态库和动态库: 静态库 命名规范为libXXX.a
编译后库函数会被连接进可执行程序,可执行文件体积较大 可执行文件运行时,不需要从磁盘载入库函数,执行效率较高 库函数更新后,需要重新编译程序 动态库 命名规范为libXXX.so 编译后库函数不会被连接进可执行程序,可执行文件体积较小 可执行文件运行时,库函数动态载入 使用灵活,库函数更新后,不需要重新编译程序

19 静态库的生成 编译子函数源代码 使用ar命令将目标文件打包成静态库.a $>gcc -c fun1.c
int fun1(int i) { return(i+i); } 子函数 fun2.c int fun2(int i) { return(i*i); } 编译子函数源代码 $>gcc -c fun1.c $>gcc -c fun2.c 使用ar命令将目标文件打包成静态库.a $>ar cr libtest.a fun1.o fun2.o

20 动态库的生成 编译子函数源代码,必须要使用 -fPIC 参数 使用编译器-shared参数将目标文件连接成动态库.so
子函数 fun1.c int fun1(int i) { return(i+i); } 子函数 fun2.c int fun2(int i) { return(i*i); } 编译子函数源代码,必须要使用 -fPIC 参数 $>gcc -c -fPIC fun1.c $>gcc -c -fPIC fun2.c 使用编译器-shared参数将目标文件连接成动态库.so $>gcc -o libtest.so -shared fun1.o fun2.o

21 库函数的使用 生成的库函数可以直接使用,连接时提供即可,可以通过两种方式: 方式一:连接时,直接使用库函数路径
主函数 main.c,会调用库函数中的子程序 #include <stdio.h> int main() { int i=10, sum, product; sum=fun1(i); product=fun2(i); printf("the sum is %d, the product is %d\n",sum,product); } 生成的库函数可以直接使用,连接时提供即可,可以通过两种方式: 方式一:连接时,直接使用库函数路径 $>gcc -c main.c $>gcc -o program main.o /home/test/lib64/libtest.a 或者 $>gcc -o program main.o /home/test/lib64/libtest.so

22 库函数的使用(续) 方式二:使用编译器的 -L<path> -lXXX 参数,表示在指定库函数路径下搜索名为libXXX.so或libXXX.a的库文件 如果在库函数路径下同时有静态库和动态库,会选择动态库 -L可以指定多次,-L<path1> -L<path2> -L指定的搜索路径优先级最高 如果在-L指定的搜索路径中没有找到库函数,或者没有指定-L,编译器还会按优先级从高到低搜索以下路径 LIBRARY_PATH(静态库)、LD_LIBRARY_PATH(动态库)环境变量指定路径 系统配置文件/etc/ld.so.conf中指定的动态库搜索路径 系统的/lib(64)、/usr/lib(64)等库文件目录 $>gcc -c main.c $>gcc -o program main.o -L/home/test/lib64 -ltest

23 程序运行时动态库的搜索路径 可执行程序运行时,动态链接的函数库需要从磁盘载入内存,动态库同样有搜索路径 搜索路径优先级从高到低:
LD_LIBRARY_PATH 环境变量指定的路径 系统配置文件/etc/ld.so.conf中指定的动态库搜索路径 系统的/lib(64)、/usr/lib(64)等库文件目录 $>gcc -c main.c $>gcc -o program main.o -L/home/test/lib64 -ltest $>export LD_LIBRARY_PATH=$ LD_LIBRARY_PATH:/home/test/lib64 $>ldd ./program linux-vdso.so.1 => (0x00007fffd3bff000) libtest.so => /public/users/libin/test/libtest.so (0x00002b0f21f37000) libc.so.6 => /lib64/libc.so.6 (0x00002b0f2216b000) /lib64/ld-linux-x86-64.so.2 (0x00002b0f21d16000)

24 编译优化选项 常用的编译优化选项(以GNU编译器为例) -O1、-O2、-O3,不同优化级别,一般情况下选项 -O2 比较合理
-Os,针对减小执行文件体积优化 -funroll-loops,循环展开优化 -march=native、-msse3等,针对CPU指令集优化 ... Intel、PGI等编译器的优化选项请参考相关文档或man手册 关于编译优化 编译优化需适度,过多的优化参数反而可能会降低运行速度 过于激进的优化,会降低程序数值精度,导致计算结果不正确 对于编写不规范的代码,过高的优化级别,可能导致程序运行错误

25 编译调试选项 常用的一些编译调试选项(以GNU编译器为例) -Wall,打开编译告警,有助于发现代码不规范的地方和潜在错误
-g,用于产生调试信息,供gdb等调试器使用,-g会自动禁止部分编译优化 -pg,用于产生profile信息,供gprof等profile工具使用

26 提纲 Linux下常用编译器介绍 串行程序 OpenMP多线程并行程序 MPI并行程序 Make工具介绍

27 OpenMP简介 针对共享式内存的多线程并行编程标准 支持C、C++、Fortran等编程语言
编译制导( Compiler Directive )型,通常由编译器提供支持 主线程 并行执行区域

28 OpenMP程序示例 OpenMP示例程序hello.c #include <omp.h>
#include <stdio.h> int main (int argc, char *argv[]) { #pragma omp parallel printf("Hello World!"); }

29 OpenMP程序的编译 OpenMP通过编译器支持,编译时打开OpenMP编译选项即可 常用编译器的OpenMP编译选项:
GNU Intel PGI OpenMP编译选项 -fopenmp -openmp -mp 以GNU编译器为例: $>gcc -o hello -fopenmp hello.c

30 OpenMP程序的运行 编译 通过环境变量指定线程数运行 $>gcc -o hello -fopenmp hello.c
$>ldd hello linux-vdso.so.1 => (0x00007fff789ff000) libgomp.so.1 => /usr/lib64/libgomp.so.1 (0x00002b3e5ab8e000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b3e5ad97000) libc.so.6 => /lib64/libc.so.6 (0x00002b3e5afb4000) librt.so.1 => /lib64/librt.so.1 (0x00002b3e5b313000) /lib64/ld-linux-x86-64.so.2 (0x00002b3e5a96d000) 通过环境变量指定线程数运行 $>export OMP_NUM_THREADS=2 $>./hello 或者 $> OMP_NUM_THREADS=2 ./hello Hello World!

31 提纲 Linux下常用编译器介绍 串行程序 OpenMP多线程并行程序 MPI并行程序 Make工具介绍

32 MPI简介 Message Passing Interface(MPI)是消息传递函数库的标准规范,支持Fortran和C、C++

33 常见MPI实现 注:请区分 OpenMPI 与 OpenMP MPI实现 支持的网络种类 兼容的MPI规范 MPICH Ethernet
Ethernet、InfiniBand MVAPICH MPI-1.1,部分MPI-2 MVAPICH2 Intel MPI HP-MPI MPI-1.1,MPI-2 注:请区分 OpenMPI 与 OpenMP

34 MPI 程序示例 MPI示例程序 hello.c #include <stdio.h>
#include <mpi.h> int main (argc, argv) int argc; char *argv[]; { int rank, size; MPI_Init(&argc, &argv); /* start MPI */ MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* get process id */ MPI_Comm_size(MPI_COMM_WORLD, &size); /* get NO. of processes */ printf("Hello world from process %d of %d\n", rank, size); MPI_Finalize(); /* stop MPI */ return 0; }

35 MPI程序的编译 常见MPI实现都会提供相应的编译命令:
编程语言 C C++ Fortran77 Fortran90/95 编译器调用 mpicc mpicxx mpif77 mpif90 这些编译命令只是一层封装,实际会调用系统编译器,编译时自动添加MPI头文件搜索路径,连接时自动连接MPI库文件,方便使用 以 OpenMPI 的 mpicc为例: $>mpicc -show gcc -I/public/software/openmpi-gnu/include -pthread -L/public/software/openmpi-gnu/lib -lmpi -lopen-rte -lopen-pal -ldl -Wl,--export-dynamic -lnsl -lutil -lm -ldl

36 MPI程序的编译(举例) 使用 OpenMPI 的 mpicc 编译示例程序hello.c
$>mpicc -o hello hello.c $>ldd hello linux-vdso.so.1 => (0x00007fff20dff000) libmpi.so.0 => /public/software/openmpi-gnu/lib/libmpi.so.0 (0x00002b7397ef7000) libopen-rte.so.0 => /public/software/openmpi-gnu/lib/libopen-rte.so.0 (0x00002b739819c000) libopen-pal.so.0 => /public/software/openmpi-gnu/lib/libopen-pal.so.0 (0x00002b73983e8000) libdl.so.2 => /lib64/libdl.so.2 (0x00002b739868f000) libnsl.so.1 => /lib64/libnsl.so.1 (0x00002b ) libutil.so.1 => /lib64/libutil.so.1 (0x00002b7398aab000) libm.so.6 => /lib64/libm.so.6 (0x00002b7398caf000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b7398f05000) libc.so.6 => /lib64/libc.so.6 (0x00002b ) /lib64/ld-linux-x86-64.so.2 (0x00002b7397cd6000)

37 MPI程序的运行 常见MPI实现都会提供MPI程序的启动器,不同MPI实现的启动器名称和语法略有不同: MPI实现 启动器名 指定进程数
指定运行节点 MPICH mpirun -np -machinefile OpenMPI -hostfile MVAPICH mpirun_rsh MPICH2 MVAPICH2 Intel MPI mpiexec.hydra -n -f

38 MPI程序的运行(举例) 以OpenMPI为例,在单节点运行示例MPI程序: $>mpirun -np 4 ./hello 多节点运行
Hello world from process 2 of 4 Hello world from process 0 of 4 Hello world from process 1 of 4 Hello world from process 3 of 4 多节点运行 $>mpirun -np 4 -machinefile ./hosts.list ./hello $>cat hosts.list node1 node2 (本例中,启动4个进程运行hello程序,每个节点2个进程)

39 提纲 Linux下常用编译器介绍 串行程序 OpenMP多线程并行程序 MPI并行程序 Make工具介绍

40 Make简介 在开发大系统时,经常要将程序划分为许多模块。各个模块之间存在着各种各样的依赖关系,在Linux中通常使用 Makefile来管理。 由于各个模块间不可避免存在关联,所以当一个模块改动后,其他模块也许会有所更新,对小系统来说,手工编译连接是没问题,但是如果是一个大系统,存在很多个模块,那么手工编译的方法就不适用了 为此,在Linux系统中,专门提供了make工具来自动维护目标文件。 与手工编译和连接相比,make命令的优点在于他只更新修改过的文件,而对没修改的文件则置之不理,并且make命令不会漏掉一个需要更新的文件。

41 Makefile文件 Makefile由规则组成,每一条规则都有三部分组成: 目标(object) 依赖(dependency)
命令(commands) object : dependency \t commands #注意:行首这里是一个tab,不是空格! 依赖可以是另一条规则的目标,也可以是文件 如果目标是一个文件,当它的依赖比目标新时,则运行规则所包含的命令来更新目标

42 一个简单的例子 a.c和b.c两个源文件 Makefile文件内容
extern void p(char *); main() { p(“hello world!"); } b.c #include <stdio.h> void p(char *str) { printf(“%s\n",str); } Makefile文件内容 hello: a.c b.c gcc a.c b.c -o hello 执行make,会查找Makefile,执行gcc a.c b.c -o hello $>make

43 一个简单的例子(续1) 将上例中的Makefile改造:
hello: a.o b.o gcc a.o b.o -o hello a.o: a.c gcc –c a.c b.o: b.c gcc -c b.c 运行make时,可以接一目标名(eg. make hello)作为参数,表示要处理改目标。如没有参数,则处理第一个目标 对上述例子执行make,处理的是hello这个目标 编译完成后,如果更新b.c源文件,执行make,只重新编译b.c,不会重新编译a.c

44 一个简单的例子(续2) Makefile还可以处理其它的非编译任务 我们继续更新上例的Makefile文件:
hello: a.o b.o gcc a.o b.o -o hello a.o: a.c gcc –c a.c b.o: b.c gcc -c b.c clean: rm -rf *.o hello 执行make clean,将删除目标文件和可执行文件 $>make clean

45 Makefile中的宏变量 Makefile中可以定义宏变量,定义后别处可引用。 宏的定义格式为:
宏名 = 宏的值 (宏名一般习惯用大写字母) 宏的引用格式为: $(宏名) 仍然用前面的例子说明,定义了宏的Makefile: EXE = hello OBJS = a.o b.o CC = gcc CFLAGS = -O2 $(EXE): $(OBJS) $(CC) $(OBJS) -o $(EXE) a.o: a.c $(CC) $(CFLAGS) –c a.c b.o: b.c $(CC) $(CFLAGS) -c b.c clean: rm -rf *.o $(EXE)

46 Makefile中的宏变量(续) make还有一些内部宏变量,它们根据每一个规则内容定义。 对我们的Makefile继续改造:
当前规则的目的文件名 $< 依赖列表中的第一个依赖 $^ 整个依赖列表(除掉了里面的所有重复内容)。 $? 依赖列表中新于目标的 对我们的Makefile继续改造: EXE = hello OBJS = a.o b.o CC = gcc CFLAGS = -O2 $(EXE): $(OBJS) $(CC) $^ -o a.o: a.c $(CC) $(CFLAGS) –c $< b.o: b.c $(CC) $(CFLAGS) -c $< clean: rm -rf *.o $(EXE)

47 Makefile中的隐含规则 上面例子中,从.c产生.o的步骤是完全一样的,Makefile能否简化? 用make的隐含规则就可以处理!
EXE = hello OBJS = a.o b.o CC = gcc CFLAGS = -O2 $(EXE): $(OBJS) $(CC) $^ -o .c.o: $(CC) $(CFLAGS) –c $< clean: rm -rf *.o $(EXE)

48 Make进阶 关于make的其它语法和高级功能,可以参考其它资料,或者查阅GNU Make的在线手册: $>man make
$>man makefile

49 谢谢!


Download ppt "Linux下的程序编译和运行 曙光信息产业股份有限公司."

Similar presentations


Ads by Google