【Linux】21.基础IO(3)

文章目录

  • 3. 动态库和静态库
    • 3.1 静态库与动态库
    • 3.2 静态库的制作和使用原理
    • 3.3 动态库的制作和使用原理
      • 3.3.1 动态库是怎么被加载的
    • 3.4 关于地址


3. 动态库和静态库

3.1 静态库与动态库

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库

  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码

  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking

  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。


3.2 静态库的制作和使用原理

mymath.h

#pragma once#include <stdio.h>int add(int x, int y);//加
int sub(int x, int y);//减
int mul(int x, int y);//乘
int div(int x, int y);//除

mymath.c

#include "mymath.h"int myerrno = 0;int add(int x, int y){return x + y;
}int sub(int x, int y){return x - y;
}int mul(int x, int y){return x * y;
}int div(int x, int y){if(y == 0){myerrno = 1;return -1;}return x / y;
}

makefile

lib=libmymath.a$(lib):mymath.oar -rc $@ $^
mymath.o:mymath.cgcc -c $^.PHONY:clean
clean:rm -rf *.o *.a lib.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib

make后:

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ ll
total 28
drwxrwxr-x  2 ydk_108 ydk_108 4096 Jan 24 22:18 ./
drwxrwxr-x 17 ydk_108 ydk_108 4096 Jan 24 22:07 ../
-rw-rw-r--  1 ydk_108 ydk_108 2024 Jan 24 22:18 libmymath.a
-rw-rw-r--  1 ydk_108 ydk_108  229 Jan 24 22:18 makefile
-rw-rw-r--  1 ydk_108 ydk_108  276 Jan 24 22:13 mymath.c
-rw-rw-r--  1 ydk_108 ydk_108  147 Jan 24 22:12 mymath.h
-rw-rw-r--  1 ydk_108 ydk_108 1848 Jan 24 22:18 mymath.o
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ 

make output后:

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ make output
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ ll
total 32
drwxrwxr-x  3 ydk_108 ydk_108 4096 Jan 24 22:22 ./
drwxrwxr-x 17 ydk_108 ydk_108 4096 Jan 24 22:07 ../
drwxrwxr-x  4 ydk_108 ydk_108 4096 Jan 24 22:22 lib/
-rw-rw-r--  1 ydk_108 ydk_108 2024 Jan 24 22:18 libmymath.a
-rw-rw-r--  1 ydk_108 ydk_108  229 Jan 24 22:18 makefile
-rw-rw-r--  1 ydk_108 ydk_108  276 Jan 24 22:13 mymath.c
-rw-rw-r--  1 ydk_108 ydk_108  147 Jan 24 22:12 mymath.h
-rw-rw-r--  1 ydk_108 ydk_108 1848 Jan 24 22:18 mymath.o
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ tree lib
lib
├── include
│   └── mymath.h
└── mymathlib└── libmymath.a2 directories, 2 files
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ 

然后在lesson23这个目录下创建test目录,把lib复制到test目录下

同时在test目录下创建main.c文件

#include "lib/include/mymath.h"int main(){printf("1+1=%d\n",add(1,1));return 0;
}

如果main.c文件的头文件写的是#include "mymath.h"

那么直接gcc是无法编译的,可以用gcc main.c -I ./lib/include/

当然如果头文件是#include "lib/include/mymath.h"就可以直接gcc了。

gcc编译后:

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ gcc main.c
/usr/bin/ld: /tmp/ccErjU5m.o: in function `main':
main.c:(.text+0x13): undefined reference to `add'
collect2: error: ld returned 1 exit status
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$

因为gcc默认去系统的默认库里面找的,所以我们要加点东西:

gcc main.c -L ./lib/mymathlib/ -lmymath

这里的-L表示的是在lib库里面,

-lmymath表示是在./lib/mymathlib/库里面的libmymath.a库文件,这里要去掉lib前缀和.a后缀。-l表示库名称。

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ gcc main.c -L ./lib/mymathlib/ -lmymath
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 24 22:40 ./
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 24 22:24 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 24 22:40 a.out*
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 24 22:22 lib/
-rw-rw-r-- 1 ydk_108 ydk_108    90 Jan 24 22:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ 

可是gcc main.c -L ./lib/mymathlib/ -lmymath这个东西太长了,我们可不可以不写这么长的呢?

我们可以把mymath.hlibmymath.a放到系统默认路径下,这个操作也叫库的安装

这样只需要gcc main.c -lmymath就可以了。

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 25 10:26 ./
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 25 10:27 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:26 a.out*
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108    90 Jan 25 10:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ sudo cp lib/include/mymath.h /usr/include/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ls /usr/include/mymath.h
/usr/include/mymath.h
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ sudo cp lib/mymathlib/libmymath.a /lib64/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ls /lib64/libmymath.a
/lib64/libmymath.a
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c
/usr/bin/ld: /tmp/ccDIK8p6.o: in function `main':
main.c:(.text+0x13): undefined reference to `add'
collect2: error: ld returned 1 exit status
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c -lmymath
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 25 10:52 ./
drwxrwxr-x 3 ydk_108 ydk_108  4096 Jan 25 10:27 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:52 a.out*
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108    90 Jan 25 10:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ 

不过不建议我们自己乱加文件,这样可能会在以后背刺自己,造成冲突。


注意:如果我们把main.c写成这样

#include "lib/include/mymath.h"int main(){printf("10/0=%d, error=%d\n",div(10,0),myerrno);return 0;
}       

那么运行结果就是

10/0=-1, error=0

为什么会这样呢?这里的运算一看就出错了,为什么error不是1呢?

因为C语言默认是从右向左运算的,这里先把myerrno的值传进来才传div(10,0)的值。

我们可以修改一下代码:

#include "lib/include/mymath.h"int main(){int n = div(10,0);printf("10/0=%d, error=%d\n",n,myerrno);return 0;
}       

打印:

10/0=-1, error=1

把我们提供的方法,给别人用有两个方法:

  1. 我把源文件直接给他

  2. 把我们的源代码想办法打包成库 =库+.h

  1. 第三方库,往后使用的时候,必定要是用gcc-l

  2. 深刻理解errno的本质

  3. 如果系统中只提供静态链接,gcc则只能对该库进行静态链接

  4. 如果系统中需要链接多个库,则gcc可以链接多个库

  5. 不带static有动态库就动态编译,只有静态库才静态编译。带static只静态编译。


3.3 动态库的制作和使用原理

makefile

# 定义动态库名称
dy-lib=libmymethod.so
# 定义静态库名称
static-lib=libmymath.a# 声明all为伪目标(不是实际文件)
.PHONY:all
# 默认目标 - 构建动态库和静态库
all: $(dy-lib) $(static-lib)# 从mymath.o创建静态库的规则
$(static-lib):mymath.oar -rc $@ $^     # 创建归档文件,如存在则替换,创建索引
mymath.o:mymath.cgcc -c $^        # 将C文件编译为目标文件# 从mylog.o和myprint.o创建动态库的规则
$(dy-lib):mylog.o myprint.ogcc -shared -o $@ $^    # 将目标文件链接成共享库
mylog.o:mylog.cgcc -fPIC -c $^         # 使用位置无关代码编译
myprint.o:myprint.cgcc -fPIC -c $^         # 使用位置无关代码编译# 清理目标 - 删除所有生成的文件
.PHONY:clean
clean:rm -rf *.o *.a *.so mylib# 输出目标 - 创建发布目录结构
.PHONY:output
output:mkdir -p mylib/include  # 创建头文件目录mkdir -p mylib/lib      # 创建库文件目录cp *.h mylib/include    # 复制头文件cp *.a mylib/lib        # 复制静态库cp *.so mylib/lib       # 复制动态库

myprint.h

#pragma once#include <stdio.h>void Print();

myprint.c

#include "myprint.h"void Print()
{printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");
}

mylog.h

#pragma once#include <stdio.h>void Log(const char*);

mylog.c

#include "mylog.h"void Log(const char*info)
{printf("Warning: %s\n", info);
}

然后编译:make ; make output

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ make ; make output
gcc -fPIC -c mylog.c
gcc -fPIC -c myprint.c
gcc -shared -o libmymethod.so mylog.o myprint.o
gcc -c mymath.c
ar -rc libmymath.a mymath.o
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ ll
total 76
drwxrwxr-x  4 ydk_108 ydk_108  4096 Jan 25 13:50 ./
drwxrwxr-x 18 ydk_108 ydk_108  4096 Jan 25 10:26 ../
-rw-rw-r--  1 ydk_108 ydk_108  2024 Jan 25 13:50 libmymath.a
-rwxrwxr-x  1 ydk_108 ydk_108 16312 Jan 25 13:50 libmymethod.so*
-rw-rw-r--  1 ydk_108 ydk_108   448 Jan 25 13:34 makefile
drwxrwxr-x  4 ydk_108 ydk_108  4096 Jan 25 13:50 mylib/
-rw-rw-r--  1 ydk_108 ydk_108    85 Jan 25 13:34 mylog.c
-rw-rw-r--  1 ydk_108 ydk_108    58 Jan 25 13:34 mylog.h
-rw-rw-r--  1 ydk_108 ydk_108  1704 Jan 25 13:50 mylog.o
-rw-rw-r--  1 ydk_108 ydk_108   276 Jan 25 10:26 mymath.c
-rw-rw-r--  1 ydk_108 ydk_108   147 Jan 25 10:26 mymath.h
-rw-rw-r--  1 ydk_108 ydk_108  1848 Jan 25 13:50 mymath.o
-rw-rw-r--  1 ydk_108 ydk_108   176 Jan 25 13:35 myprint.c
-rw-rw-r--  1 ydk_108 ydk_108    49 Jan 25 13:35 myprint.h
-rw-rw-r--  1 ydk_108 ydk_108  1864 Jan 25 13:50 myprint.o
drwxrwxr-x  3 ydk_108 ydk_108  4096 Jan 25 10:52 test/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ tree mylib
mylib
├── include
│   ├── mylog.h
│   ├── mymath.h
│   └── myprint.h
└── lib├── libmymath.a└── libmymethod.so2 directories, 5 files
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ 

这里的执行过程是:

  1. 首先执行 make,因为没有指定目标,所以默认执行 Makefile 中的第一个目标 all,完成所有库文件的编译和创建
  2. 然后执行 make output,创建目录结构并复制文件

这样写的原因是:

  • 必须先执行 make 生成所有的库文件(.so.a)
  • 再执行 make output 进行文件整理和复制
  • 如果直接执行 make output,可能会因为库文件还未生成而导致复制失败

然后我们把库复制进test文件夹里面,并且把main.c文件改一下:

#include "mylog.h"
#include "myprint.h"int main()
{Print();Log("hello log function");return 0;
}

运行

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 40
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 14:00 ./
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 13:50 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:52 a.out* # 之前静态编译留下的
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108   116 Jan 25 13:57 main.c
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 14:00 mylib/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c -I mylib/include/ -L mylib/lib -lmymethod
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 40
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 14:00 ./
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 13:50 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16712 Jan 25 14:00 a.out* # 现在动态编译新生成的
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108   116 Jan 25 13:57 main.c
drwxrwxr-x 4 ydk_108 ydk_108  4096 Jan 25 14:00 mylib/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ 

虽然编译过了,但是一运行就这样了:

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ./a.out
./a.out: error while loading shared libraries: libmymethod.so: cannot open shared object file: No such file or directory

为什么呢?

我们之前编译的时候告诉编译器我们的动态库在哪里了,但是我们编译后,并没有告诉系统(即加载器)我们的动态库在哪里。

可以将自己库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中。

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffc9cd79000)libmymethod.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa1947d3000)/lib64/ld-linux-x86-64.so.2 (0x00007fa1949d3000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ydk_108/108/lesson24/test/mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ echo $LD_LIBRARY_PATH
::/home/ydk_108/108/lesson24/test/mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffd6835f000)libmymethod.so => /home/ydk_108/108/lesson24/test/mylib/lib/libmymethod.so (0x00007f105f59b000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f105f3a2000)/lib64/ld-linux-x86-64.so.2 (0x00007f105f5a7000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ./a.out
hello new world!
hello new world!
hello new world!
hello new world!
Warning: hello log function
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ 

ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ su
Password: 
root@iZuf68hz06p6s2809gl3i1Z:/home/ydk_108/108/lesson24/test# cd /etc/ld.so.conf.d
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ll
total 20
drwxr-xr-x  2 root root 4096 Jun  3  2024 ./
drwxr-xr-x 89 root root 4096 Jan 14 19:45 ../
-rw-r--r--  1 root root   38 Sep  7  2019 fakeroot-x86_64-linux-gnu.conf
-rw-r--r--  1 root root   44 Apr 15  2020 libc.conf
-rw-r--r--  1 root root  100 Apr 15  2020 x86_64-linux-gnu.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# touch ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ll
total 20
drwxr-xr-x  2 root root 4096 Jan 25 14:25 ./
drwxr-xr-x 89 root root 4096 Jan 14 19:45 ../
-rw-r--r--  1 root root   38 Sep  7  2019 fakeroot-x86_64-linux-gnu.conf
-rw-r--r--  1 root root   44 Apr 15  2020 libc.conf
-rw-r--r--  1 root root  100 Apr 15  2020 x86_64-linux-gnu.conf
-rw-r--r--  1 root root    0 Jan 25 14:25 ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# pwd
/etc/ld.so.conf.d
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# vim ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ldconfig
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# 
# ldconfig前
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffc8e125000)libmymethod.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4e24c2d000)/lib64/ld-linux-x86-64.so.2 (0x00007f4e24e2d000)
# ldconfig后
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007fffa231c000)libmymethod.so => /home/ydk_108/108/lesson24/test/mylib/lib/libmymethod.so (0x00007f9b2d7b8000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9b2d5c6000)/lib64/ld-linux-x86-64.so.2 (0x00007f9b2d7cb000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ 

解决加载找不到动态库的方法:

  1. 拷贝到系统默认的库路径 /ib64 /usr/lib64/

  2. 在系统默认的库路径 /ib64 /usr/lib64/下建立软连接

  3. 将自己的库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中。(重启就消失了)

  4. /etc/ld.so.conf.d 建立自己的动态库路径的配置文件(名字随便写,里面路径写对就行),然后重新ldconfig即可。(重启不会消失)

实际情况,我们用的库都是别人的成熟的库,都采用直接安装到系统的方式。


3.3.1 动态库是怎么被加载的

动态库(共享库)的共享机制:

  1. 内存共享机制
  • 当第一个进程加载动态库时,动态库的代码段被加载到物理内存中
  • 后续进程再使用这个动态库时,不会重新加载代码段,而是直接映射到已加载的物理内存
  • 多个进程共享同一份物理内存中的代码段,节省内存资源
  • 每个进程有自己的数据段副本,确保数据隔离

动态库在进程运行的时候,是要被加载的(静态库没有)

所以,动态库在系统中加载之后,会被所有进程共享。

9e45eb35a7ab2f9f8cb634e75555f4e3

这个共享库一旦被多个进程共享,那么它对应的页就会引入计数2,缺页中断的时候,识别发现是被多个进程共享的就会写时拷贝。


3.4 关于地址

程序没有加载前的地址(程序)

程序编译好之后,内部有地址的概念吗?

有的。(可以联想之前C++多态的虚函数表)

库可以在虚拟内存中,任意位置加载。

怎么做到的呢?

让自己内部函数不要采用绝对编址,只表示每个函数在库中的偏移量即可。

例如:printf的地址0x1122,这个0x1122代表printf相对于库的起始地址的偏移量。

然后加载动态库的时候,在地址空间里随便放。只需要记住这个库在虚拟地址空间的起始地址就可以了。

解释:为什么动态库可以被加载到任意位置,并且多个进程可以共享同一份代码?

  1. 假设有一个动态库 libexample.so:
起始位置: 未知 (加载时才确定)
函数A: +0x100 (偏移量)
函数B: +0x200 (偏移量)
函数C: +0x300 (偏移量)
  1. 不同进程加载时的情况:
进程1:
- 库加载到虚拟地址 0x10000000
- 函数A实际地址 = 0x10000000 + 0x100 = 0x10000100
- 函数B实际地址 = 0x10000000 + 0x200 = 0x10000200进程2:
- 库加载到虚拟地址 0x20000000
- 函数A实际地址 = 0x20000000 + 0x100 = 0x20000100
- 函数B实际地址 = 0x20000000 + 0x200 = 0x20000200
  1. 工作原理:
  • 编译时:函数地址全部用偏移量表示
  • 加载时:
    • 动态链接器选择一个可用的虚拟地址空间
    • 记录库的基地址
    • 所有函数调用时:实际地址 = 基地址 + 偏移量

这就好比:

  • 一本书的目录不用页码,而用"距离书开始的页数"
  • 无论这本书放在书架的哪个位置(起始地址)
  • 只要知道书在哪(基地址),就能通过偏移量找到每个章节

这也就是为什么fPIC叫做产生位置无关码

  1. 静态库为什么不谈加载呢?

因为静态库会直接把程序拷贝到可执行程序里面,就谈不上加载了。

  1. 静态库为什么不说与位置无关呢?

因为库方法拷贝到可执行程序里后,就不谈偏移量了。他在什么位置就是确定的了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7639.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统之gzip命令的基本使用

Linux系统之gzip命令的基本使用 一、gzip命令简介二、gzip命令使用帮助2.1 help帮助信息2.2 选项解释 三、gzip命令的基本使用3.1 压缩文件3.2 保留原始文件3.3 解压文件3.4 查看压缩信息3.5 标准输出/输入3.6 批量处理文件3.7 递归解压缩目录3.8测试压缩文件完整性 四、注意事…

【Matlab高端绘图SCI绘图模板】第05期 绘制高阶折线图

1.折线图简介 折线图是一个由点和线组成的统计图表&#xff0c;常用来表示数值随连续时间间隔或有序类别的变化。在折线图中&#xff0c;x 轴通常用作连续时间间隔或有序类别&#xff08;比如阶段1&#xff0c;阶段2&#xff0c;阶段3&#xff09;。y 轴用于量化的数据&#x…

免费SSL证书申请,springboot 部署证书

申请免费域名证书,SSL证书(一共有两个有用&#xff0c;一个是私钥private.key 另一个是certificate.crt) 1、打开网址 申请免费域名证书,SSL证书 2、选择生成CSR 3、生成以后点击下一步&#xff08;private key 有用 ) ​​​​​​​ 4、这里选择 Cname域名解析验证 5、…

Java 大视界 -- Java 大数据中的隐私增强技术全景解析(64)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

深度学习项目--基于LSTM的糖尿病预测探究(pytorch实现)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 LSTM模型一直是一个很经典的模型&#xff0c;一般用于序列数据预测&#xff0c;这个可以很好的挖掘数据上下文信息&#xff0c;本文将使用LSTM进行糖尿病…

js/ts数值计算精度丢失问题及解决方案

文章目录 概念及问题问题分析解决方案方案一方案二方案其它——用成熟的库 概念及问题 js中处理浮点数运算时会出现精度丢失。js中整数和浮点数都属于Number数据类型&#xff0c;所有的数字都是以64位浮点数形式存储&#xff0c;整数也是如此。所以打印x.00这样的浮点数的结果…

vite环境变量处理

环境变量: 会根据当前代码环境产生值的变化的变量就叫做环境变量 代码环境: 开发环境测试环境预发布环境灰度环境生产环境 举例: 百度地图 SDK,小程序的SDK APP_KEY: 测试环境和生产环境还有开发环境是不一样的key 开发环境: 110 生产环境:111 测试环境: 112 我们去请求第三…

Android GLSurfaceView 覆盖其它控件问题 (RK平台)

平台 涉及主控: RK3566 Android: 11/13 问题 在使用GLSurfaceView播放视频的过程中, 增加了一个播放控制面板, 覆盖在视频上方. 默认隐藏setVisibility(View.INVISIBLE);点击屏幕再显示出来. 然而, 在RK3566上这个简单的功能却无法正常工作. 通过缩小视频窗口可以看到, 实际…

Linux 环境变量

目录 一、环境变量的基本概念 1.常见环境变量 2.查看环境变量方法 ​3.几个环境变量 环境变量&#xff1a;PATH 环境变量&#xff1a;HOME 环境变量&#xff1a;SHELL 二、和环境变量相关的命令 三、库函数getenv&#xff0c;setenv 四、环境变量和本地变量 五、命令行…

Redis实战(黑马点评)——涉及session、redis存储验证码,双拦截器处理请求

项目整体介绍 数据库表介绍 基于session的短信验证码登录与注册 controller层 // 获取验证码PostMapping("code")public Result sendCode(RequestParam("phone") String phone, HttpSession session) {return userService.sendCode(phone, session);}// 获…

MYSQL数据库 - 启动与连接

MYSQL数据库的启动&#xff1a; 一 在cmd控制界面以管理员身份运行 执行语句: net start mysql80 net stop mysql80 二 MYSQL数据库客户端建立连接&#xff1a; 1 该种方法是使用windows系统的cmd界面&#xff0c;需要配置相关路径path 2 使用MYSQL自带的

【Salesforce】审批流程,代理登录 tips

审批流程权限 审批流程权限问题解决方案代理登录代理登录后Logout 审批流程权限 前几天&#xff0c;使用审批流程&#xff0c;但是是两个sandbox&#xff0c;同样的配置&#xff0c;我有管理员权限。但是profile不是管理员&#xff0c;只是通过具备管理员权限的permission set…

RDMA 工作原理 | 支持 RDMA 的网络协议

注&#xff1a;本文为 “RDMA” 相关文章合辑。 英文引文机翻未校。 图片清晰度受引文所限。 Introduction to Remote Direct Memory Access (RDMA) Written by: Dotan Barak on March 31, 2014.on February 13, 2015. What is RDMA? 什么是 RDMA&#xff1f; Direct me…

hexo + Butterfly搭建博客

Hexo‌是一个基于Node.js的静态网站生成器&#xff0c;主要用于快速搭建博客和个人网站。它使用Markdown语法编写文章&#xff0c;能够迅速生成静态页面并部署到服务器上。 配置node 使用nvm安装node(v16.13.2)后配置镜像 安装并使用node&#xff1a; nvm install 16.13.2 n…

手撕B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

【2025年数学建模美赛F题】(顶刊论文绘图)模型代码+论文

全球网络犯罪与网络安全政策的多维度分析及效能评估 摘要1 Introduction1.1 Problem Background1.2Restatement of the Problem1.3 Literature Review1.4 Our Work 2 Assumptions and Justifications数据完整性与可靠性假设&#xff1a;法律政策独立性假设&#xff1a;人口统计…

【FreeRTOS 教程 四】队列创建与发布项目到队列

目录 一、FreeRTOS队列&#xff1a; &#xff08;1&#xff09;队列介绍&#xff1a; &#xff08;2&#xff09;用户模型说明&#xff1a; &#xff08;3&#xff09;阻塞队列&#xff1a; 二、队列管理 API&#xff1a; &#xff08;1&#xff09;uxQueueMessagesWaiti…

如何在data.table中处理缺失值

&#x1f4ca;&#x1f4bb;【R语言进阶】轻松搞定缺失值&#xff0c;让数据清洗更高效&#xff01; &#x1f44b; 大家好呀&#xff01;今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值&#xff0c;并且提供了一个自定义函数calculate_missing_va…

基于OpenCV实现的答题卡自动判卷系统

一、图像预处理 🌄 二、查找答题卡轮廓 📏 三、透视变换 🔄 四、判卷与评分 🎯 五、主函数 六、完整代码+测试图像集 总结 🌟 在这篇博客中,我将分享如何使用Python结合OpenCV库开发一个答题卡自动判卷系统。这个系统能够自动从扫描的答题卡中提取信…

C++ list 容器用法

C list 容器用法 C 标准库提供了丰富的功能&#xff0c;其中 <list> 是一个非常重要的容器类&#xff0c;用于存储元素集合&#xff0c;支持双向迭代器。<list> 是 C 标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它允许在容器的任意位置快速…