【ONE·Linux || 基础IO(二)】
总言
文件系统与动静态库相关介绍。
文章目录
- 总言
- 2、文件系统
- 2.1、背景知识
- 2.2、磁盘管理
- 2.2.1、磁盘文件系统图
- 2.2.2、inode与文件名
- 2.3、软硬链接
- 3、动静态库
- 3.1、站在编写库的人的角度:如何写一个库?
- 3.1.1、静态库制作
- 3.1.3、动态库制作
- 3.2、站在使用库的人的角度:如何用别人提供的库?
- 3.2.1、使用静态库
- 3.2.2、使用动态库
2、文件系统
2.1、背景知识

说明一:
掉电易失存储介质:内存
永久性存储介质:磁盘、SSD、U盘、flash卡、光盘、磁带
磁盘是外设,且还是计算机中唯一一个机械设备,与OS等相比其操作效率很慢,因此OS会有一定的提速方式。
说明二:
CHS与LBA之间的转换:

说明三:
虽然硬件层面扇区通常为512bit,但操作系统(文件系统)与磁盘进行IO的基本单位是4kb,原因如下:
1、若IO交互的基本单位太小,有可能导致多次IO,降低效率。
2、使得硬件和软件解耦合。若IO交互使用和磁盘一样的单位,当磁盘基本单位变大时,OS也要随之修改。
2.2、磁盘管理
2.2.1、磁盘文件系统图
1)、解释磁盘文件系统图各小块含义

整体说明: 磁盘是典型的块设备,硬盘分区被划分为一个个的block。上图为磁盘文件系统图(Linux ext2文件系统,不同内核内存映像有所不同)。一个block的大小是由格式化的时候确定的,并且不可以更改,例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。上图中启动块(Boot Block)的大小是确定的。
1、数据区(Data block):多个4KB(扇区×8)大小的集合,通常用于存放文件内容。
PS:文件=内容+属性,Linux在磁盘上存储文件时,是将内容和属性分开存储的。
2、i节点表(inode Table):inode是大小为128字节的空间,保存着对应文件的属性,如文件大小、所有者、最近修改时间等。inode table是该组块(block group)内所有文件的inode空间的集合,为了标识每个inode的唯一性,会为其附一个inode编号,也存储在各自的inode空间中。
PS:一般而言,一个文件,对应一个inode,用于记录其文件属性,同时该inode有一个标识编号(inode编号)。
[wj@VM-4-3-centos T0730]$ ls -li //加-i选项,可查看每个文件的inode编号
total 24
789948 -rw-rw-r-- 1 wj wj 51 Jul 30 21:45 log.txt
790002 -rw-rw-r-- 1 wj wj 65 Jul 30 12:24 makefile
790001 -rw-rw-r-- 1 wj wj 2929 Jul 30 21:45 myfile.c
789573 -rwxrwxr-x 1 wj wj 8568 Jul 30 21:45 myfile.o
[wj@VM-4-3-centos T0730]$
3、块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用。将bit位与特定的data block中数据块一一对应,若比特位为1,则该数据块被占用,为0表示未被占用。
4、inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。同理,将比特位与inode一一对应,为1表示占用,为0表示未被占用。
PS:block bitmap和inode bitmap是用于管理信息的,扫描这两区域,即可查看该块组内inode和数据块使用程度。
5、块组描述符(Group Descriptor Table):GDT,用户描述块组属性信息,如该块组多大,已经使用多少空间,有多少个inode/data block,占用和未占用情况。
格式化说明: 如上述,将块组分割成上述内容,并写入相关的管理数据,在磁盘内就能让一个文件的信息可追溯、可管理。将每个块组统一规定为如上格式,那么整个分区就被写入 了文件系统信息。将这种操作称之为格式化。
2)、其它说明
1、一个文件对应一个inode编号、inode属性节点,那么,一个文件只能有一个block吗?
回答:不一定。data block大小有限,当文件内容很大时,一个数据块不足以存储所有文件内容,此时就需要多个数据块支持。
2、哪些block属于同一个文件?如何找到它们?
3、文件=内容+属性,通过inode编号可以找到文件inode属性集合,那么怎么找到文件内容呢?
回答:inode中,除了保存文件的属性(大小、权限、inode编号等),还有一个其它属性,用于保存同块组内对应文件的数据块编号。

4、当这个文件特别大时,inode中的blocks也不够存储对应data blocks的量,又该怎么办?

2.2.2、inode与文件名
1)、问题引入:如何找到一个文件?
回答:找到一个文件,首先要找到其inode编号,根据inode编号获取特定的块组block group ,在块组内的inode table中找到对应的inode,从而能够获取到文件的属性与内容。
那么,如何获取到文件的inode编号呢?
2)、文件inode编号与文件名说明
1、inode属性中并不存储文件名称,文件名称实则是由该文件隶属的目的来保存的。
2、目录也是文件,其内部可以具有多个文件,同一个目录下的文件不能有重名。对于一个目录而言,既然它是文件,就具有自己的inode,也有对应的data blocks。而根据1可知,inode中不保存文件名称,因此,文件的文件名称实则是与其inode编号建立映射关系,保存在目录的data blocks中的。
3、上述内容也解释了进入目录、创建文件、显示文件名与属性中权限需求说明。如创建一个文件,需要把文件的inode编号和文件名称建立映射关系并保存在目录的data blocks中,因此需要目录有w权限。读取文件时也需要先找到对应的inode编号,就需要访问目录的data blocks,因此目录要有r权限。
3)、根据上述内容可回答三个问题
问题1、创建文件,系统做了什么?
[root@localhost linux]# touch abc
[root@localhost linux]# ls -i abc
263466 abc
回答:创建一个新文件主要有以下四个操作:
① 存储属性 :内核先找到一个空闲的i节点(这里是263466),内核把文件的属性信息记录到其中。
② 存储数据 :假设该文件需要存储在三个磁盘块,内核需要找到了三个空闲的数据块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。
③ 记录分配情况:将文件内容按顺序300、500、800存放。内核在inode上的磁盘分布区记录了上述块列表。
④添加文件名到目录:新的文件名abc。内核将入口(263466,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。

问题2、删除文件,系统做了什么?
回答:以文件名索引inode编号,置零inode bitmap,置零data bitmap,断开目录中记录的映射关系。PS:对应文件内容和inode table中的属性,可以不用清除。(类比参考数据结构阶段学习的各类删除接口)
延伸:删除的文件能够恢复吗?
回答:可以,恢复找到inode编号就能找对对应文件。前提是对应位置的inode、data block没有被重复占用。
问题3、查看文件,系统做了什么?
回答:以文件名为索引,找到inode编号,显示对应的文件信息。例如ls 只显示目录下的所有文件名称,加各项选项会显示其它属性信息。
4)、周边问题
1、组块中,分区中各小块是由谁来划分的?
回答:格式化操作的过程,就是在磁盘中写入文件系统的过程。
2、创建文件,写入失败,或者直接创建文件失败,分析可能的原因?
回答:inode table大小固定,data blocks大小固定。以下情况可能导致创建文件失败:
①inode已满,data block未满;
②inode未满,data block已满。
2.3、软硬链接
1)、如何建立软硬链接?
相关指令如下:
ln -s:创建软链接,如ln -s testlink1.txt soft.link
ln:创建硬链接,如ln testlink2.txt hard.link
代码演示如下:
[wj@VM-4-3-centos T0804]$ mkdir dir
[wj@VM-4-3-centos T0804]$ touch testlink1.txt
[wj@VM-4-3-centos T0804]$ touch testlink2.txt
[wj@VM-4-3-centos T0804]$ touch testlink3.txt[wj@VM-4-3-centos T0804]$ ls -li
total 4
790036 drwxrwxr-x 2 wj wj 4096 Aug 4 15:21 dir
790037 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink1.txt
790038 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink2.txt
790039 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink3.txt[wj@VM-4-3-centos T0804]$ ln -s testlink1.txt soft.link
[wj@VM-4-3-centos T0804]$ ln testlink2.txt hard.link[wj@VM-4-3-centos T0804]$ ls -li
total 4
790036 drwxrwxr-x 2 wj wj 4096 Aug 4 15:21 dir
790038 -rw-rw-r-- 2 wj wj 0 Aug 4 15:21 hard.link
790040 lrwxrwxrwx 1 wj wj 13 Aug 4 15:25 soft.link -> testlink1.txt
790037 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink1.txt
790038 -rw-rw-r-- 2 wj wj 0 Aug 4 15:21 testlink2.txt
790039 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink3.txt
[wj@VM-4-3-centos T0804]$
结果观察:

PS:unlink XXXX可删除链接关系。
[wj@VM-4-3-centos uselib]$ ln -s main.c main.so #建立软链接
[wj@VM-4-3-centos uselib]$ ll
total 28
-rw-rw-r-- 1 wj wj 157 Aug 4 21:57 main.c
-rwxrwxr-x 1 wj wj 8440 Aug 5 17:54 main.out
lrwxrwxrwx 1 wj wj 6 Aug 5 18:57 main.so -> main.c
-rw-rw-r-- 1 wj wj 35 Aug 5 16:13 makefile
drwxrwxr-x 5 wj wj 4096 Aug 5 16:07 output
drwxrwxr-x 4 wj wj 4096 Aug 5 10:55 staticlib[wj@VM-4-3-centos uselib]$ unlink main.so #删除
[wj@VM-4-3-centos uselib]$ ll
total 28
-rw-rw-r-- 1 wj wj 157 Aug 4 21:57 main.c
-rwxrwxr-x 1 wj wj 8440 Aug 5 17:54 main.out
-rw-rw-r-- 1 wj wj 35 Aug 5 16:13 makefile
drwxrwxr-x 5 wj wj 4096 Aug 5 16:07 output
drwxrwxr-x 4 wj wj 4096 Aug 5 10:55 staticlib
2)、软硬链接有什么区别?
软硬链接本质区别: 有无独立inode编号。
回答:软链接有独立的inode,说明软链接是一个独立的文件。硬链接没有独立的inode,说明硬链接不是一个独立的文件。
问题:它们之间的这种独立性的差距有什么意义?
对软链接:
特性:软链接的文件内容指向被链接文件的对应路径。因此,删除软链接文件,被链接文件仍旧存在(相当于Windows下删除快捷键,但所指向程序仍旧存在)。
[wj@VM-4-3-centos test01]$ ls -li
total 0
790040 lrwxrwxrwx 1 wj wj 13 Aug 4 15:25 soft.link -> testlink1.txt
790037 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink1.txt
790038 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink2.txt[wj@VM-4-3-centos test01]$ rm soft.link //删除软链接文件,实际被链接文件仍旧存在[wj@VM-4-3-centos test01]$ ls -li
total 0
790037 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink1.txt
790038 -rw-rw-r-- 1 wj wj 0 Aug 4 15:21 testlink2.txt
[wj@VM-4-3-centos test01]$
相关应用:相当于Windows下的快捷方式

对硬链接:
硬链接的inode为被链接文件的inode,不是真正的创建新文件。创建硬链接时,只是在指定目录下,建立文件名与指定indoe的映射关系(相当于起别名)。
属性中存在硬链接数:用于确定多少个文件名与该indoe关联。实际删除文件时,并不是把该文件真正删除,而是减少其硬链接数,直到其为0,该文件才真正删除。

默认创建一个普通文件、目录,其默认硬链接数说明:
问题:如下,为什么默认创建一个普通文件,其硬链接数为1?为什么默认创建一个普通目录,其硬链接数为2?
[wj@VM-4-3-centos test01]$ mkdir dir
[wj@VM-4-3-centos test01]$ touch test.txt
[wj@VM-4-3-centos test01]$ ls -li
total 4
790037 drwxrwxr-x 2 wj wj 4096 Aug 4 19:11 dir
790038 -rw-rw-r-- 1 wj wj 0 Aug 4 19:11 test.txt
[wj@VM-4-3-centos test01]$
对普通文件:创建一个普通文件,其自身文件名和自身inode建立关系,故硬链接数为1。如上述例子中的(790038,test.txt)
对目录:创建一个空目录,除了自身目录名和inode建立关联,进入目录内,可看到隐藏目录.(代表当前路径),也与该inode关联,故而空目录的链接数2。PS:若目录内还有其它子目录,则子目录中隐藏文件..也和该inode关联。

[wj@VM-4-3-centos test01]$ ls
dir test.txt
[wj@VM-4-3-centos test01]$ ls -ali
total 12
790036 drwxrwxr-x 3 wj wj 4096 Aug 4 19:11 .
790035 drwxrwxr-x 4 wj wj 4096 Aug 4 15:47 ..
790037 drwxrwxr-x 2 wj wj 4096 Aug 4 19:11 dir
790038 -rw-rw-r-- 1 wj wj 0 Aug 4 19:11 test.txt
[wj@VM-4-3-centos test01]$ cd dir
[wj@VM-4-3-centos dir]$ ls -ali
total 8
790037 drwxrwxr-x 2 wj wj 4096 Aug 4 19:11 .
790036 drwxrwxr-x 3 wj wj 4096 Aug 4 19:11 ..
[wj@VM-4-3-centos dir]$
3、动静态库
3.1、站在编写库的人的角度:如何写一个库?
3.1.1、静态库制作
1)、准备工作
为了方便演示,此处写头文件与方法实现分离,写一两个演示代码:
[wj@VM-4-3-centos test03]$ cat accum.h
#pragma once
#include<stdio.h>extern void accum(int from, int to);[wj@VM-4-3-centos test03]$ cat accum.c
#include "accum.h"int accumulate(int from, int to)
{int sum = 0;int i = 0;for(i = from; i <= to; ++i){sum+=i;}return sum;
}
[wj@VM-4-3-centos test03]$ cat print.h#pragma once
#include<stdio.h>
#include<time.h>extern void print(const char* str);[wj@VM-4-3-centos test03]$ cat print.c
#include "print.h"void print(const char* str)
{printf("%s[%d]\n",str,(int)time(NULL));
}
假设以上两个为待封装库的相关内容实现,这里我们分别使用两个目录,用以演示库制作与库使用。注意:制作库时,库中不能存在main函数。
[wj@VM-4-3-centos test03]$ ls
mklib uselib
[wj@VM-4-3-centos test03]$ tree
.
|-- mklib
| |-- accum.c
| |-- accum.h
| |-- print.c
| `-- print.h
`-- uselib2 directories, 4 files
[wj@VM-4-3-centos test03]$
使用一个makefile来完成上述内容的编译过程。(PS:该文件后续还会改动)
[wj@VM-4-3-centos mklib]$ cat makefile
.PHONY:all
all:print.o accum.oprint.o:print.cgcc -c print.c -o print.o
accum.o:accum.cgcc -c accum.c -o accum.o.PHONY:clean
clean:rm -rf *.o[wj@VM-4-3-centos mklib]$
2)、制作一个静态库
1、问题:直接将
.o文件和.h文件给别人,是否能形成可执行程序以供使用?
一点准备工作:如下,将形成的accum.o、accum.h、print.o、print.h拷贝一份放入uselib目录中,用以演示:
[wj@VM-4-3-centos mklib]$ ll //当前文件
total 24
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 153 Aug 4 21:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h[wj@VM-4-3-centos mklib]$ make
gcc -c print.c -o print.o
gcc -c accum.c -o accum.o[wj@VM-4-3-centos mklib]$ ll //生成.o文件
total 28
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 1280 Aug 4 21:44 accum.o
-rw-rw-r-- 1 wj wj 153 Aug 4 21:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
-rw-rw-r-- 1 wj wj 1576 Aug 4 21:42 print.o
在ueslib目录中创建一个main.c文件,在该文件中使用这两个拟库。
[wj@VM-4-3-centos mklib]$ mv *.h *.o ../uselib //相关指令
[wj@VM-4-3-centos mklib]$ cd ../uselib/ //转到uselib文件中
[wj@VM-4-3-centos uselib]$ ls
accum.h accum.o print.h print.o main.c
[wj@VM-4-3-centos uselib]$ cat main.c //写一个main.c,在其中用到这两个文件
#include "print.h"
#include "accum.h"int main()
{print("hello main:");int ret = accumulate(1,50);printf("result:%d\n",ret);return 0;
}
如下:我们能成功使用:
[wj@VM-4-3-centos uselib]$ gcc -o main.out main.c print.o accum.o
[wj@VM-4-3-centos uselib]$ ls
accum.h accum.o main.c main.out print.h print.o
[wj@VM-4-3-centos uselib]$ ./main.out
hello main:[1691157514]
result:1275
[wj@VM-4-3-centos uselib]$

如上方式虽然能够演示成功, 一旦用于制作的库文件过多,这样交给方式有些繁琐,属于一种野生级别的方式。因此我们还需要将库进行打包归档。
2、静态库制作:使用ar指令整理打包库(archive files:归档文件)
相关指令:
①gcc -c XXX.c -o XXX.o ,获取.o二进制目标文件;
②ar -rc libXXX.a XXX.o XXX.o将库归档处理。
此处要注意 libXXX.a中,lib、.a为语法格式,中间名称可任意取。后面要打包几个.o文件就跟几个。
演示如下:
[wj@VM-4-3-centos mklib]$ ll //mklib中的文件
total 28
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 1280 Aug 4 21:46 accum.o
-rw-rw-r-- 1 wj wj 153 Aug 4 21:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
-rw-rw-r-- 1 wj wj 1576 Aug 4 21:46 print.o[wj@VM-4-3-centos mklib]$ ar -rc libstatic.a accum.o print.o //对.o文件进行归档打包[wj@VM-4-3-centos mklib]$ ll
total 32
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 1280 Aug 4 21:46 accum.o
-rw-rw-r-- 1 wj wj 3074 Aug 5 10:13 libstatic.a //新生成的静态库
-rw-rw-r-- 1 wj wj 153 Aug 4 21:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
-rw-rw-r-- 1 wj wj 1576 Aug 4 21:46 print.o
[wj@VM-4-3-centos mklib]$
使用makefile生成:
libstatic.a:print.o accum.o //形成静态库:step2ar -rc libstatic.a accum.o print.oprint.o:print.c //形成静态库:step1gcc -c print.c -o print.o
accum.o:accum.c //形成静态库:step1gcc -c accum.c -o accum.o.PHONY:clean
clean:rm -rf *.o libstatic.a
[wj@VM-4-3-centos mklib]$ make clean
rm -rf *.o libstatic.a
[wj@VM-4-3-centos mklib]$ ls
accum.c accum.h makefile print.c print.h
[wj@VM-4-3-centos mklib]$ make
gcc -c print.c -o print.o
gcc -c accum.c -o accum.o
ar -rc libstatic.a accum.o print.o
[wj@VM-4-3-centos mklib]$ ll
total 32
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 1280 Aug 5 10:29 accum.o
-rw-rw-r-- 1 wj wj 3074 Aug 5 10:29 libstatic.a //所形成的静态库
-rw-rw-r-- 1 wj wj 230 Aug 5 10:20 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
-rw-rw-r-- 1 wj wj 1576 Aug 5 10:29 print.o
[wj@VM-4-3-centos mklib]$
3)、发布库
说明:该静态库目录分为两部分,其一为头文件目录include,其二为打包的库目录lib,这里我们仍旧使用makefile生成。
在上述的makefile中再加入这段指令:
.PHONY:staticlib
staticlib:mkdir -p staticlib/includemkdir -p staticlib/lib cp -rf *.h staticlib/includecp -rf *.a staticlib/lib
由此我们就得到一个staticlib静态库目录,其中.h头文件和.a静态库分开存放:
[wj@VM-4-3-centos mklib]$ make staticlib
mkdir -p staticlib/include
mkdir -p staticlib/lib
cp -rf *.h staticlib/include
cp -rf *.a staticlib/lib [wj@VM-4-3-centos mklib]$ ll
total 36
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 1280 Aug 5 10:32 accum.o
-rw-rw-r-- 1 wj wj 3074 Aug 5 10:32 libstatic.a
-rw-rw-r-- 1 wj wj 373 Aug 5 10:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
-rw-rw-r-- 1 wj wj 1576 Aug 5 10:32 print.o
drwxrwxr-x 4 wj wj 4096 Aug 5 10:43 staticlib //静态库目录文件[wj@VM-4-3-centos mklib]$ tree staticlib/
staticlib/
|-- include
| |-- accum.h
| `-- print.h
`-- lib`-- libstatic.a2 directories, 3 files
[wj@VM-4-3-centos mklib]$
发布后,如何使用静态库见3.2.1部分。
3.1.3、动态库制作
1)、准备工作
此处使用静态库中的演示例子:accum.c、accum.h、print.c、print.h,具体内部代码见上述。
[wj@VM-4-3-centos mklib]$ ll
total 24
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 373 Aug 5 10:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
drwxrwxr-x 4 wj wj 4096 Aug 5 10:43 staticlib
2)、制作一个库
相关指令:
①gcc -fPIC -c XXX.c -o XXX.o :fPIC形成一个与位置无关的二进制目标文件。
②gcc -shared XXX.o XXX.o -o libXXX.so :-shared用于表示所形成的是动态库而非可执行程序。
演示如下:
[wj@VM-4-3-centos mklib]$ ll
total 24
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rw-rw-r-- 1 wj wj 373 Aug 5 10:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
drwxrwxr-x 4 wj wj 4096 Aug 5 10:43 staticlib[wj@VM-4-3-centos mklib]$ gcc -fPIC -c print.c -o print_d.o #step1:生成一个与位置无关的二进制目标文件
[wj@VM-4-3-centos mklib]$ gcc -fPIC -c accum.c -o accum_d.o
[wj@VM-4-3-centos mklib]$ ls
accum.c accum_d.o accum.h makefile print.c print_d.o print.h staticlib[wj@VM-4-3-centos mklib]$ gcc -shared accum_d.o print_d.o -o libdynamic.so #step2:生成一个动态库
[wj@VM-4-3-centos mklib]$ ll
total 40
-rw-rw-r-- 1 wj wj 165 Aug 4 21:44 accum.c
-rw-rw-r-- 1 wj wj 1280 Aug 5 14:36 accum_d.o
-rw-rw-r-- 1 wj wj 75 Aug 4 21:44 accum.h
-rwxrwxr-x 1 wj wj 8152 Aug 5 14:36 libdynamic.so #动态库
-rw-rw-r-- 1 wj wj 373 Aug 5 10:42 makefile
-rw-rw-r-- 1 wj wj 96 Aug 4 21:07 print.c
-rw-rw-r-- 1 wj wj 1624 Aug 5 14:35 print_d.o
-rw-rw-r-- 1 wj wj 88 Aug 4 21:07 print.h
drwxrwxr-x 4 wj wj 4096 Aug 5 10:43 staticlib
[wj@VM-4-3-centos mklib]$
使用makefile生成:
dynamiclib.so:print_d.o accum_d.ogcc -shared print_d.o accum_d.o -o libdynamic.soprint_d.o:print.cgcc -fPIC -c print.c -o print_d.o
accum_d.o:accum.cgcc -fPIC -c accum.c -o accum_d.o
3)、发布库
这里我们直接生成动静态库两个版本:makefile中相关写法如下
.PHONY:all
all::libdynamic.so libstatic.a##动态库##
libdynamic.so:print_d.o accum_d.ogcc -shared print_d.o accum_d.o -o libdynamic.soprint_d.o:print.cgcc -fPIC -c print.c -o print_d.o
accum_d.o:accum.cgcc -fPIC -c accum.c -o accum_d.o
######静态库##
libstatic.a:print.o accum.oar -rc libstatic.a accum.o print.oprint.o:print.cgcc -c print.c -o print.o
accum.o:accum.cgcc -c accum.c -o accum.o
####.PHONY:output
output:mkdir -p output/lib mkdir -p output/includecp -rf *.h output/includecp -rf *.a *.so output/lib .PHONY:clean
clean:rm -rf *.o *.a *.so output
演示结果:

3.2、站在使用库的人的角度:如何用别人提供的库?
3.2.1、使用静态库
我们获取到别人归档后的库,并不能直接使用,还需要做一定操作,以下为几种使用静态库的方法介绍。
1)、做法一:将我们所下载的库拷贝到系统库(系统默认路径)中
前提认知:
头文件gcc默认搜索路径:/usr/include
库文件云服务器默认搜索路径:/lib64 或者 /usr/lib64

相关指令:
拷贝lib:sudo cp XXX/XXX/*.a /lib64 -rf
sudo cp ./staticlib/lib/*.a /lib64 -rf
拷贝头文件:sudo cp XXX/XXX/*.h /usr/include -rf
sudo cp ./staticlib/include/*.h /usr/include -rf
第三方库编译方法:gcc XXX.c -lXXX,要加-l,后面跟的是库名称。libXXX.a中,去掉前缀和后缀,XXX为库的名称。
gcc main.c -lstatic -o main.out
以下为演示过程及结果:将库拷贝到系统默认路径下,就叫做库的安装。

问题说明:
上述使用静态库的方法容易污染系统库,一般不建议这样使用。
[wj@VM-4-3-centos uselib]$ sudo rm /usr/include/accum.h
[sudo] password for wj:
[wj@VM-4-3-centos uselib]$ sudo rm /usr/include/print.h
[wj@VM-4-3-centos uselib]$ sudo rm /lib64/libstatic.a
2)、做法二:硬使用
相关指令: gcc XXX.c -I ./XXX/include/ -L ./XXX/lib/ -lXXX
gcc main.c -I ./staticlib/include/ -L ./staticlib/lib/ -lstatic -o main.out
-I:指定库的头文件搜索路径
-L:指定搜索lib路径
-lXXX:指定库名称
相关演示如下:

说明:
上述方式中,a、若只有静态库,则只能静态链接。 b、动静态库同时存在时:默认使用动态库。

c、动静态库同时存在,但非要使用静态库,如何操作?
-static :强制静态链接。
该选项的意义:摒弃默认优先使用动态库的原则,直接使用静态库。

3.2.2、使用动态库
1)、整体说明
1、同上述静态库中方法一,可以在系统默认路径对动态库进行安装、卸载操作,但同样不建议这样使用。
2、针对方法二中动态库无法运行的问题说明:
[wj@VM-4-3-centos uselib]$ gcc main.c -I ./output/include -L ./output/lib2 -loutput #使用相关指令
[wj@VM-4-3-centos uselib]$ ls
a.out main.c makefile output staticlib[wj@VM-4-3-centos uselib]$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=41a35062c3b3113f93efd9ef58eeaf1f795adfb5, not stripped[wj@VM-4-3-centos uselib]$ ./a.out #运行,发现运行失败
./a.out: error while loading shared libraries: liboutput.so: cannot open shared object file: No such file or directory
[wj@VM-4-3-centos uselib]$
静态库将相应代码嵌入程序中,程序执行时就在自己所处的代码区域;动态库相应代码加载在共享区,程序执行时在共享区和代码区反复跳转。动态库只用加载一次,就能被多个进程同时使用,即共享库。

我们使用gcc main.c -I ./output/include -L ./output/lib2 -loutput指令时,只是针对gcc说明需要动态库,但动态库运行加载是操作系统加载器来完成的,故需要给OS说明。
1)、解决方案
解决方案一:系统的环境变量
LD_LIBRARY_PATH设置此环境变量:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/XXXXX/XXXXX
[wj@VM-4-3-centos uselib]$ echo $LD_LIBRARY_PATH
:/home/wj/.VimForCpp/vim/bundle/YCM.so/el7.x86_64

1、上述方式关闭shell后,设置好的内存级的环境变量被清空,下次进入将重新设置。
2、这里export导环境变量也可以使用相对路径:
[wj@VM-4-3-centos uselib]$ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:./output/lib/
[wj@VM-4-3-centos uselib]$ echo $LD_LIBRARY_PATH
LD_LIBRARY_PATH:./output/lib/
[wj@VM-4-3-centos uselib]$ ldd main.outlinux-vdso.so.1 => (0x00007fff066be000)/$LIB/libonion.so => /lib64/libonion.so (0x00007f9da91f4000)libdynamic.so => ./output/lib/libdynamic.so (0x00007f9da8ed9000)libc.so.6 => /lib64/libc.so.6 (0x00007f9da8b0b000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f9da8907000)/lib64/ld-linux-x86-64.so.2 (0x00007f9da90db000)
[wj@VM-4-3-centos uselib]$ ./main.out
hello main:[1691230176]
result:1275
[wj@VM-4-3-centos uselib]$
解决方案二:修改配置文件
ls /etc/ld.so.conf.d/,相对方案一,该方案属于长久保存的一个方法。
操作:在该目录下创建一个XXX.conf文件,在该文件内填入对应的动态库搜索路径
①sudo touch /etc/ld.so.conf.d/XXX.conf:创建文件
②sudo vim /etc/ld.so.conf.d/XXX.conf :填入路径
③sudo ldconfig:让配置文件更新生效
具体演示:


解决方案三:建立软链接
sudo ln -s XXX/XXX /lib64/libXXX.so,注意使用绝对路径。
相关演示:

除了上述方法,还有其它方式可以支持。
相关文章:
【ONE·Linux || 基础IO(二)】
总言 文件系统与动静态库相关介绍。 文章目录 总言2、文件系统2.1、背景知识2.2、磁盘管理2.2.1、磁盘文件系统图2.2.2、inode与文件名 2.3、软硬链接 3、动静态库3.1、站在编写库的人的角度:如何写一个库?3.1.1、静态库制作3.1.3、动态库制作 3.2、站在…...
【LeetCode 算法】Power of Heroes 英雄的力量
文章目录 Power of Heroes 英雄的力量问题描述:分析代码Math Tag Power of Heroes 英雄的力量 问题描述: 给你一个下标从 0 开始的整数数组 nums ,它表示英雄的能力值。如果我们选出一部分英雄,这组英雄的 力量 定义为ÿ…...
合宙Air724UG LuatOS-Air script lib API--ntp
ntp Table of Contents ntp ntp.timeSync(period, fnc, fun) ntp 模块功能:网络授时. 重要提醒!!!!!! 本功能模块采用多个免费公共的NTP服务器来同步时间 并不能保证任何时间任何地点都能百分…...
LangChain+ChatGLM大模型应用落地实践(一)
LLMs的落地框架(LangChain),给LLMs套上一层盔甲,快速构建自己的新一代人工智能产品。 一、简介二、LangChain源码三、租用云服务器实例四、部署实例 一、简介 LangChain是一个近期非常活跃的开源代码库,目前也还在快速…...
PSO粒子群优化算法
PSO粒子群优化算法 算法思想matlab代码python代码 算法思想 粒子群算法(Particle Swarm Optimization) 优点: 1)原理比较简单,实现容易,参数少。 缺点: 1)易早熟收敛至局部最优、迭代后期收敛速度慢的…...
记一次 .NET某医疗器械清洗系统 卡死分析
一:背景 1. 讲故事 前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。 二:WinDbg 分析 1. 为什么会卡死 因为是窗体程序,理所当然就是看主…...
C# 基于Rijndael对文件进行加解密
介绍: Rijndael 是一种对称加密算法,也是 AES(Advanced Encryption Standard)的前身。它用于数据的加密和解密,并提供了安全且高效的加密功能。 在.NET Framework 中,Rijndael 类是一个实现了 Rijndael 算法…...
Elasticsearchr入门
首先在官网下载elasticsearch8.9版本,以及8.9版本的kibana。 解压,点击es8.9bin目录下的elasticsearch.bat文件启动es 如图所示即为成功。 启动之后打开idea,添加依赖 <dependency><groupId>com.fasterxml.jackson.core</g…...
【ARM】imx6ul移植kernel记录,恩智浦github提供的最新kernel(2023年7月31)
❤️作者主页:凉开水白菜 ❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步! ❤️专栏目录: ❤️专栏资料: ❤️点赞 👍 收藏 ⭐再看,养成习惯 订阅的粉丝可通过PC端文末加我微信,可对文章的内容进行一对一答疑! 文章目录 一、简介二、源码下载三、官方…...
eeglab(自用)
目录 1.加载、显示数据 2.绘制脑电头皮图 3.绘制通道光谱图 4.预处理工具 5.ICA去除伪迹 5. 提取数据epoch 1.加载、显示数据 观察事件值(Event values):该数据集中包含2400个事件,每个事件指定了EEG.event结构的字段Type(类型)、position(位置)和…...
Dockerfile构建Tomcat镜像(源码)
Dockerfile构建Tomcat镜像 目录 Dockerfile构建Tomcat镜像 1、建立工作目录 2、编写Dockerfile文件 3、构建镜像 4、测试容器 5、浏览器访问测试: 1、建立工作目录 [roothuyang1 ~]# mkdir tomcat[roothuyang1 ~]# cd tomcat/[roothuyang1 tomcat]# lsapach…...
Frida Error: getPackageInfoNoCheck(): has more than one overload的解决方法
使用frida绕过证书的时候执行代码: frida -U -f de.robv.android.xposed.installer --codeshare akabe1/frida-multiple-unpinning --no-pause遇到这样的错误 Error: getPackageInfoNoCheck(): has more than one overload, use .overload() to choose from: 网上查…...
flutter开发实战-RawKeyboardListener监听键盘事件及keycode。
flutter开发实战-RawKeyboardListener监听键盘事件及keycode。 最近开发过程中遇到外设备的按钮点击触发相应的操作,需要监听对应的keycode来开启游戏或者相关操作。 这里用到了RawKeyboardListener 一、RawKeyboardListener是什么? RawKeyboardListe…...
Temu、希音们全托管引争议,跨境电商应变“工贸一体化”
自7月27日Shopee宣布正式上线全托管模式起,全托管似乎突然又进入了爆发期。 在7月31日至8月1日举行的2023第八届深圳国际跨境电商贸易博览会上,全托管成为SHEIN、Wish、Lazada等平台力推的运营模式。进入8月,跨境圈突然涌现大批传言称&#…...
某科技公司提前批测试岗
文章目录 题目 今天给大家带来一家提前批测试岗的真题,目前已经发offer 题目 1.自我介绍 2.登录页面测试用例设计 3.如何模拟多用户登录 可以使用Jmeter,loadRunner性能测试工具来模拟大量用户登录操作去观察一些参数变化 4.有使用过Jmeter,loadRunner做过性能压…...
一次redis缓存不均衡优化经验
背景 高并发接口,引入redis作为缓存之后,运行一段时间发现redis各个节点在高峰时段的访问量严重不均衡,有的节点访问量7000次/s,有的节点访问量500次/s 此种现象虽然暂时不影响系统使用,但是始终是个安全隐患&#x…...
npm发布包
1.npm 登录 在控制台输入命令 npm login 按提示输入用户名,密码,邮箱后登录 如果出现如下提示 需要将淘宝镜像源切换为npm源,删除或注释以下内容就行 2.发布 进入准备发布的代码的根目录下,输入命令 npm publish 3.删除已发…...
Qt5.13引入QtWebApp的模块后报错: error C2440: “reinterpret_cast”: 无法从“int”转换为“quintptr”
1、开发环境 Win10-64 qt5.13 msvc2015-64bit-release 2、报错 新建一个demo工程。 引入QtWebApp的httpserver、logging、templateengine三个模块后。 直接运行,,此时报错如下: E:\Qt5.13.1\install\5.13.1\msvc2015_64\include\QtCore…...
软件为什么要进行性能压力测试?
软件为什么要进行性能压力测试?随着软件应用的不断增多和复杂度的提高,软件的性能对用户体验和业务成功至关重要。性能问题可能导致软件运行缓慢、崩溃或无响应,给用户带来不便甚至损失。为了确保软件能够在高负载和压力下正常运行࿰…...
阻塞队列BlockingQueue详解
一、阻塞队列介绍 1、队列 队列入队从队首开始添加,直至队尾;出队从队首出队,直至队尾,所以入队和出队的顺序是一样的 Queue接口 add(E) :在指定队列容量条件下添加元素,若成功返回true,若当前…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
