【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.h
和libmymath.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
把我们提供的方法,给别人用有两个方法:
我把源文件直接给他
把我们的源代码想办法打包成库 =
库+.h
第三方库,往后使用的时候,必定要是用
gcc-l
深刻理解
errno
的本质如果系统中只提供静态链接,
gcc
则只能对该库进行静态链接如果系统中需要链接多个库,则
gcc
可以链接多个库不带
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$
这里的执行过程是:
- 首先执行
make
,因为没有指定目标,所以默认执行 Makefile 中的第一个目标all
,完成所有库文件的编译和创建- 然后执行
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$
解决加载找不到动态库的方法:
拷贝到系统默认的库路径
/ib64 /usr/lib64/
在系统默认的库路径
/ib64 /usr/lib64/
下建立软连接将自己的库所在的路径,添加到系统的环境变量
LD_LIBRARY_PATH
中。(重启就消失了)
/etc/ld.so.conf.d
建立自己的动态库路径的配置文件(名字随便写,里面路径写对就行),然后重新ldconfig
即可。(重启不会消失)
实际情况,我们用的库都是别人的成熟的库,都采用直接安装到系统的方式。
3.3.1 动态库是怎么被加载的
动态库(共享库)的共享机制:
- 内存共享机制
- 当第一个进程加载动态库时,动态库的代码段被加载到物理内存中
- 后续进程再使用这个动态库时,不会重新加载代码段,而是直接映射到已加载的物理内存
- 多个进程共享同一份物理内存中的代码段,节省内存资源
- 每个进程有自己的数据段副本,确保数据隔离
动态库在进程运行的时候,是要被加载的(静态库没有)
所以,动态库在系统中加载之后,会被所有进程共享。
这个共享库一旦被多个进程共享,那么它对应的页就会引入计数2,缺页中断的时候,识别发现是被多个进程共享的就会写时拷贝。
3.4 关于地址
程序没有加载前的地址(程序)
程序编译好之后,内部有地址的概念吗?
有的。(可以联想之前C++多态的虚函数表)
库可以在虚拟内存中,任意位置加载。
怎么做到的呢?
让自己内部函数不要采用绝对编址,只表示每个函数在库中的偏移量即可。
例如:printf的地址0x1122,这个0x1122代表printf相对于库的起始地址的偏移量。
然后加载动态库的时候,在地址空间里随便放。只需要记住这个库在虚拟地址空间的起始地址就可以了。
解释:为什么动态库可以被加载到任意位置,并且多个进程可以共享同一份代码?
- 假设有一个动态库 libexample.so:
起始位置: 未知 (加载时才确定)
函数A: +0x100 (偏移量)
函数B: +0x200 (偏移量)
函数C: +0x300 (偏移量)
- 不同进程加载时的情况:
进程1:
- 库加载到虚拟地址 0x10000000
- 函数A实际地址 = 0x10000000 + 0x100 = 0x10000100
- 函数B实际地址 = 0x10000000 + 0x200 = 0x10000200进程2:
- 库加载到虚拟地址 0x20000000
- 函数A实际地址 = 0x20000000 + 0x100 = 0x20000100
- 函数B实际地址 = 0x20000000 + 0x200 = 0x20000200
- 工作原理:
- 编译时:函数地址全部用偏移量表示
- 加载时:
- 动态链接器选择一个可用的虚拟地址空间
- 记录库的基地址
- 所有函数调用时:实际地址 = 基地址 + 偏移量
这就好比:
- 一本书的目录不用页码,而用"距离书开始的页数"
- 无论这本书放在书架的哪个位置(起始地址)
- 只要知道书在哪(基地址),就能通过偏移量找到每个章节
这也就是为什么fPIC
叫做产生位置无关码
。
- 静态库为什么不谈加载呢?
因为静态库会直接把程序拷贝到可执行程序里面,就谈不上加载了。
- 静态库为什么不说与位置无关呢?
因为库方法拷贝到可执行程序里后,就不谈偏移量了。他在什么位置就是确定的了。
相关文章:

【Linux】21.基础IO(3)
文章目录 3. 动态库和静态库3.1 静态库与动态库3.2 静态库的制作和使用原理3.3 动态库的制作和使用原理3.3.1 动态库是怎么被加载的 3.4 关于地址 3. 动态库和静态库 3.1 静态库与动态库 静态库(.a):程序在编译链接的时候把库的代码链接到可…...

深度学习算法:从基础到实践
简介 深度学习作为人工智能领域的一个重要分支,近年来在多个领域取得了显著的成就。本文将从基础概念出发,探讨深度学习算法的核心原理,并介绍一些实际应用案例。 深度学习算法的核心概念 深度学习算法基于人工神经网络,通过构…...

27. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表服务
报表是每个记账应用所具备的功能,要实现报表功能就需要把账本的核心功能(记账)完成,因此报表服务作为本专栏第一部分单体应用开发中最后一个要实现的功能,这一篇文章很简单,我们一起来实现一个简单的报表服…...

coffee销售数据集分析:基于时间趋势分析的实操练习
**文章说明:**对coffee销售数据集的简单分析练习(时间趋势分析练习),主要是为了强化利用python进行数据分析的实操能力。属于个人的练习文章。 **注:**这是我第一次使用md格式编辑博客文章,排版上还是不是很…...

【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能
【1】原贴地址:eclipse - 怎么还原原来版本的搜索功能_eclipse打开类型搜索类功能失效-CSDN博客 https://blog.csdn.net/sinat_32238399/article/details/145113105 【2】原文如下: 更新eclipse-24-09版本后之后,新的搜索功能(CT…...

Centos 修改历史读录( HISTSIZE)
history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件,默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…...

lwIP——4 网络接口
1.lwIP网络接口 网络接口(网卡):个人理解是处理网络层和数据传输关系的接口(tcp/ip协议栈中的网络接口层部分),直接与硬件平台打交道 lwIP协议栈支持多种不同的网络接口(网卡)&#…...

pytest自动化测试 - pytest夹具的基本概念
<< 返回目录 1 pytest自动化测试 - pytest夹具的基本概念 夹具可以为测试用例提供资源(测试数据)、执行预置条件、执行后置条件,夹具可以是函数、类或模块,使用pytest.fixture装饰器进行标记。 1.1 夹具的作用范围 夹具的作用范围: …...

FreeRtos的使用教程
定义: RTOS实时操作系统, (Real Time Operating System), 指的是当外界事件发生时, 能够有够快的响应速度,调度一切可利用的资源, 控制实时任务协调一致的运行。 特点: 支持多任务管理, 处理多个事件, 实现更复杂的逻辑。 与计算…...

yolov11 解读简记
1 文章详细介绍了YOLOv11的架构设计,包括以下几个关键组件: C3k2块:这是YOLOv11引入的一种新型卷积块,替代了之前版本中的C2f块。C3k2块通过使用两个较小的卷积核代替一个大的卷积核,提高了计算效率,同时保…...

实验二 数据库的附加/分离、导入/导出与备份/还原
实验二 数据库的附加/分离、导入/导出与备份/还原 一、实验目的 1、理解备份的基本概念,掌握各种备份数据库的方法。 2、掌握如何从备份中还原数据库。 3、掌握数据库中各种数据的导入/导出。 4、掌握数据库的附加与分离,理解数据库的附加与分离的作用。…...

Kafka常见问题之 `javax.management.InstanceAlreadyExistsException`
文章目录 Kafka常见问题之 javax.management.InstanceAlreadyExistsException1. 概述2. 常见原因3. 具体异常示例4. 解决方案4.1 确保单一 Kafka Producer 实例4.2 配置 Kafka Broker 和 Producer 使用唯一的 JMX 名称(对于Producer重点检查 client.id)4…...

性能测试丨JVM 性能数据采集
什么是JVM性能数据采集? JVM性能数据采集是指通过一些工具和技术采集与Java虚拟机相关的性能数据。这些数据包括但不限于内存使用、CPU使用、垃圾回收(GC)行为、线程活动等。合理地分析这些数据,可以帮助我们找出系统的瓶颈&…...

计算机图形学实验练习(实验1.2-4.1AND补充实验12)
实验1.2 OpenGL与着色器编程 1.理论知识 1.1 OpenGL的含义 OpenGL是一种应用程序编程接口(Application Programming Interface,API),它是一种可以对图形硬件设备特性进行访问的软件库。OpenGL最新的4.3版本包含了超过500个不同的命令,可以用于设置所需的对象、图像和操…...

JWT实现单点登录
文章目录 JWT实现单点登录JWT 简介存在问题及解决方案登录流程后端程序实现前端保存Tokenstore存放信息的缺点及解决 校验流程:为gateway增加登录校验拦截器 另一种单点登录方法:Token+Redis实现单点登录 JWT实现单点登录 登录流程ÿ…...

云计算的概念与特点:开启数字化时代的新篇章
在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…...

salesforce中如何获取一个profile的18位id
在 Salesforce 中,要获取一个 Profile 的 18 位 ID,可以通过以下几种方式实现: 方法 1:通过 Developer Console 登录 Salesforce。 点击右上角的 头像 或 设置齿轮,选择 “开发者控制台”(Developer Conso…...

Vue 3 中的标签 ref 与 defineExpose:模板引用与组件暴露
在 Vue 3 中,ref 不仅可以用于创建响应式数据,还可以用于获取 DOM 节点或组件实例。通过 ref,我们可以直接访问模板中的元素或组件,并在需要时操作它们。此外,defineExpose 用于在 <script setup> 语法中显式暴露…...

FLTK - FLTK1.4.1 - demo - adjuster.exe
文章目录 FLTK - FLTK1.4.1 - demo - adjuster.exe概述笔记根据代码,用fluid重建一个adjuster.fl 备注 - fluid生成的代码作为参考代码好了修改后可用的代码END FLTK - FLTK1.4.1 - demo - adjuster.exe 概述 想过一遍 FLTK1.4.1的demo和测试工程,工程…...

单路由及双路由端口映射指南
远程登录总会遇到登陆不上的情况,可能是访问的大门没有打开哦,下面我们来看看具体是怎么回事? 当软件远程访问时,主机需要两个条件,一是有一个唯一的公网IP地址(运营商提供),二是开…...

专为课堂打造:宏碁推出三款全新耐用型 Chromebook
IT之家 1 月 25 日消息,宏碁(Acer)昨日(1 月 24 日)发布公告,针对教育市场,推出 Chromebook Spin 512 (R857T)、Chromebook Spin 511 (R757T) 和 Chromebook 511 (C737) 三款产品,兼…...

云计算架构学习之LNMP架构部署、架构拆分、负载均衡-会话保持
一.LNMP架构部署 1.1. LNMP服务搭建 1.磁盘信息 2.内存 3.负载信息 4.Nginx你们公司都用来干嘛 5.文件句柄(文件描述符 打开文件最大数量) 6.你处理过系统中的漏洞吗 SSH漏洞 7.你写过什么shell脚本 8.监控通过什么告警 zabbix 具体监控哪些内容 9.mysql redis查询 你好H…...

Python案例--暂停与时间格式化
在编程中,时间的处理是一个常见的需求。无论是日志记录、任务调度还是数据时间戳的生成,正确地获取和格式化时间都至关重要。Python 提供了强大的时间处理模块,其中 time 模块是基础且广泛使用的工具之一。本文将通过一个简单的示例ÿ…...

【javaweb项目idea版】蛋糕商城(可复用成其他商城项目)
该项目虽然是蛋糕商城项目,但是可以复用成其他商城项目或者购物车项目 想要源码的uu可点赞后私聊 技术栈 主要为:javawebservletmvcc3p0idea运行 功能模块 主要分为用户模块和后台管理员模块 具有商城购物的完整功能 基础模块 登录注册个人信息编辑…...

git gui 笔记
这里写目录标题 1. [下载安装git](https://blog.csdn.net/jiesunliu3215/article/details/111559125)2. [下载Git Gui](https://git-scm.com/downloads)3. 上传下载代码4. 创建版本5. 版本切换-checkout参考狂神说 git教程 -讲的是真的好gitee的git帮助 其他 1. 下载安装git 2…...

使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化
使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化 前言环境准备运行 Oracle Database 23ai Free 容器基本命令参数说明示例 注意事项高级配置参数说明 总结 前言 Oracle Database 23ai Free 是 Oracle 提供的免费版数据库,基于 Oracle …...

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)
医疗 MLLM 框架编程实现 本医疗 MLLM 框架结合 Python 与 PyQt6 构建,旨在实现多模态医疗数据融合分析并提供可视化界面。下面从数据预处理、模型构建与训练、可视化界面开发、模型 - 界面通信与部署这几个关键部分详细介绍编程实现。 6.1 数据预处理 在医疗 MLLM 框架中,多…...

salesforce公式字段 ISBLANK 函数和 <> NULL的区别
在 Salesforce 公式字段中,ISBLANK 函数和 <> NULL 的作用都可以用来检查字段是否有值,但它们的行为有一些显著的区别。以下是它们的详细对比和适用场景: 1. 基本区别 功能ISBLANK<> NULL主要作用检查字段是否为空(适…...

微服务学习-服务调用组件 OpenFeign 实战
1. OpenFeign 接口方法编写规范 1.1. 在编写 OpenFeign 接口方法时,需要遵循以下规范 1.1.1.1. 接口中的方法必须使用 RequestMapping、GetMapping、PostMapping 等注解声明 HTTP 请求的类型。 1.1.1.2. 方法的参数可以使用 RequestParam、RequestHeader、PathVa…...

关于安卓greendao打包时报错问题修复
背景 项目在使用greendao的时候,debug安装没有问题,一到打包签名就报了。 环境 win10 jdk17 gradle8 项目依赖情况 博主的greendao是一个独立的module项目,项目目前只适配了java,不支持Kotlin。然后被外部集成。greendao版本…...