目录
[1 软件包管理器](#1 软件包管理器)
[1.1 什么是软件包](#1.1 什么是软件包)
[1.2 linux软件生态](#1.2 linux软件生态)
[1.3 yum具体操作](#1.3 yum具体操作)
[1.3.1 查看软件包](#1.3.1 查看软件包)
[1.3.2 安装软件](#1.3.2 安装软件)
[1.3.3 卸载软件](#1.3.3 卸载软件)
[2 编辑器Vim](#2 编辑器Vim)
[2.1 vim命令模式命令集](#2.1 vim命令模式命令集)
[2.2 vim底行模式命令集](#2.2 vim底行模式命令集)
[3 编译器gcc/g++](#3 编译器gcc/g++)
[3.1 预处理](#3.1 预处理)
[3.2 编译](#3.2 编译)
[3.3 汇编](#3.3 汇编)
[3.4 链接](#3.4 链接)
[3.5 知识点](#3.5 知识点)
[3.5.1 如何理解条件编译?条件编译的用途?](#3.5.1 如何理解条件编译?条件编译的用途?)
[3.5.2 为什么C/C++编译,要先变成汇编?](#3.5.2 为什么C/C++编译,要先变成汇编?)
[3.5.3 编译器要用什么语言写呢?先有汇编语言还是先有汇编语言写的编译器?](#3.5.3 编译器要用什么语言写呢?先有汇编语言还是先有汇编语言写的编译器?)
[3.5.4 什么叫做动静态库,什么叫做动静态链接,如何理解](#3.5.4 什么叫做动静态库,什么叫做动静态链接,如何理解)
[3.5.5 动静态库对比](#3.5.5 动静态库对比)
[3.5.6 做验证工作](#3.5.6 做验证工作)
[3.5.7 周边问题](#3.5.7 周边问题)
1 软件包管理器
1.1 什么是软件包
在linux上安装软件通常有三种方式。第一种是使用源码安装;第二种是使用软件包安装;第三种是使用软件包管理器(yum)安装。
源码安装是下载到程序的源代码,并进行编译,得到可执行程序。这样很麻烦,一般都不这样做。
于是有些人就把一些常用的软件提前编译好,做成软件包(可以理解成对应windows上的安装程序)放在一个服务器上。软件包安装现在也基本不使用,现在常用包管理器进行安装。通过包管理器可以方便的获取到编译好的软件包,直接进行安装。
软件包和软件包管理器,就好比App和应用商店的关系。
yum是linux下的一种常用的包管理器,主要应用在Fedora,RedHat,Centos等上;Ubuntu下主要使用apt作为包管理器。
包管理器是使用网络下载软件,然后安装(本质就是拷贝),所以要有root权限,安装到系统里面,只要安装一次,任何人都能使用。为什么会推荐包管理器?因为它会自动给我们解决软件包的依赖问题(一个软件A不能独立运行,需要其他库文件,文件有时又需要其他文件)。
如果没有包管理器,手动安装gcc需要几步?
- 下载 gcc 源码包
- 尝试编译 → 报错:缺少 GMP 库
- 下载 GMP 源码
- 编译 GMP → 报错:缺少 M4
- 下载 M4 源码
- ......
相当麻烦,这就是传说中的"依赖地狱"。


1.2 linux软件生态
如何去评估一款操作系统的好坏?一款操作系统生态好,这款操作系统就算好的操作系统,这其中包括社区,文档,人群,解决问题等方面。

一款操作系统一经发行,吸引的人群有哪些,在这个社区上有没有积累足够多的文档,文档有没有反映出足够多的问题,问题有没有被解决,这些都被称之为操作系统的生态问题。如何理解这个过程呢?
在Centos、Ubuntu等系统有其对应的社区。社区里有活跃的开发者,开发出对应的软件然后将源代码托管到官网或github,然后源码编译形成软件。一个软件可能在多个平台编,上到对应社区平台上,所以会有很多软件。自己的linux中预装yum/apt,通过指令来下载这些软件。
为什么会有人免费特定社区提供软件,还发布?还提供云服务器让你下载?
开源,本质是一种商业模式,很多公司会用你社区上的东西,我推出一款操作系统,为了让更多人用我的操作系统,将软件开源。很多公司用你社区上的东西,不想让你倒闭,一旦你用了,就介入了我的因果。
我的机器怎么知道下载链接的呢?
因为操作系统内部内置链接,centos默认内置链接www.centos.org,因为有防火墙,生态在国外,所以就有国人将国外网站镜像过来,同时更改下载链接,就可以下载了。
1.3 yum具体操作
1.3.1 查看软件包
通过yum list命令可以罗列出当前一共有哪些软件包,由于包的数量非常多,这里我们使用grep命令筛选出我们关注的包。例如:
//找出关键字中包含sl的软件
sudo yum list | grep sl
1.3.2 安装软件
通过yum,可以通过简单的命令完成gcc的安装
//centos
sudo yum -y install gcc
//ubuntu
sudo apt -y install gcc
注意事项:
- yum/apt会⾃动找到都有哪些软件包需要下载,这时候敲"y"确认安装
- 出现"complete"字样或者中间未出现报错,说明安装完成
- 安装软件时由于需要向系统⽬录中写⼊内容,⼀般需要sudo或者切到root账⼾下才能完成
- yum/apt安装软件只能⼀个装完了再装另⼀个。正在yum/apt安装⼀个软件的过程中,如果再尝试⽤ yum/apt安装另外⼀个软件,yum/apt会报错
1.3.3 卸载软件
使用命令卸载指定的软件
//centos
//强制卸载sl
sudo yum remove -y sl
//ubuntu
sudo apt remove -y sl
使用yum/apt的所有操作需要保证机器网络畅通。
2 编辑器Vim
vim键盘图

这里说明vim的三种模式:命令模式,插入模式,底行模式。进入vim默认是默认模式。想要正常写东西,要从命令模式下切换到插入模式,输入i,切换到插入模式。想退出,按esc,进入命令模式,然后按shift+;,进入底行模式,按wq,保存退出。

2.1 vim命令模式命令集
这里列出一些重点命令
- gg:快速回归到光标第一行
- shift+g=G:快速定位到结尾
- n+shift+g:快速定位第n行
- shift+4=$:光标定位到行结尾
- shift+6=^:光标定位到行开头
- 上下左右移动用键盘的上下左右键,或者h:左移,j:下移,k:上移,l:右移
- yy:复制
- n+yy:复制n行
- p:粘贴
- n+p:粘贴n行
- dd:删除/剪切当前行,然后可以p,粘贴
- n+dd:删除n行/剪切n行,然后n+p,重复粘贴n行。
- u:撤销历史操作
- ctrl+r:撤销u操作
- x:删除光标所在位置的字符
- n+x:删除n个字符
- shift+x:以光标为分界线,光标右侧不动,光标左侧删除。删完后也可p。
- shift+v:光标处大小写切换
- r:替换光标所在字符
- shift+r=R:批量化替换,进入替换模式
- ctrl+v:切换到视图模式,然后h j k l进行区域选择,然后shift+i进入插入模式,//进行注释这些行,然后esc回退默认模式,进行快速注释
2.2 vim底行模式命令集
- w:保存
- q:退出
- wq:保存退出
- ZZ(大写):快速退出
- set nu:添加行号
- set nonu:去掉行号
- !+指令:可以在不退出vim时执行指令
- /关键字:先按/键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按n会往后寻找到您要的关键字为⽌
- ?关键字:先按?键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按n会往前寻找到您要的关键字为⽌

3 编译器gcc/g++
格式:
gcc [选项] 要编译的文件 [选项] [目标文件]
我们知道C/C++代码从源代码到可执行文件需要经过预处理,编译,汇编,链接这几个过程,下面就说明这几个过程
3.1 预处理
预处理做了什么?
- 头文件展开:把 头文件的内容原封不动地复制进来
- 宏替换:例如把#define MAX 100替换成 100
- 条件编译:处理#ifdef,#ifndef等
- 删除注释:把//和/* */去掉
选项-E,在预处理结束后,生成.i后缀文件,然后停下来。例如:gcc -E hello.c -o hello.i
选项-o是指目标文件
3.2 编译
预处理过后,就要编译,在这个阶段,gcc要检查代码的规范性,是否有语法错误等,以及确定代码实际要做的工作,在检查无误后,gcc把代码翻译成汇编语言。
选项:-S,作用是只进行编译而不进行汇编。
例如:gcc -S hello.i -o hello.s
3.3 汇编
将汇编代码翻译成机器指令,生成可重定位目标文件(.o文件),此时就是二进制文件了,但是还不能直接运行。
选项:-c,将汇编代码转化为".o"的二进制目标代码了。汇编完成就停下来。
例如:gcc -c hello.s -o hello.o
3.4 链接
把多个目标文件和库**合并成一个可执行文件。**例如:gcc hello.o -o hello。
3.5 知识点
编译多个文件,一般都是用-c选项把源文件统一变成.o文件,然后用gcc code1.o code2.o code3.o......-o code 整体生成一个可执行文件code。
ldd+可执行程序,可以查看可执行程序依赖哪些库
3.5.1 如何理解条件编译?条件编译的用途?
条件编译:#if,#endif;#ifdef,#ifndef。条件编译 是 C/C++ 预处理阶段的一个开关 ------它让你能根据不同的条件,决定哪些代码参与编译,哪些代码被忽略。
用途:
(1)对软件进行专业度,收费情况进行区分(业务层面),使用条件编译,可以进行代码的动态裁剪,进行软件专业版和社区版的区分。
(2)内核源代码也是采用条件编译进行代码裁剪
(3)开发工具,应用软件采用条件编译让软件在windows和linux都能运行。
3.5.2 为什么C/C++编译,要先变成汇编?
我们编程的本质就是控制计算机。最开始的开关->打孔编程->汇编语言。
从汇编语言开始,编译器就要产生了,要将语言翻译成二进制。那么这时问题就来了:编译器究竟是C语言直接翻译二进制还是C语言翻译成汇编语言,然后再转换成二进制呢?
答案显然是后者。从C语言到汇编毕竟是从文本到文本,难度低,从汇编到二进制就是站在巨人的肩膀上。从C语言直接到二进制,成本太高。
3.5.3 编译器要用什么语言写呢?先有汇编语言还是先有汇编语言写的编译器?
答案是先有汇编语言,第一版的编译器是二进制版的,这样就可以编译汇编语言了。后来用汇编语言写一个汇编编译器,再用二进制版的编译器翻译一下,就诞生了第一个汇编语言的编译器。这个过程叫做编译器的自举过程。
所以根据以上流程,先有C语言,然后有C语言写的C编译器(拿汇编语言写一个C编译器,就能不编译C语言了,然后拿C语言写一个C编译器,用汇编语言写的C编译器翻译一下,诞生第一个C编译器)。
3.5.4 什么叫做动静态库,什么叫做动静态链接,如何理解
这里做简单说明,做小白式的理解,具体后续另写。
所谓的库,是一套方法或者数据集,为我们的开发提供最基本的保证(基本接口,功能,加速我们的二次开发)。
使用ls /usr/lib64查看系统里默认的库,.so结尾一般是动态库,windows中.dll文件是动态库。

C动态库:libc.so,C静态库:libc.a。Linux中,命名,去掉前缀lib,去掉后缀.so或.a剩下的就是名字。平时形成的可执行程序(c写的)都依赖动态库。

动静态库内部实现方法:
我们自己的程序,会使用库中的方法,自己的程序和库中的方法链接起来形成可执行程序,链接就是让自己的程序能在库中找到方法。
动态库的典型特点是:执行目标方法时(例如printf),需要跳转到库中执行,完了再返回。

所以链接就是让我的程序,能找到库中的方法的地址!动态链接的过程只是在地址上产生关联。
所谓静态链接,就是将库中实现的内容拷贝一份到我自己的程序中,这种链接方式叫做静态链接。
举一个例子:小明是一个初中生考上了XXX高中,他喜欢玩游戏,这个高中不允许携带任何电子设备。于是小明学长问有没有玩游戏的地方,学长告诉小明学校东门有一个网咖,小明知道了具体地址。到了开学后的周末,小明周末指定学习计划:9:00语文作业,10:00数学作业,11:00吃饭,12:00去上网,14:00化学作业。小明到了计划时间根据地址去到网咖,在100号机器上网,时间到了就回去了。
这个故事中,小明就是程序;高中就是内存;小明问学长,学长告诉小明具体地址,这个叫动态链接;网咖就是动态库,小明上高中就叫加载过程,执行学习计划就是执行代码;执行计划去网咖叫做库调用;回学校叫做库函数返回。
在这个故事里:
-
链接时机 :小明在开学前就从学长那里知道了网咖地址(编译时确定地址)
-
加载时机 :小明到了周末才真正去网咖(运行时才加载库)
-
调用过程:小明按地址找到机器(通过地址跳转到函数)
所以叫"动态链接"------链接发生在编译时,但真正使用是在运行时。
所以动态链接是在编译时,没加载时就链接好了。动态链接是将要访问库方法的地址写在程序中,执行时就通过地址找到了。
这个学校很多学生都去这个网咖,所以这个动态库,也叫做共享库。后来因为某些原因网咖关门了。所有同学的计划无法执行了,所以共享库有一个特点:被多个程序共享,一旦缺失,会导致所有程序无法运行。这是个缺点。
故事二:网咖关门了,上不了网,小明回家后告诉父亲因为无法去网咖查阅学习资料导致成绩下滑,他父亲就找到学校,学校允许带电脑了。小明父亲又找到网咖老板,将小明用的100号机器买了下来,搬到宿舍里,以后上网就在宿舍了,这个过程叫做把网咖电脑拷贝给小明,其他同学也纷纷效仿,这种链接方式叫做静态链接。
静态链接的特点是:把我们自己程序中用的库方法,拷贝给我自己!静态库只有在链接的时候有用,一旦形成可执行程序,静态库可以不再需要。
3.5.5 动静态库对比
| 对比维度 | 动态链接 | 静态链接 |
|---|---|---|
| 库的位置 | 系统里共享的一份 | 每个程序自己有一份 |
| 内存占用 | 小(共用一份) | 大(每人一份) |
| 可执行文件大小 | 小(只记录地址) | 大(包含库代码) |
| 更新库 | 所有程序自动受益 | 每个程序需重新编译 |
| 库缺失 | 所有程序无法运行 | 不受影响 |
| 典型后缀 | .so(Linux)、.dll(Windows) |
.a(Linux)、.lib(Windows) |
如何选择?
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 普通应用 | 动态链接 | 省内存、易更新 |
系统工具(如 ls) |
动态链接 | 和系统 libc 保持一致 |
| 嵌入式系统 | 静态链接 | 环境固定,避免依赖缺失 |
| 容器内程序 | 静态链接 | 容器小,避免找库 |
| 发布给别人的程序 | 静态链接 | 用户不用装依赖 |
3.5.6 做验证工作
写一个简单的printf的代码



ldd code,发现库是.so的库,所以默认是动态链接的。

ll code,发现体积是8360字节。
用静态库:C静态库就得存在。

发现报错cannot find -lc,意味着确实C库(libc.a)不存在,所以要安装静态库:


此时再次编译

可执行程序的体积是861216字节,体积扩大了100倍。
结论是:gcc编译,默认动态链接,-static用静态链接。
同理,C++程序使用g++编译,安装C++静态库使用sudo yum install libstdc++-static安装。
3.5.7 周边问题
动静态库,源文件,可执行程序本质都是文件。加载程序时,首先将程序加载到内存里,一般动态库也要加载到内存里,跳转执行是在内存中,以后其他程序要调用这个库时,也调用它。所以动态库本质是把所有程序中公共代码在内存中只出现一份。所以一旦动态库缺失,相当一大部分指令都无法运行。
为什么汇编要翻译成.o文件呢?为了和库文件(后缀.o)进行合并。链接的本质是将所有的.o文件进行合并。关于动态库更详细内容,以后文章再说明。
以上就是本文全部内容了,希望对你有所帮助,如果这篇文章对你有用,可以点点赞哦,你的支持就是我写下去的动力,后续会继续更新其他知识。