目录
代码: https://github.com/WHaoL/study/tree/master/00_06_Linux_SystemCode_and_SocketCode
代码: https://gitee.com/liangwenhao/study/tree/master/00_06_Linux_SystemCode_and_SocketCode
1. vim
安装vim
sudo apt install vim
sudo yum install vim
自带帮助文档:
- 在终端上执行命令:
vimtutor
1.1 vim的模式
使用vim将文件打开, 默认是命令模式
-
命令模式
- 通过命令对文件内容进行修改
- 复制 yy nyy
- 粘贴 p
- 删除行 dd
- 保存,并退出 ZZ (ctrl+z ctrl+z)
- 代码格式化 gg=G
- 移动到行首 0
- 移动到行尾 shift+4
- 移动到文件头 gg
- 移动到文件尾 G
- 行跳转 行号+G
- 从当前行向下跳转对应的行数 数字(行数)+回车
- h: 光标左移
j: 光标下移
k: 光标上移
l: 光标右移 - 撤销 u
- 反撤销 ctrl+r
- 查找 / 待搜索的字符串 n 正序 N 逆序
- …
- 通过命令对文件内容进行修改
-
文件编辑模式
-
在文件中进行写操作
命令模式
切换到文本编辑模式
i文本编辑模式
切换到命令模式
Esc
-
-
末行模式
-
在终端的最下边一行中输入一些命令对文件进行操作
命令模式
切换到末行模式
:末行模式
切换到命令模式
连续两次Esc- 保存, 并退出 :wq
- 替换字符串 :%s/待替换的字符串/替换成什么字符串/g (替换所有匹配的)
- 行跳转 :行号
- 执行shell命令 :!shell命令
- 分屏
- 数据查找
- 内容的替换
-
1.2 命令模式
-
保存退出
# 两个大写的Z, 在键盘上输入 ZZ Ctrl+z Ctrl+z
-
代码格式化
# 在键盘上输入: gg=G
-
光标移动
# 1. 上下左右移动 h: 光标左移 j: 光标下移 k: 光标上移 l: 光标右移 # 2. 移动到行首 行首: 0 # 3. 移动到行尾 行尾: shift+4 # 4. 移动到文件头 -> 第一行的行首 gg # 5. 移动到文件尾 -> 最后一行行首 G # 6. 行跳转 行号G # 从当前行向下跳转对应的行数 数字(行数)+回车
-
删除命令
# 1. 字符删除 - 删除光标盖住的字符(当前字符): x (小写) - 删除光标盖住的字符前边的字符: X (大写) # 2. 删除单词 - dw: 将光标放到要删除的单词的最前边, 用光标盖住单词的第一个字符, 否则只能删除一部分 # 3. 删除光标前的字符串 - d0 # 4. 删除光标后的字符串 (两种方式) - d$ - D # 5. 删除行 - dd # 6. 删除多行 - 行数dd (ndd) - 10dd # 一次删除10行, 从光标所在的行向下
-
撤销和反撤销
# 1. 撤销 u # 2. 反撤销 ctrl+r
-
复制和粘贴
# 复制 -> 复制光标所在的当前行 yy # 复制N行 nyy (n是要复制的行数, 从当前行向下复制) # 粘贴 -> 粘贴到光标所在行的下一行 p # 粘贴 -> 粘贴到光标所在行的上一行 P
-
可视模式
# 在可以看到的情况下, 进行行列的选择, 进行复制, 删除 # 进入到可视模式: v # 如何选择: h: 光标左移 j: 光标下移 k: 光标上移 l: 光标右移 # 删除 d # 复制 y # 将可视模式中复制的内容进行粘贴 p: 粘贴到当前光标的后边 P: 粘贴到当前光标的前边
-
替换操作
# 替换某一个字符, 光标在哪个字符上, 就替换哪个字符, 其余的字符无法操作 r # 从光标所在的当前字符开始覆盖原字符, 输入多少新字符, 覆盖多少旧的字符 R
-
查找命令
# 搜索 # 1. 从上往下搜索 先按键盘上的 / 再输入要搜索的字符串 # 搜索到的字符会加底色显示, 我需要遍历查看 n: 从上到下 -> 正序 N: 从下到上 -> 逆序 # 2. 从文件尾部向上搜索 先按键盘上的 ? 再输入要搜索的字符串 # 3. 找到要搜索的关键字, 将光标放到这个关键字上, 在键盘上输入# 使用N/n进行遍历
-
查看man文档
# man首页 man man# 在vim中直接跳转到man文档 # 将光标放到某一个函数上边, 输入章节号 + shift+k
1.3 文本编辑模式
命令模式切换到文本编辑模式
a -> 从当前光标的后边开始插入数据
A -> 从当前行的后边开始插入数据
i -> 从当前光标的前边开始插入数据 #################
I -> 从当前行行首开始插入数据
o -> 在当前行下边创建新的行进行数据插入
O -> 在当前行上边创建新的行进行数据插入
s -> 删除光标盖住的字符, 从删除的字符的位置开始插入数据
S -> 删除当前行, 从该行进行数据插入
# 从文本编辑模式 -> 命令模式
esc
1.4 末行模式
-
命令模式切换到末行模式
# 按键盘上的符号 # 命令模式 -> 末行模式 : # 末行模式 -> 命令模式 - 连续按两次esc - 在末行模式下执行一个指令
-
保存退出
# 保存, 不退出 :w # 保存, 并退出 :wq == x # 退出不保存 # 如果我们修改了文件, 使用q退出, 会提示我们保存 :q # 强制退出不保存 :q!
-
替换
# 对文件中的指定字符串进行替换 ######################################## # 替换光标所在行中的第一个符合条件的字符串 :s/待替换的字符串/替换成什么字符串 # 替换光标所在行中的所有字符串 :s/待替换的字符串/替换成什么字符串/g # 替换指定行中的字符串(需要指定一个范围) :2,10s/待替换的字符串/替换成什么字符串 # 这些行的第一个符合条件的字符串 :2,10s/待替换的字符串/替换成什么字符串/g # 替换所有 # 整个文件范围内都替换 :%s/待替换的字符串/替换成什么字符串 # 所有行中的第一个符合条件的字符串 ######################################## # 替换文本中,所有复合条件的字符串 :%s/待替换的字符串/替换成什么字符串/g # 替换所有!!!!!
-
分屏
# 将文件分成多个屏幕显示 # 水平分屏 :sp # 垂直分屏 :vsp # 拆分的屏幕之间的切换 ctrl+w+w # 同时关闭多个屏幕 :qall # 保存并退出多个屏幕 :wqall # 保存多个屏幕中的文件数据 :wall # 分屏的时候在不同的屏幕中打开不同的文件 :sp 文件名 :vsp 文件名 # 使用vim打开文件的时候指定多个文件分屏显示 # 参数o, 水平分屏 vim -o file1 file2 file3 ... # 参数O, 垂直分屏 vim -O file1 file2 file3 ...
-
行跳转
:行号
-
执行shell命令
:!shell命令 # 举例 :!ls
1.5 vim配置文件
# 用户级别 -> 对当前用户生效
~/.vimrc
# 系统级别 -> 对所有用户生效
/etc/vim/vimrc
# 都配了, 用户级别生效
2.gcc
GNU编译器套件(GNU Compiler Collection)原名為GNU C語言编译器(GNU C Compiler),因為它原本只能处理C語言。GCC在发布后很快地得到扩展,包括C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。GCC的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。此处,“自由”的含义是它尊重用户的自由。
GUN 编译器套件包含多种前端处理器,以翻译各种不同语言。当然,我们重点讨论的是基于C语言的前端处理器 GCC。
GCC 也是一种多目标(multitarget)编译器;换句话说,它通过使用可互换的后端处理器,为多种不同的计算机架构生成相应的可执行程序。
GCC 不仅支持C的许多“方言”,也可以区别不同的C语言标准;也就是说,可以使用命令行选项来控制编译器在翻译源代码时应该遵循哪个C标准。例如,当使用命令行参数
-std=c99
启动 GCC 时,编译器支持 C99 标准。GUN99 c99
在linux下使用 gnu打头的标准: gun99 gun11
1.1 安装
-
安装命令
# gcc用于编译c程序, g++编译c++程序 sudo apt install gcc g++ sudo yum install gcc g++ # 在Linux-CentOS7-1804中写程序: gcc版本 > 4.8.5
-
查看版本
gcc -v gcc --version g++ -v g++ --version # 如果写c++程序, 并且使用c++11中的语法, 编译器版本>4.8.5
-
gcc与g++有什么区别?
gcc和g++都是GNU(组织)的一个编译器。
-
误区一:gcc只能编译c代码,g++只能编译c++代码。两者都可以,但是请注意:
- 后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;
- 后缀为.cpp的,两者都会认为是c++程序,C++的语法规则更加严谨一些。
- 编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
-
误区二:gcc不会定义
__cplusplus
宏,而g++会- 实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释
- 如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。
-
误区三:编译只能用gcc,链接只能用g++
- 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用
g++
或者gcc -lstdc++
。 - gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价
- 严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用
-
1.2 gcc 工作流程
// 1. 预处理
/*
- 头文件展开
- 宏替换
- 注释删除
*/
// test.c -> test.i
gcc -E test.c -o test.i
// 2. 编译 -> 得到汇编文件
gcc -S test.i -o test.s
// 3. 汇编 -> 二进制文件
gcc -c test.s -o test.o
// 4. 链接 -> 二进制文件, 可执行
// 如果不指定生成的文件名, 默认a.out
gcc test.o
1.3 gcc常用参数选项
gcc编译选项 | 选项的意义 |
---|---|
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定的源文件,但是不进行链接 |
-o [file2] [file1] / [file1] -o [file2] | 将文件 file1 编译成可执行文件 file2 |
-I directory | 指定 include 包含文件的搜索目录 |
-g | 在编译的时候,生成调试信息,该程序可以被调试器调试 |
-D | 在gcc/g++程序编译的时候,指定一个宏 |
-w | 不生成任何警告信息 |
-Wall | 生成所有警告信息 |
-On | n的取值范围:0~3。编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 |
-l | 在程序编译的时候,指定使用的库 |
-L | 指定编译的时候,搜索的库的路径。 |
-fPIC/fpic | 生成与位置无关的代码 |
-shared | 生成共享目标文件。通常用在建立共享库时 |
-std | 指定C方言,如:-std=c99,gcc默认的方言是GNU C |
#############gcc常用参数
-g # 生成调试信息
-w # 不成成警告信息
-I # 指定头文件的搜索路径
-l # 指定库名
-L # 指定库的搜索路径
-fPIC # 生成与位置无关的代码
-shared # 生成动态库目标文件
-std=c11 # 指定GNU c的版本
-std=c99 # 指定GNU c的版本
-D # 调试时,指定宏
CFLAGS # 执行“ CC”编译器的命令行参数(编译.c源文件的选项)
CXXFLAGS # 执行“ g++”编译器的命令行参数(编译.cc源文件的选项)
LDFLAGS # 链接器(如:“ ld”)参数
#include <stdio.h>
int main()
{
int a = 10;
#ifdef DEBUG
printf("我是一个程序猿, 我不会爬树...\n");
#endif
for(int i=0; i<3; ++i)
{
printf("hello, GCC!!!\n");
}
return 0;
}
// 参数 -D 使用场景: 程序调试的时候使用, 通过-D指定的宏打开log输出
// 默认编译第12行代码不输出, 如果想要输出这句话, 可以使用-D
gcc test_-DDEBUG.c -D DEBUG
gcc test_-DDEBUG.c -o test_-DDEBUG -std=c11 -DDEBUG
// 参数 -On 优化程序, 有4个级别
// n 取值范围: 0-3
// O0 -> 不优化, O1 -> 默认的级别, O3 -> 最高级别
// 最简单的例子:
int a, b, c, d;
a = 10;
b = a;
c = b;
d = c;
// 优化:
b = 10;
c = 10;
d = 10;
// 参数 -std , 指定编译器规范
// c最早的标准:c89
// c99开始支持现在的书写习惯
int a = 10;
func();
int b =5;
for(int i=0; i<5; ++i)
gcc -std=c99/gnu99 test_-DDEBUG.c -o test_-DDEBUG
-DDEBUG 参数和值之间的空格可有可无
gcc test_-DDEBUG.c -o test_-DDEBUG -DDEBUG # define DEBUG
3 静态库和动态库
2.1 扫盲
- 库是什么?
- 是一个文件
- 二进制的
- 二进制数据 -> 函数的实现(函数定义)
- 为什么要使用库?
- 保密的需要
- 方便分发和部署
- 库有了如何使用?
- 库中定义的函数对应头文件
- 生成的库文件
2.2 静态库
-
命名规则
- Linux
- libxxx.a
- lib -> 前缀, 固定的
- xxx -> 库的名字, 随意指定
- .a -> 后缀, 固定的
- libxxx.a
- windows
- libxxx.lib
- Linux
-
静态库制作
# 将源文件 -> 静态库
# 原材料
.
├── add.c
├── div.c
├── include
│ └── head.h
├── main.c
├── mult.c
└── sub.c
# 1. 将.c -> .o
$ gcc -c add.c div.c mult.c sub.c -I ./include/
$ ls
add.c add.o div.c div.o include main.c mult.c mult.o sub.c sub.o
# 2. 将生成的.o 打包成静态库
# ar rcs 生成的库的名字 原材料(*.o)
# r: 替换
# c: 创建
# s: 索引
$ ar rcs libcalc.a *.o
robin@OS:~/Linux/3Day/calc$ ls
add.c add.o div.c div.o include `libcalc.a` main.c mult.c mult.o sub.c sub.o
# 发布
将头文件`head.h`和静态库`libcalc.a`
###########################################
#步骤如下:
#1. 将.c -> .o
gcc -c add.c div.c mult.c sub.c -I ./include/
#2. 将生成的.o 打包成静态库
ar rcs libcalc.a *.o
#3. 发布
head.h libcalc.a
###########################################
1. 生成libXXX.a的Makefile
# 根据 *.c 和 .h
# 生成静态库 libcalc.a
#---------------------------------------------------------
# 工程名 / 库名 # 生成静态库 libcalc.a 计算简单的加减乘除
PROJECT = libcalc
OUTNAME = $(PROJECT).a
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH = -I./include/
#---------------------------------------------------------
# .o文件
SrcSuf = c
ObjSuf = o
LibSuf = a
OBJFILES = ./add.$(ObjSuf)\
./div.$(ObjSuf)\
./mult.$(ObjSuf)\
./sub.$(ObjSuf)\
#---------------------------------------------------------
CC = gcc
LD = ar rcs
# 编译选项
CFlag = -w -g -ggdb -fshort-wchar -std=c11 $(INCLUDEPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)
all: $(PROJECT) clean
$(PROJECT):$(OBJFILES)
@echo "Linking $(OUTNAME) start..."
$(LD) $(OUTNAME) $(OBJFILES)
@echo "Linking $(OUTNAME) end"
clean:
@echo "Cleaning $(PROJECT) project files"
@rm -f $(OBJFILES) core
.$(SrcSuf).$(ObjSuf):
@echo "Compiling $(PROJECT) $<"
$(CC) $(CFlag) -c $< -o $@
- 静态库使用
# 测试静态库函数是不是能支持工作
# 根据头文件中的函数声明, 对这些函数进行调用, 测试得到的结果
# 测试场景
.
├── head.h -> 发布的头文件
├── libcalc.a -> 发布的静态库
└── main.c -> 编写的测试程序, 测试静态库中的函数
# 编译测试程序
$ gcc main.c -o app
/tmp/cchYTdck.o: In function `main':
main.c:(.text+0x38): undefined reference to `add'
main.c:(.text+0x58): undefined reference to `subtract'
main.c:(.text+0x78): undefined reference to `multiply'
main.c:(.text+0x98): undefined reference to `divide'
collect2: error: ld returned 1 exit status
# 上述错误原因: 只能找到函数声明, 找不到定义, 函数定义在静态库中
# 在编译的时候需要指定静态库
# -L: 静态库存储的路径(相对/绝对)
# -l: 指定库的名字, 掐头(lib), 去尾(.a)
# gcc main.c -o app -L 库的路径 -l库的名字
gcc main.c -o app -L ./ -l calc
2. 使用libXXX.a的Makefile
# 根据main.c libcalc.a head.h
# 生成可执行文件 execute
#---------------------------------------------------------
# 工程名 计算简单的加减乘除
PROJECT = execute
#---------------------------------------------------------
# .o文件
SrcSuf = c
ObjSuf = o
LibSuf = a
OBJFILES = ./main.$(ObjSuf)\
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH = -I../include/
#---------------------------------------------------------
#依赖库所在的目录
LIBPATH= -L../
#---------------------------------------------------------
#依赖的静态库
LIBS=../libcalc.a
#---------------------------------------------------------
CC = gcc
CFlag = -w -g -ggdb -fshort-wchar -std=c11 $(INCLUDEPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)
all: $(PROJECT) clean
$(PROJECT):$(OBJFILES)
@echo "creating $(PROJECT) start..."
$(CC) $(OBJFILES) -o $(PROJECT) $(INCLUDEPATH) $(LIBPATH) $(LIBS)
@echo "creating $(PROJECT) end"
#%.o:%.c
# $(CC) -MM $^
# $(CC) $(CXXFLAG) -c $^ $(INCLUDEPATH) -o $@
clean:
@echo "Cleaning $(PROJECT) project files"
@rm -f $(OBJFILES) core
.$(SrcSuf).$(ObjSuf):
@echo "Compiling $(PROJECT) $<"
$(CC) $(CFlag) -c $< -o $@
2.3 动态库/共享库
共享 -> 共享内存中的库
-
命名规则
- linux:
- libxxx.so
- lib -> 前缀
- .so -> 后缀
- xxx -> 库的名字, 随意
- 在linux中是一个可执行文件
- libxxx.so
- windows:
- 由vs制作
- libxxx.dll
- libxxx.lib
- 非vs
- libxxx.dll
- 由vs制作
- linux:
-
动态库制作
# 源文件 -> 动态库的过程
.
├── add.c
├── div.c
├── include
│ └── head.h
├── main.c
├── mult.c
└── sub.c
# .c -> .o
# -fpic 参数必须要加 == -fPIC
$ gcc -c add.c div.c mult.c sub.c -I ./include/ -fPIC
# .o 打包 -> .so
# 使用了参数: shared
$ gcc -shared -o libcalc.so *.o
robin@OS:~/Linux/3Day/calc$ ls
add.c add.o div.c div.o include `libcalc.so` main.c mult.c mult.o sub.c sub.o
-
动态库使用
# 测试动态库函数是不是能正常工作 # 根据头文件中的函数声明, 对这些函数进行调用, 测试得到的结果 # 测试场景 . ├── head.h -> 发布的头文件 ├── libcalc.so -> 发布动态库 └── main.c -> 测试程序 # 编译测试程序 # -L: 动态库的路径 # -l: 指定动态库的名字, 掐头(lib)去尾(.so) $ gcc main.c -L ./ -l calc -o app
查看动态库的依赖
#1. 查看依赖的库: objdump -x libName.so | grep NEEDED #2.查看动态库的依赖是否完备 $ ldd libName.so #如果某个依赖的库不存在,会输出类似 OOXX.so not found 字样。 # 举例如下 [root@lwh test]# locate libmysqlclient.so /usr/lib64/mysql/libmysqlclient.so [root@lwh test]# ldd /usr/lib64/mysql/libmysqlclient.so linux-vdso.so.1 => (0x00007ffc731d6000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f842cf82000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f842cd7e000) libssl.so.10 => /lib64/libssl.so.10 (0x00007f842cb0c000) libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f842c6a9000) librt.so.1 => /lib64/librt.so.1 (0x00007f842c4a1000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f842c19a000) libm.so.6 => /lib64/libm.so.6 (0x00007f842be98000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f842bc82000) libc.so.6 => /lib64/libc.so.6 (0x00007f842b8b4000) /lib64/ld-linux-x86-64.so.2 (0x00007f842da9e000) libgssapi_krb5.so.2 => /lib64/libgssapi_krb5.so.2 (0x00007f842b667000) libkrb5.so.3 => /lib64/libkrb5.so.3 (0x00007f842b37e000) libcom_err.so.2 => /lib64/libcom_err.so.2 (0x00007f842b17a000) libk5crypto.so.3 => /lib64/libk5crypto.so.3 (0x00007f842af47000) libz.so.1 => /lib64/libz.so.1 (0x00007f842ad31000) libkrb5support.so.0 => /lib64/libkrb5support.so.0 (0x00007f842ab21000) libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007f842a91d000) libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f842a704000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f842a4dd000) libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f842a27b000) [root@lwh test]# objdump -x /usr/lib64/mysql/libmysqlclient.so | grep NEEDED NEEDED libpthread.so.0 NEEDED libdl.so.2 NEEDED libssl.so.10 NEEDED libcrypto.so.10 NEEDED librt.so.1 NEEDED libstdc++.so.6 NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6 NEEDED ld-linux-x86-64.so.2 [root@lwh test]#
-
运行可执行程序提示找不到动态库
$ ./app ./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
-
动态库和静态库是如何工作的
/* 静态库: libcalc.a - 链接命令: gcc main.c -o app -L ./ -l calc - 静态库通过上边的命令, 被打包了可执行程序app中 - 启动可执行程序app的时候, 静态库被加载到内存中(代码区), 因此这些代码在内存中的位置是固定的 - 在程序中使用静态库中的函数的时候, 不需要额外加载, 直接调用即可 - 效率高 */ /* 动态库: libcalc.so - 链接命令: gcc main.c -o app -L ./ -l calc - 动态库是不会被打包到可执行程序中, 只是将动态库名和其中的函数进行了记录, 动态库没被加载到内存中 - app在运行过程中, 要调用动态中的函数的时候, 动态库才会被加载到内存 - 加载动态需要使用系统的动态链接器, 动态链接器按照以下路径去搜索:(优先级) - DT_RPATH段 -> 无法修改, 跳过 - 环境变量LD_LIBRARY_PATH - /etc/ld.so.cache 文件 - /lib/, /usr/lib目录 - 在以上文件/目录中动态链接器都没有找到动态库的存储目录, 提示这个库找不到无法加载 - 多个不相关的程序是可以共同使用加载到内存中的动态库的 */
解决动态库找不到的错误
# 方式1: 修改环境变量: LD_LIBRARY_PATH
# 环境变量取值: $LD_LIBRARY_PATH
# 1. 临时设置: (关闭当前终端或在其他终端中都是无效的)
# 找到到动态库的绝对路径: /home/robin/Linux/3Day/mytest
# 将找到的路径加入环境变量中: (在当前终端中执行)
# export LD_LIBRARY_PATH=/home/lwh/Desktop/study/03dynamicLib/main:$LD_LIBRARY_PATH
# 2. 永久设置:
# 将第一步中的export写入配置文件
# 用户级别: ~/.bashrc
# 系统级别: /etc/profile
# 将 export LD_LIBRARY_PATH=/home/robin/Linux/3Day/mytest:$LD_LIBRARY_PATH
# 写入上述任一文件中
# 重新加载设置: (不执行以下命令, 可以关闭当前终端在重新打开)
# . ~/.bahsrc . == source
# . /etc/profile
# 方式2, 找/etc/ld.so.conf文件
# 1. 将动态库的绝对路径写入到这个文件的空行中, 保存退出
# 2. /etc/ld.so.conf 更新到 -> /etc/ld.so.cache 文件
# sudo ldconfig
# 方式3: 将动态库移动/拷贝 到 /lib, /usr/lib任意目录中都可以
1. 生成libXXX.so的Makefile
# 根据 *.c 和 .h
# 生成动态库 libcalc.so
#---------------------------------------------------------
# 工程名 / 库名 # 生成静态库 libcalc.so 计算简单的加减乘除
PROJECT = libcalc
OUTNAME = $(PROJECT).so
#---------------------------------------------------------
# 头文件目录
INCLUDEPATH = -I./include/
#---------------------------------------------------------
CC = gcc
LD = gcc
#---------------------------------
# 编译依赖项
CFlag = -w -g -ggdb -fshort-wchar -std=c11 -fPIC $(INCLUDEPATH)
#---------------------------------
# 链接依赖项
LIBEXPORT = -fvisibility=hidden
.DEFINES = -DTRACE_LOG
CxxFlage = -shared $(LIBEXPORT) $(.DEFINES)
#---------------------------------------------------------
# .o文件
SrcSuf = c
ObjSuf = o
LibSuf = so
OBJFILES = ./add.$(ObjSuf)\
./div.$(ObjSuf)\
./mult.$(ObjSuf)\
./sub.$(ObjSuf)\
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)
all: $(PROJECT) clean
$(PROJECT):$(OBJFILES)
@echo "Linking $(OUTNAME) start..."
$(LD) $(OBJFILES) -o $(OUTNAME) $(CxxFlage) -ldl
@echo "Linking $(OUTNAME) end"
clean:
@echo "Cleaning $(PROJECT) project files"
@rm -f $(OBJFILES) core
.$(SrcSuf).$(ObjSuf):
@echo "Compiling $(PROJECT) $<"
$(CC) $(CFlag) -c $< -o $@
2. 使用libXXX.so的Makefile
# 根据main.c libcalc.so head.h
# 生成可执行文件 execute
#---------------------------------------------------------
# 工程名 计算简单的加减乘除
PROJECT = execute
#---------------------------------------------------------
# .o文件
SrcSuf = c
ObjSuf = o
LibSuf = so
OBJFILES = ./main.$(ObjSuf)
#---------------------------------------------------------
#依赖的静态库库
#LIBS = -l../libcalc.a
#依赖的动态库
LIBS2 = -l calc
#---------------------------------------------------------
CC = gcc
# 头文件目录
INCLUDEPATH = -I../include/
#依赖库所在的目录
LIBPATH = -L../
# 编译依赖项
CFlag = -w -g -ggdb -fshort-wchar -std=c11 $(INCLUDEPATH)
# 链接依赖项
CxxFlage = $(INCLUDEPATH) $(LIBPATH)
#---------------------------------------------------------
.SUFFIXES: .$(SrcSuf) .$(ObjSuf) .$(LibSuf)
all: $(PROJECT) clean
$(PROJECT):$(OBJFILES)
@echo "creating $(PROJECT) start..."
$(CC) $(OBJFILES) -o $(PROJECT) $(CxxFlage) $(LIBS2)
@echo "creating $(PROJECT) end"
clean:
@echo "Cleaning $(PROJECT) project files"
@rm -f $(OBJFILES) core
.$(SrcSuf).$(ObjSuf):
@echo "Compiling $(PROJECT) $<"
$(CC) $(CFlag) -c $< -o $@
2.4 对比
-
静态库
- 优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
- 缺点:
- 消耗系统资源,浪费内存
- 更新、部署、发布麻烦。
-
动态库
-
优点:
-
- 可实现进程间资源共享
- 程序升级简单
- 程序猿可以控制何时加载动态库
-
缺点:
-
- 加载速度比静态库慢
- 发布程序需要提供依赖的动态库
-
vim插件-vimplus
# vim插件
https://github.com/chxuan/vimplus