Linux基础开发工具
# 软件包管理器-yum
在 Linux 下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序。但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成 windows 上的安装程序)放在一个服务器上, 通过包管理器可以很方便的获取到这个编译好的软件包, 直接进行安装。软件包和软件包管理器, 就好比 "App" 和 "应用商店" 这样的关系。
yum(Yellow dog Updater, Modified)是 Linux 下非常常用的一种包管理器. 主要应用在 Fedora, RedHat, Centos 等发行版上。
# 查看软件包
yum list
示例搜索 lrzsz
lrzsz.x86_64 0.12.20-36.el7 @base
- 软件包名称: 主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu 架构.
- "x86_64" 后缀表示 64 位系统的安装包, "i686" 后缀表示 32 位系统安装包. 选择包时要和系统匹配.
- "el7" 表示操作系统发行版的版本. "el7" 表示的是 centos7/redhat7. "el6" 表示 centos6/redhat6.
- "base" 表示的是 "软件源" 的名称
# 安装软件包
yum install xxx
# 卸载软件包
yum remove xxx
# 编辑器-vim
vim/vi 都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于 x window、 mac os、windows。
# 三种模式
vim 的三种模式,分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:
- 正常/普通/命令模式(Normal) 控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入 Insert mode 下,或者到 last line mode
- 插入模式(Insert) 只有在 Insert 下,才可以做文字输入,按「ESC」键可回到命令行模式。
- 末行模式(last line) 文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。
在命令模式下,shift + : 即可进入该模式。
要查看你的所有模式:打开 vim,底行模式直接输入:help vim-modes
# 基本操作
- [正常模式]切换至[插入模式]:输入 i/a/o
- [插入模式]切换至[正常模式]:Esc
- [正常模式]切换至[末行模式]:Shift + ; == :
- [末行模式]切换至[正常模式]:Esc
- 退出 vim 及保存文件:在[正常模式]下,进入[末行模式],再按 wq 即可退出并保存
# 命令模式命令集
【移动光标】
- 按「k」:光标上移。
- 按「j」:光标下移。
- 按「h」:光标左移。
- 按「l」:光标右移。
- 按「$」:移动到光标所在行的行尾。
- 按「^」:移动到光标所在行的行首。
- 按「gg」:移动到文本开始。
- 按「Shift + g」:移动到文本末尾。
- 按「n + Shift + g」:移动到第 n 行行首。
- 按「n + Enter」:当前光标向下移动 n 行。
- 按「w」:光标从左到右,从上到下的跳到下一个字的开头。
- 按「e」:光标从左到右,从上到下的跳到下一个字的结尾。
- 按「b」:光标从右到左,从下到上的跳到上一个字的开头
【删除】
- 按「x」:删除光标所在位置的字符。
- 按「nx」:删除光标所在位置开始往后的 n 个字符。
- 按「X」:删除光标所在位置的前一个字符。
- 按「nX」:删除光标所在位置的前 n 个字符。
- 按「dd」:删除光标所在行。
- 按「ndd」:删除光标所在行开始往下的 n 行。
【复制粘贴】
- 按「yy」:复制光标所在行到缓冲区。
- 按「nyy」:复制光标所在行开始往下的 n 行到缓冲区。
- 按「yw」:将光标所在位置开始到字尾的字符复制到缓冲区。
- 按「nyw」:将光标所在位置开始往后的 n 个字复制到缓冲区。
- 按「p」:将已复制的内容在光标的下一行粘贴上。
- 按「np」:将已复制的内容在光标的下一行粘贴 n 次。
【剪切】
- 按「dd」:剪切光标所在行。
- 按「ndd」:剪切光标所在行开始往下的 n 行。
- 按「p」:将已剪切的内容在光标的下一行粘贴上。
- 按「np」:将已剪切的内容在光标的下一行粘贴 n 次。
【撤销】
- 按「u」:撤销。
- 按「Ctrl + r」:恢复刚刚的撤销。
【大小写切换】
- 按「~」:完成光标所在位置字符的大小写切换。
- 按「n~」:完成光标所在位置开始往后的 n 个字符的大小写切换。
【替换】
- 按「r」:替换光标所在位置的字符。
- 按「R」:替换光标所到位置的字符,直到按下「Esc」键为止。
【更改】
- 按「cw」:将光标所在位置开始到字尾的字符删除,并进入插入模式。
- 按「cnw」:将光标所在位置开始往后的 n 个字删除,并进入插入模式。
【翻页】
- 按「Ctrl + b」:上翻一页。
- 按「Ctrl + f」:下翻一页。
- 按「Ctrl + u」:上翻半页。
- 按「Ctrl + d」:下翻半页。
# 底行模式命令集
【行号设置】
- 「set nu」:显示行号。
- 「set nonu」:取消行号。
【保存退出】
- 「w」:保存文件。
- 「q」:退出 vim,如果无法离开 vim,可在「q」后面跟一个「!」表示强制退出。
- 「wq」:保存退出。
【分屏指令】
- 「vs 文件名」:实现多文件的编辑。
- 「Ctrl + w + w」:在正常模式下光标在多屏幕下进行切换。
【执行指令】
- 「! + 指令」:在不退出 vim 的情况下,可以在指令前面加上「!」就可以执行 Linux 的指令,例如查看目录、编译当前代码等。
# 简单配置 vim
在目录 /etc/ 下面,有个名为 vimrc 的文件,这是系统中公共的 vim 配置文件,对所有用户都有效。而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:.vimrc
- /root 目录下,通常已经存在一个.vimrc 文件,如果不存在,则创建之。
- 切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~打开自己目录下的.vimrc 文件,执行 vim .vimrc
常用配置选项,用来测试:
- 设置语法高亮:
syntax on - 显示行号:
set nu - 设置缩进的空格数为 4:
set shiftwidth=4 - ...
# 编译和链接
在 ANSI C 的任何⼀种实现中,存在两个不同的环境:
- 翻译环境,源代码被转换为可执行的机器指令
- 执行环境,实际执行代码
翻译环境是由编译和链接两个大的过程组成的,编译又可以分解成:预处理、编译、汇编三个过程。

# 预处理
预处理(头文件展开、去注释、宏替换、条件编译)
在预处理阶段,源文件和头文件会被处理成为.i为后缀的文件。
- 将所有的
#define删除,并展开所有的宏定义。 - 处理所有的条件编译指令,如:
#if、#ifdef、#elif、#else、#endif - 处理
#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置。 - 删除所有的注释
- 添加行号和文件名标识,方便后续编译器生成调试信息等。
- 保留所有的
#pragma的编译器指令,编译器后续会使用。
# 编译
编译过程就是将预处理后的文件进行一系列的:词法分析、语法分析、语义分析及优化,生成相应的汇编代码文件
# 汇编
汇编器是将汇编代码转转变成机器可执行的指令,即二进制代码,每一个汇编语句几乎都对应一条机器指令。
# 链接
链接是⼀个复杂的过程,链接的时候需要把一堆汇编产生的二进制文件链接在一起生成可执行程序。
# 编译器-gcc/g++
gcc和g++分别是 GNU 的 C 和 C++的编译器
# gcc/g++语法
语法:gcc/g++ 选项 文件
选项:
-E只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面-S编译到汇编语言不进行汇编和链接-c编译到目标代码-o文件输出到文件-static此选项对生成的文件采用静态链接-g生成调试信息,GNU 调试器可利用该信息-shared此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库-w不生成任何警告信息。-Wall生成所有警告信息
# 过程
预处理:
gcc -E test.c -o test.i
- -E 选项的作用是让 gcc/g++在预处理结束后停止编译过程
- -o 选项是指目标文件,
xxx.i文件为已经过预处理的原始程序
编译:
gcc -S test.i -o test.s
- 用户可以使用-S 选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码
- -o 选项是指目标文件,
xxx.s文件为已经过翻译的原始程序
汇编:
gcc -c test.s -o test.o
- 汇编阶段是把编译阶段生成的
xxx.s文件转成目标文件 - 使用-c 选项就可以得到汇编代码转化为
xxx.o的二进制目标代码了
链接:
gcc test.o -o test
- gcc/g++不带-E、-S、-c 选项时,就默认生成预处理、编译、汇编、链接全过程后的文件
- 若不用-o 选项指定生成文件的文件名,则默认生成的可执行文件名为 a.out
链接的本质:我们调用库函数的手如何与标准库链接的?
# 动态库和静态库
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也 就不再需要库文件了。其后缀名一般为“.a”
- 动态库在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时 链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态 库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件。 gcc 默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证
动态链接:优点:省空间(磁盘的空间,内存的空间),bin 体积小,加载速度快;缺点:依赖动态库,程序可移植性较差。
静态链接:优点:不依赖第三方库,程序的可移植性较高;缺点:浪费空间。
# 调试器-gdb
# gdb 使用须知
程序发布方式: 1、debug 版本:程序本身会被加入更多的调试信息,以便于进行调试。 2、release 版本:不会添加任何调试信息,是不可调试的。
# gdb 命令集
【进入 gdb】
指令:gdb 文件名
【调试】
- 「run/r」:运行代码(启动调试)。
- 「next/n」:逐过程调试。
- 「step/s」:逐语句调试。
- 「until 行号」:跳转至指定行。
- 「finish」:执行完当前正在调用的函数后停下来(不能是主函数)。
- 「continue/c」:运行到下一个断点处。
- 「set var 变量=x」:修改变量的值为 x。
- 「watch」:观察变量值的变化等。
- 「gdb 文件名 core文件」:调试段错误。
【显示】
- 「list/l n」:显示从第 n 行开始的源代码,每次显示 10 行,若 n 未给出则默认从上次的位置往下显示.。
- 「list/l 函数名」:显示该函数的源代码。
- 「print/p 变量」:打印变量的值。
- 「print/p &变量」:打印变量的地址。
- 「print/p 表达式」:打印表达式的值,通过表达式可以修改变量的值。
- 「display 变量」:将变量加入常显示(每次停下来都显示它的值)。
- 「display &变量」:将变量的地址加入常显示。
- 「undisplay 编号」:取消指定编号变量的常显示。
- 「bt」:查看各级函数调用及参数。
- 「info/i locals」:查看当前栈帧当中局部变量的值。
【断点】
- 「break/b n」:在第 n 行设置断点。
- 「break/b 函数名」:在某函数体内第一行设置断点。
- 「info breakpoint/b」:查看已打断点信息。
- 「delete/d 编号」:删除指定编号的断点。
- 「disable 编号」:禁用指定编号的断点。
- 「enable 编号」:启用指定编号的断点。
【shell】
- 「shell」:执行 shell 操作。
【退出 gdb】
- 「quit/q」:退出 gdb。
# 项目自动化构建工具-make/makefile
# 引入
会不会写 makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的 规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂 的功能操作。 makefile 带来的好处就是——“自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编 译,极大的提高了软件开发的效率。
make 是一个命令工具,是一个解释 makefile 中指令的命令工具。
make 是一条命令,makefile 是一个文件,两个搭配使用,完成项目自动化构建。
# 依赖关系和依赖方法
- 依赖关系: 文件 A 的变更会影响到文件 B,那么就称文件 B 依赖于文件 A。
例如,test.o 文件是由 test.c 文件通过预处理、编译以及汇编之后生成的文件,所以 test.c 文件的改变会影响 test.o,所以说 test.o 文件依赖于 test.c 文件。
- 依赖方法: 如果文件 B 依赖于文件 A,那么通过文件 A 得到文件 B 的方法,就是文件 B 依赖于文件 A 的依赖方法。
例如,test.o 依赖于 test.c,而 test.c 通过 gcc -c test.c -o test.o 指令就可以得到 test.o,那么 test.o 依赖于 test.c 的依赖方法就是 gcc -c test.c -o test.o
# 多文件编写
例如,编写一个进度条小程序:
.
├── Makefile
├── progress
├── progress.cpp
├── progress.h
└── test.cpp
2
3
4
5
6
Makefile 文件:
test:test.cpp progress.cpp
g++ -o progress test.cpp progress.cpp # 不需要头文件
.PHONY:clean
clean:
rm -rf progress
2
3
4
5
progress.cpp 文件:
#include"progress.h"
#include <iostream>
#include <iomanip>
#include <chrono>
#include <thread>
#include <string>
void progresson() {
const int totalSteps = 100;
std::string proc(totalSteps, ' ');
const std::string symbols = "\\|/-";
for (int i = 0; i <= totalSteps; ++i) {
proc[i] = '#';
std::cout << "[" << proc << "]" << "[" << std::setw(3) << i << "%]" << "[" << symbols[i % 4] << "]\r" << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds(60));
}
std::cout << std::endl;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19