鸟哥的Linux私房菜
  • 目录及概述
  • 第零章 计算机概论
    • 0.1 电脑-辅助人脑的好工具
    • 0.2 个人电脑架构与相关设备元件
    • 0.3 数据表示方式
    • 0.4 软件程序运行
    • 0.5 重点回顾
    • 0.6 本章习题
    • 0.7 参考资料与延伸阅读
  • 第一章 Linux是什么与如何学习
    • 1.1 Linux是什么
    • 1.2 Torvalds的Linux发展
    • 1.3 Linux当前应用的角色
    • 1.4 Linux该如何学习
    • 1.5 重点回顾
    • 1.6 本章习题
    • 1.7 参考资料与延伸阅读
  • 第二章 主机规划与磁盘分区
    • 2.1 Linux与硬件的搭配
    • 2.2 磁盘分区
    • 2.3 安装Linux前的规划
    • 2.4 重点回顾
    • 2.5 本章习题
    • 2.6 参考资料与延伸阅读
  • 第三章 安装CentOS7.X
    • 3.1 本练习机的规划-尤其是分区参数
    • 3.2 开始安装CentOS_7
    • 3.3 多重开机安装流程与管理
    • 3.4 重点回顾
    • 3.5 本章习题
    • 3.6 参考资料与延伸阅读
  • 第四章 首次登陆与线上求助
    • 4.1 首次登录系统
    • 4.2 文字模式下指令的下达
    • 4.3 Linux系统的线上求助man_page与info_page
    • 4.4 超简单文字编辑器-nano
    • 4.5 正确的关机方法
    • 4.6 重点回顾
    • 4.7 本章习题
    • 4.8 参考资料与延伸阅读
  • 第五章 Linux的文件权限与目录配置
    • 5.1 使用者与群组
    • 5.2 Linux文件权限概念
    • 5.3 Linux目录配置
    • 5.4 重点回顾
    • 5.5 本章习题
    • 5.6 参考资料与延伸阅读
  • 第六章 Linux文件与目录管理
    • 6.1 目录与路径
    • 6.2 文件与目录管理
    • 6.3 文件内容查阅
    • 6.4 文件与目录的默认权限与隐藏权限
    • 6.5 指令与文件的搜寻
    • 6.6 极重要的复习-权限与指令间的关系
    • 6.7 重点回顾
    • 6.8 本章习题
    • 6.9 参考资料与延伸阅读
  • 第七章 Linux磁盘与文件系统管理
    • 7.1 认识Linux文件系统
    • 7.2 文件系统的简单操作
    • 7.3 磁盘的分区-格式化-检验与挂载
    • 7.4 设置开机挂载
    • 7.5 内存交换空间swap创建
    • 7.6 文件系统的特殊观察与操作
    • 7.7 重点回顾
    • 7.8 本章习题
    • 7.9 参考资料与延伸阅读
  • 第八章 文件的压缩与打包
    • 8.1 压缩文件的用途与技术
    • 8.2 Linux系统常见的压缩指令
    • 8.3 打包指令-tar
    • 8.4 XFS文件系统的备份与还原
    • 8.5 光盘写入工具
    • 8.6 其他常见的压缩与备份工具
    • 8.7 重点回顾
    • 8.8 本章习题
    • 8.9 参考资料与延伸阅读
  • 第九章 vim程序编辑器
    • 9.1 vi与vim
    • 9.2 vi的使用
    • 9.3 vim的额外功能
    • 9.4 其他vim使用注意事项
    • 9.5 重点回顾
    • 9.6 本章习题
    • 9.7 参考资料与延伸阅读
  • 第十章 认识与学习BASH
    • 10.1 认识bash这个shell
    • 10.2 shell的变量功能
    • 10.3 命令别名与历史命令
    • 10.4 BashShell的操作环境
    • 10.5 数据流重导向
    • 10.6 管线命令-pipe
    • 10.7 重点回顾
    • 10.8 本章习题
    • 10.9 参考资料与延伸阅读
  • 第十一章 正则表达式与文件格式化处理
    • 11.1 开始之前-什么是正则表达式
    • 11.2 基础正则表达式
    • 11.3 延伸正则表达式
    • 11.4 文件的格式化与相关处理
    • 11.5 重点回顾
    • 11.6 本章习题
    • 11.7 参考资料与延伸阅读
  • 第十二章 学习shell scripts
    • 12.1 什么是ShellScripts
    • 12.2 简单的ShellScript练习
    • 12.3 善用判断式
    • 12.4 条件判断式
    • 12.5 循环loop
    • 12.6 ShellScript的追踪与debug
    • 12.7 重点回顾
    • 12.8 本章习题
  • 第十三章 Linux帐号管理与ACL权限控制
    • 13.1 Linux的账户与群组
    • 13.2 账号管理
    • 13.3 主机的细部权限规划-ACL的使用
    • 13.4 使用者身份切换
    • 13.5 使用者的特殊shell和PAM模块
    • 13.6 Linux主机上的使用者讯息传递
    • 13.7 CentOS7环境下大量创建账号的方法
    • 13.8 重点回顾
    • 13.9 本章习题
    • 13.10 参考资料与延伸阅读
  • 第十四章 磁盘配额(Quota)与进阶文件系统管理
    • 14.1 磁盘配额Quota的应用与实作
    • 14.2 软件磁盘阵列SoftwareRAID
    • 14.3 逻辑卷轴管理员LogicalVolumeManager
    • 14.4 重点回顾
    • 14.5 本章习题
    • 14.6 参考资料与延伸阅读
  • 第十五章 例行性工作调度(crontab)
    • 15.1 什么是例行性工作调度
    • 15.2 仅执行一次的工作调度
    • 15.3 循环执行的例行性工作调度
    • 15.4 可唤醒停机期间的工作任务
    • 15.5 重点回顾
    • 15.6 本章习题
  • 第十六章 程序管理与SELinux初探
    • 16.1 什么是程序process
    • 16.2 工作管理JobControl
    • 16.3 程序管理
    • 16.4 特殊文件与程序
    • 16.5 SELinux初探
    • 16.6 重点回顾
    • 16.7 本章习题
    • 16.8 参考资料与延伸阅读
  • 第十七章 认识系统服务(daemon)
    • 17.1 什么是daemon与服务service
    • 17.2 通过systemctl管理服务
    • 17.3 systemctl针对service类型的配置文件
    • 17.4 systemctl针对timer的配置文件
    • 17.5 CentOS7.x默认启动的服务简易说明
    • 17.6 重点回顾
    • 17.7 本章习题
    • 17.8 参考资料与延伸阅读
  • 第十八章 认识与分析登录文件
    • 18.1 什么是登录文件
    • 18.2 rsyslog.service-记录登录文件的服务
    • 18.3 登录文件的轮替logrotate
    • 18.4 systemd-journald.service简介
    • 18.5 分析登录文件
    • 18.6 重点回顾
    • 18.7 本章习题
    • 18.8 参考资料与延伸阅读
  • 第十九章 开机流程、模块管理与Loader
    • 19.1 Linux的开机流程分析
    • 19.2 核心与核心模块
    • 19.3 BootLoader-Grub2
    • 19.4 开机过程的问题解决
    • 19.5 重点回顾
    • 19.6 本章习题
    • 19.7 参考资料与延伸阅读
  • 第二十章 网络设置与备份策略
    • 20.1 系统基本设置
    • 20.2 服务器硬件数据的收集
    • 20.3 备份要点
    • 20.4 备份的种类和频率与工具的选择
    • 20.5 鸟哥的备份策略
    • 20.6 灾难复原的考虑
    • 20.7 重点回顾
    • 20.8 本章习题
    • 20.9 参考资料与延伸阅读
  • 第二十一章 软件安装:源代码与Tarball
    • 21.1 开源代码的软件安装与升级简介
    • 21.2 使用传统程序语言进行编译的简单范例
    • 21.3 用make进行宏编译
    • 21.4 Tarball的管理与建议
    • 21.5 函数库管理
    • 21.6 检验软件正确性
    • 21.7 重点回顾
    • 21.8 本章习题
    • 21.9 参考资料与延伸阅读
  • 第二十二章 软件安装:RPM,SRPM与YUM功能
    • 22.1 软件管理员简介
    • 22.2 RPM软件管理程序-rpm
    • 22.3 YUM线上升级机制
    • 22.4 SRPM的使用-rpmbuild
    • 22.5 重点回顾
    • 22.6 本章习题
    • 22.7 参考资料与延伸阅读
  • 第二十三章 X Window设置介绍
    • 23.1 什么是XWindowSystem
    • 23.2 XServer配置文件解析与设置
    • 23.3 显卡驱动程序安装范例
    • 23.4 重点回顾
    • 23.5 本章习题
    • 23.6 参考资料与延伸阅读
  • 第二十四章 核心编译
    • 24.1 编译前的任务-认识核心与取得核心源代码
    • 24.2 核心编译的前处理与核心功能选择
    • 24.3 核心的编译与安装
    • 24.4 额外-单一核心模块编译
    • 24.5 以最新核心版本编译CentOS7.x的核心
    • 24.6 重点回顾
    • 24.7 本章习题
    • 24.8 参考资料与延伸阅读
Powered by GitBook
On this page
  • 21.2.1 单一程序:印出 Hello World
  • 21.2.2 主、副程序链接:副程序的编译
  • 21.2.3 调用外部函数库:加入链接的函数库
  • 21.2.4 gcc 的简易用法 (编译、参数与链结)
  1. 第二十一章 软件安装:源代码与Tarball

21.2 使用传统程序语言进行编译的简单范例

Previous21.1 开源代码的软件安装与升级简介Next21.3 用make进行宏编译

Last updated 2 years ago

经过上面的介绍之后,你应该比较清楚的知道源代码、编译器、函数库与可执行文件之间的相关性了。 不过,详细的流程可能还是不很清楚,所以,在这里我们以一个简单的程序范例来说明整个编译的过程喔!赶紧进入 Linux 系统,实地的操作一下下面的范例呢!

21.2.1 单一程序:印出 Hello World

我们以 Linux 上面最常见的 C 语言来撰写第一支程序!第一支程序最常作的就是..... 在屏幕上面印出“Hello World!”的字样~当然, 这里我们是以简单的 C 语言来撰写,如果你对于 C 有兴趣的话,那么请自行购买相关的书籍喔! ^_^ 好了,不啰唆,立刻编辑第一支程序吧!

Tips 请先确认你的 Linux 系统里面已经安装了 gcc 了喔!如果尚未安装 gcc 的话,请先参考下一节的 RPM 安装法,先安装好 gcc 之后,再回来阅读本章。 如果你已经有网络了,那么直接使用“ yum groupinstall "Development Tools" ” 预先安装好所需的所有软件即可。 rpm 与 yum 均会在下一章介绍。

  • 编辑程序码,亦即源代码

[root@study ~]# vim hello.c   <==用 C 语言写的程序扩展名建议用 .c
#include <stdio.h>
int main(void)
{
        printf("Hello World\n");
}

上面是用 C 语言的语法写成的一个程序文件。第一行的那个“ # ”并不是注解喔!如果你担心输入错误, 请到下面的链接下载这个文件:

  • 开始编译与测试执行

[root@study ~]# gcc hello.c
[root@study ~]# ll hello.c a.out
-rwxr-xr-x. 1 root root 8503 Sep  4 11:33 a.out   <==此时会产生这个文件名
-rw-r--r--. 1 root root   71 Sep  4 11:32 hello.c

[root@study ~]# ./a.out
Hello World  <==呵呵!成果出现了!

在默认的状态下,如果我们直接以 gcc 编译源代码,并且没有加上任何参数,则可执行文件的文件名会被自动设置为 a.out 这个文件名称! 所以你就能够直接执行 ./a.out 这个可执行文件啦!上面的例子很简单吧!那个 hello.c 就是源代码,而 gcc 就是编译器,至于 a.out 就是编译成功的可执行 binary program 啰! 咦!那如果我想要产生目标文件 (object file) 来进行其他的动作,而且可执行文件的文件名也不要用默认的 a.out ,那该如何是好?其实你可以将上面的第 2 个步骤改成这样:

[root@study ~]# gcc -c hello.c
[root@study ~]# ll hello*
-rw-r--r--. 1 root root   71 Sep  4 11:32 hello.c
-rw-r--r--. 1 root root 1496 Sep  4 11:34 hello.o  <==就是被产生的目标文件

[root@study ~]# gcc -o hello hello.o
[root@study ~]# ll hello*
-rwxr-xr-x. 1 root root 8503 Sep  4 11:35 hello  <==这就是可可执行文件! -o 的结果
-rw-r--r--. 1 root root   71 Sep  4 11:32 hello.c
-rw-r--r--. 1 root root 1496 Sep  4 11:34 hello.o

[root@study ~]# ./hello
Hello World

这个步骤主要是利用 hello.o 这个目标文件制作出一个名为 hello 的可执行文件,详细的 gcc 语法我们会在后续章节中继续介绍!通过这个动作后,我们可以得到 hello 及 hello.o 两个文件, 真正可以执行的是 hello 这个 binary program 喔! 或许你会觉得,咦!只要一个动作作出 a.out 就好了,干嘛还要先制作目标文件再做成可执行文件呢? 呵呵!通过下个范例,你就可以知道为什么啦!

21.2.2 主、副程序链接:副程序的编译

如果我们在一个主程序里面又调用了另一个副程序呢?这是很常见的一个程序写法, 因为可以简化整个程序的易读性!在下面的例子当中,我们以 thanks.c 这个主程序去调用 thanks_2.c 这个副程序,写法很简单:

  • 撰写所需要的主、副程序

# 1\. 编辑主程序:
[root@study ~]# vim thanks.c
#include <stdio.h>
int main(void)
{
        printf("Hello World\n");
        thanks_2();
}
# 上面的 thanks_2(); 那一行就是调用副程序啦!

[root@study ~]# vim thanks_2.c
#include <stdio.h>
void thanks_2(void)
{
        printf("Thank you!\n");
}

上面这两个文件你可以到下面下载:

  • 进行程序的编译与链接 (Link)

# 2\. 开始将源代码编译成为可执行的 binary file :
[root@study ~]# gcc -c thanks.c thanks_2.c
[root@study ~]# ll thanks*
-rw-r--r--. 1 root root   75 Sep  4 11:43 thanks_2.c
-rw-r--r--. 1 root root 1496 Sep  4 11:43 thanks_2.o  <==编译产生的!
-rw-r--r--. 1 root root   91 Sep  4 11:42 thanks.c
-rw-r--r--. 1 root root 1560 Sep  4 11:43 thanks.o    <==编译产生的!

[root@study ~]# gcc -o thanks thanks.o thanks_2.o
[root@study ~]# ll thanks*
-rwxr-xr-x. 1 root root 8572 Sep  4 11:44 thanks    <==最终结果会产生这玩意儿

# 3\. 执行一下这个文件:
[root@study ~]# ./thanks
Hello World
Thank you!

知道为什么要制作出目标文件了吗?由于我们的源代码文件有时并非仅只有一个文件,所以我们无法直接进行编译。 这个时候就需要先产生目标文件,然后再以链接制作成为 binary 可可执行文件。另外,如果有一天,你更新了 thanks_2.c 这个文件的内容,则你只要重新编译 thanks_2.c 来产生新的 thanks_2.o ,然后再以链接制作出新的 binary 可可执行文件即可!而不必重新编译其他没有更动过的源代码文件。 这对于软件开发者来说,是一个很重要的功能,因为有时候要将偌大的源代码全部编译完成,会花很长的一段时间呢!

此外,如果你想要让程序在执行的时候具有比较好的性能,或者是其他的除错功能时, 可以在编译的过程里面加入适当的参数,例如下面的例子:

[root@study ~]# gcc -O -c thanks.c thanks_2.c  <== -O 为产生最优化的参数

[root@study ~]# gcc -Wall -c thanks.c thanks_2.c
thanks.c: In function ‘main’:
thanks.c:5:9: warning: implicit declaration of function ‘thanks_2’ [-Wimplicit-function-declaration]
         thanks_2();
         ^
thanks.c:6:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
# -Wall 为产生更详细的编译过程信息。上面的讯息为警告讯息 (warning) 所以不用理会也没有关系!

至于更多的 gcc 额外参数功能,就得要 man gcc 啰~呵呵!可多的跟天书一样~

21.2.3 调用外部函数库:加入链接的函数库

刚刚我们都仅只是在屏幕上面印出一些字眼而已,如果说要计算数学公式呢?例如我们想要计算出三角函数里面的 sin (90度角)。要注意的是,大多数的程序语言都是使用径度而不是一般我们在计算的“角度”, 180 度角约等于 3.14 径度!嗯!那我们就来写一下这个程序吧!

[root@study ~]# vim sin.c
#include <stdio.h>
#include <math.h>
int main(void)
{
        float value;
        value = sin ( 3.14 / 2 );
        printf("%f\n",value);
}

上面这个文件的内容可以在下面取得!

那要如何编译这支程序呢?我们先直接编译看看:

[root@study ~]# gcc sin.c
# 新的 GCC 会主动将函数抓进来给你用,所以只要加上 include <math.h> 就好了!

新版的 GCC 会主动帮你将所需要的函数库抓进来编译,所以不会出现怪异的错误讯息! 事实上,数学函数库使用的是 libm.so 这个函数库,你最好在编译的时候将这个函数库纳进去比较好~另外要注意, 这个函数库放置的地方是系统默认会去找的 /lib, /lib64 ,所以你无须使用下面的 -L 去加入搜寻的目录! 而 libm.so 在编译的写法上,使用的是 -lm (lib 简写为 l 喔!) 喔!因此就变成:

  • 编译时加入额外函数库链接的方式:

[root@study ~]# gcc sin.c -lm -L/lib -L/lib64  <==重点在 -lm 
[root@study ~]# ./a.out                         <==尝试执行新文件!
1.000000

特别注意,使用 gcc 编译时所加入的那个 -lm 是有意义的,他可以拆开成两部份来看:

  • -l :是“加入某个函数库(library)”的意思,

  • m :则是 libm.so 这个函数库,其中, lib 与扩展名(.a 或 .so)不需要写

所以 -lm 表示使用 libm.so (或 libm.a) 这个函数库的意思~至于那个 -L 后面接的路径呢?这表示: “我要的函数库 libm.so 请到 /lib 或 /lib64 里面搜寻!”

上面的说明很清楚了吧!不过,要注意的是,由于 Linux 默认是将函数库放置在 /lib 与 /lib64 当中,所以你没有写 -L/lib 与 -L/lib64 也没有关系的!不过,万一哪天你使用的函数库并非放置在这两个目录下,那么 -L/path 就很重要了!否则会找不到函数库喔!

除了链接的函数库之外,你或许已经发现一个奇怪的地方,那就是在我们的 sin.c 当中第一行“ #include <stdio.h>”,这行说的是要将一些定义数据由 stdio.h 这个文件读入,这包括 printf 的相关设置。这个文件其实是放置在 /usr/include/stdio.h 的!那么万一这个文件并非放置在这里呢?那么我们就可以使用下面的方式来定义出要读取的 include 文件放置的目录:

[root@study ~]# gcc sin.c -lm -I/usr/include

-I/path 后面接的路径( Path )就是设置要去搜寻相关的 include 文件的目录啦!不过,同样的,默认值是放置在 /usr/include 下面,除非你的 include 文件放置在其他路径,否则也可以略过这个项目!

通过上面的几个小范例,你应该对于 gcc 以及源代码有一定程度的认识了,再接下来,我们来稍微整理一下 gcc 的简易使用方法吧!

21.2.4 gcc 的简易用法 (编译、参数与链结)

# 仅将源代码编译成为目标文件,并不制作链接等功能:
[root@study ~]# gcc -c hello.c
# 会自动的产生 hello.o 这个文件,但是并不会产生 binary 可执行文件。

# 在编译的时候,依据作业环境给予最优化执行速度
[root@study ~]# gcc -O hello.c -c
# 会自动的产生 hello.o 这个文件,并且进行最优化喔!

# 在进行 binary file 制作时,将链接的函数库与相关的路径填入
[root@study ~]# gcc sin.c -lm -L/lib -I/usr/include
# 这个指令较常下达在最终链接成 binary file 的时候,
# -lm 指的是 libm.so 或 libm.a 这个函数库文件;
# -L 后面接的路径是刚刚上面那个函数库的搜寻目录;
# -I 后面接的是源代码内的 include 文件之所在目录。

# 将编译的结果输出成某个特定文件名
[root@study ~]# gcc -o hello hello.c
# -o 后面接的是要输出的 binary file 文件名

# 在编译的时候,输出较多的讯息说明
[root@study ~]# gcc -o hello hello.c -Wall
# 加入 -Wall 之后,程序的编译会变的较为严谨一点,所以警告讯息也会显示出来!

比较重要的大概就是这一些。另外,我们通常称 -Wall 或者 -O 这些非必要的参数为旗标 (FLAGS),因为我们使用的是 C 程序语言,所以有时候也会简称这些旗标为 CFLAGS ,这些变量偶尔会被使用的喔!尤其是在后头会介绍的 make 相关的用法时,更是重要的很呐! ^_^

前面说过, gcc 为 Linux 上面最标准的编译器,这个 gcc 是由 所维护的,有兴趣的朋友请自行前往参考。既然 gcc 对于 Linux 上的 Open source 是这么样的重要,所以下面我们就列举几个 gcc 常见的参数,如此一来大家应该更容易了解源代码的各项功能吧!

http://linux.vbird.org/linux_basic/0520source/hello.c
http://linux.vbird.org/linux_basic/0520source/thanks.c
http://linux.vbird.org/linux_basic/0520source/thanks_2.c
http://linux.vbird.org/linux_basic/0520source/sin.c
GNU 计划