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

Slides:



Advertisements
Similar presentations
C enter of C omputational C hemistry 并行计算机与并行计算 张鑫 理论与计算化学国际合作研究中心 分子反应动力学国家重点实验室.
Advertisements

Linux 环境及 Shell 程序 操作系统实验 1. 二、 Shell 编程与进程通信 常用 shell 命令 文件及文件属性操作 ls 、 cp 、 mv 、 rm ln 、 ln –s 、 chmod 、 groupadd 、 useradd 输入输出操作 echo 、 cat >> 、
Unix 指令4.
基础模块 模块一 程序设计基础 (一)开发环境部分.
C语言程序设计 主讲教师 :张群燕 电话:
ARM 嵌入式系统 第七章 嵌入式Linux.
培养目标 1.建立基本的程序设计概念体系,掌握基础程序设计方法。
第1单元 操作系统概论 第一节 绪论 操作系统定义.
第 2 章 初探 C++.
行程(process).
Linux并行计算平台搭建及应用 王彦棡 2010年5月.
技术支持部 张新凤( ) 并行机群系统安装、使用和管理 技术支持部 张新凤( )
第一章 C语言概述 计算机公共教学部.
操作系统原理 Principles of Operating System
第1讲 实验环境.
LINUX 环境下程序开发基础 曙光用户培训课程系列 课程时间:1.5小时 更新日期:2008年3月.
補充: Input from a text file
基于操作系统的编程复习 张玉宏
武汉测地所 现场集群环境介绍 TC3600刀片服务器产品 扩展方案讨论.
Linux Further.
MPI并行程序设计简介 曙光信息产业(北京)有限公司 2018年11月.
南京天石软件技术有限公司 陈锺 (QQ: Solaris 10 C编程 南京天石软件技术有限公司 陈锺 (QQ:
高级语言程序设计 主讲人:陈玉华.
Linux环境下程序编译 曙光信息产业(北京)有限公司.
Compilers Flex & Bison 的安裝使用
chapter 1-Introduction
并行计算实验上机 国家高性能计算中心(合肥).
并行算法实践.
核探测与核电子学国家重点实验室 报告人:董磊 指导老师:宋克柱
第1章 Fortran概述 作为一门诞生于上个世纪50年代后半期的高级计算机语言,Fortran在这个C/C++、Java等新兴语言大行其道的时代仍然活跃在人们的视野之中。Fortran语言的长项在于数值计算,在科学研究和工程设计领域有着广泛的用途。在描述数学语言的自然性方面,Fortran同现存的其他高级语言相比有着明显的优势。对于科研工作者和工程技术人员而言,Fortran语言的易学性和易用性是公认的。
編譯環境介紹.
张吉豫 GNU编译工具链使用简介 张吉豫
第四讲 MPI并行程序设计 课程网站:CourseGrading buaa.edu.cn 主讲教师: 赵长海
编译与多文件.
第 5 章、連結與載入 作者:陳鍾誠 旗標出版社.
曙光集群简明使用手册 技术支持中心.
Linux基本操作 程設實習課 ( 2/25 ).
第1章、系統軟體 作者:陳鍾誠 旗標出版社.
Linux操作系统分析 中国科学技术大学计算机系 陈香兰(0512- )
并行计算简介 高性能事业部:曹振南 年4月.
Linux核心編譯與模組管理 2013/01/19.
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
2017 Operating Systems 作業系統實習 助教:陳主恩、林欣穎 實驗室:720A Lab3.
东软集团(大连)有限公司 SVVD事业部-于忠华
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
C++ 程式設計 基礎篇 張啟中 Chang Chi-Chung.
Name1..hour //加班時數 name2..hour //請假時數
C语言大学实用教程 第5章 函数与程序结构 西南财经大学经济信息工程学院 刘家芬
2018 Operating Systems 作業系統實習 助教:林欣穎 實驗室:720A Lab3.
Intel Compiler 安裝.
C语言环境配置.
第一章 程序设计和C语言 主讲人:高晓娟 计算机学院.
C语言程序示例: 1.输入10个数,按从小到大的顺序排序。 2.汉诺塔问题。.
OpenMP程序设计 2019/4/25.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
C程序设计.
<编程达人入门课程> 本节内容 字符与字符串 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
本节内容 字符与字符串 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第一章 C语言概述 教师:周芸.
项目1 C程序设计起步 学习目标: 通过该项目你可以知道: C语言的用途。 C语言的基本符号和关键字。 C语言程序的结构及特点。
第一章 C语言概述 目录 什么是语言、程序 C语言的历史与发展 C语言的书写形式与程序结构 运行C语言的步骤与方法
第二章 类型、对象、运算符和表达式.
第1章程序设计和C语言.
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
第6章 嵌入式软件开发基础.
嵌入式Linux编程环境.
Fortran 实用编程 Fortran Coder 研讨团队 系列视频教程
劉庠宏、林合治編著 國立高雄大學應用數學系 2005年3月1日
隨機函數.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

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

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

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

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

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

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

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

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

源代码后缀规范 在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

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

最简单的例子(续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

最简单的例子(续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

最简单的例子(续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

多个源文件的例子 主程序源文件 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); }

多个源文件的例子(续) 多个源文件同时编译 源文件分别编译,再将目标文件连接成可执行文件 $>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

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

指定头文件(举例) 编译时使用 -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

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

静态库的生成 编译子函数源代码 使用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

动态库的生成 编译子函数源代码,必须要使用 -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

库函数的使用 生成的库函数可以直接使用,连接时提供即可,可以通过两种方式: 方式一:连接时,直接使用库函数路径 主函数 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

库函数的使用(续) 方式二:使用编译器的 -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

程序运行时动态库的搜索路径 可执行程序运行时,动态链接的函数库需要从磁盘载入内存,动态库同样有搜索路径 搜索路径优先级从高到低: 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)

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

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

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

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

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

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

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!

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

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

常见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

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; }

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

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 (0x00002b7398893000) 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 (0x00002b7399122000) /lib64/ld-linux-x86-64.so.2 (0x00002b7397cd6000)

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

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个进程)

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

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

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

一个简单的例子 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

一个简单的例子(续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

一个简单的例子(续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

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)

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)

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)

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

谢谢!