Linux学习之文件系统与动静态库

目录

一,文件的管理

什么是磁盘?

磁盘的逻辑抽象结构

格式化

inode

挂载

软硬链接

二,动静态库

什么是动静态库?

1.站在库的制作者角度

静态库:

制作一个静态库

2.站在静态库使用者的角度

动态库

作为制作者

作为使用者

 动态库加载


一,文件的管理

和进程,内存一样,操作系统创建它就要管理他,文件也是如此,也需要操作系统来管理,那么如何管理呢?还是我们的六字真言,先描述在组织,这与我们学习进程的思想是近似的。

而我们

管理文件的核心就是找到文件,即文件的路径,其次管理文件根据文件的属性分为两种:

1.打开的文件在内存中的管理。

2.未打开的文件在磁盘中的管理。

 所以我们首先第一件事就是先弄清楚如何找到文件的路经,并且是快速的,准确的。而了解这个,就先了解磁盘是如何存储并且定位寻找的。

什么是磁盘?

磁盘是一种用电磁原理记录数据的存储设备,由涂上磁性物质的盘片和盘片读写装置(驱动器)组成。磁盘可以分为软盘、硬盘等类型。如果您需要管理硬盘分区及文件、搜索丢失的分区及文件、对磁盘进行快速分区格式化等操作。磁盘价格便宜,容量大,是企业大量使用的存储介质。

磁盘的结构:

磁盘与盘面的比例是一比一的,通过磁头的摆动,来定位磁盘的某个位置,磁盘的摆动只能左右摆动,盘面是在高速的旋转着,相对于其他外设较慢。其中磁头是具有磁性的,受到碰撞就会损坏磁盘。

一个磁盘上面有许多磁道,他们是一圈圈的同心圆,而扇区就是把一个磁盘分成若干份,因此磁盘再进行存储时,扇区是磁盘被读写的最小单位,一般是512字节,当我们需改某一扇区的即使某一比特位是,也需要加载这个扇区到内存中,所谓一我们把磁盘这样的设备叫块设备。

那么我们如何寻址,定位,选择哪一个扇面,本质就是上选择磁头,选择好了哪一面,下来就是选择哪一个磁道,最后在磁道当中最后在来选择哪一个扇区。同一个磁道构建出的柱面,这种的寻址方法在这里(head,cylinder,sector)就是CHS寻址法。

而文件包括了属性加内容,因此在存储时,属性和属性放在一些,内容与内容放在一起,有的磁道访属性,有的磁道放内容。经过磁头找磁道,盘面旋转确定扇区,我们完成寻址。

磁盘的逻辑抽象结构

操作系统无法用CHS这种方法,因为软硬件是不能固定的,软件的寻址方式变化,硬件不可能要变,因此软件方面是不会使用这种方法的。

对于磁盘,盘面是由一个个同心圆构成,我们可以将该盘面抽象成我们小时候用的磁带一样,比如步步高学习机,在机子里,磁带被旋转卷成一个圆,我们就可以类比这个圆是盘面。

旋转的时候是一个圆,那么拉直的时候就是一个长条子,此时的扇面就是相当于磁带一段一段的,如下图:

一个磁道是一个数组,因此用二维数组我们就可以表示出一个盘面,对于任何某一扇区的位置,我们只需知道它的下标位置即可,通过已知的下标位置,由于一个盘面的大小我们是知道的,我们也可以计算出他是哪一个盘面中的第几个。比如小标123456,同盘面大小取模再取余就能知晓扇区的位置。

于是对磁盘的管理,就变成了对数组的管理。由数组下标就转化成磁盘的CHS了。当然操作系统以扇区为单位进行存取,也可以基于文件系统,按照文件块为单位进行数据存取。例如以8个扇区为一个文件快,直接一起写,在写时也是连续的。因此我们能通过起始地址找到八个块的所在地。

最终结论:对存储设备的管理,在os层,转换成了对数组的增删查改。

格式化

在内存管理方面,我们只需要管理其中一部分空间,剩余的由于与值顺序链接的,我们就可以管理这以快空间。

以分治的思想,将一个大的分区划分成一个个小的块,进行管理,最后将管理中心放在一块小空间即可。

而我们需要知道的文件的路径取决于就是我们对这些组做管理,文件的存储,分为两个部分,一个是文件信息的存储,一个是管理文件信息的存储。因为文件的内容与属性是分开存储的,因此文件信息管理的存储决定了文件信息的存储。

对于分区之后,在先将管理的数据写入块中,即格式化。因此在使用一个盘时,我们都是要先分区,在格式化。例如我们电脑的c盘,d盘就是分区,并且我们可以选择格式化,格式化的本质是清除我们自己的文件数据,对于文件管理数据是不会修改的。

inode

在Linux系统中,我们在使用ls指令时,还有一个选项:

ls -li  //可查看文件的inode

一般情况,一个文件对应一个inode,基本上文件都有inode,inode是一个数字,在整个分区中具有唯一性,因此在Linux内核中,识别文件与文件名无关,而是依据inode。 

为了能解释清楚 inode 我们先简单了解一下文件系统:

也就是上图,可以看到我们写入管理信息到块中有许多,我们先来看看inode table。

inode_table

一般叫做i节点表,inode table主要存放文件属性,所有者,最近修改时间等。i节点表里面也是存放了许多inode,inode大小为128字节,我们将它理解为一个数组,他的下标就是表示inode位置。

每一个分组(table)都有一个起始的start_inode与记录。在分区的唯一性就是自身位置加上start_inode来确定。

data_blocks

数据区,没有任何管理数据,只是一个以4KB的大小的数据块区域,那么一个块里面能存放这么多文件,如何确定文件对应的数据块?   其实在inode中,为我们维护了一个数组 blocks[ ],记录了位置。

inode bitmap

利用位图来记录这么多文件的使用情况,0表示未使用,1表示使用。

block bitmap

除了文件的使用情况,我们还需要记录数据块的使用情况,因此利用位图记录使用情况。

那么了解了这些我们大概能知道创建和删除文件本质上就是去修改inode位图,数据块位图里的某个位置,0变1,1变为0。所以并未真正的删除数据。

Block Group 文件系统会根据分区的大小划分为数个 Block Group 。而每个 Block Group 都有着相 的结构组成。
超级块( Super Block ):存放文件系统本身的结构信息。记录的信息主要有: bolck inode 的总量, 未使用的block inode 的数量,一个 block inode 的大小,最近一次挂载的时间,最近一次写入数据的 时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block 的信息被破坏,可以说整个 文件系统结构就被破坏了。
GDT Group Descriptor Table :块组描述符,描述块组属性信息
创建一个新文件主要有一下 4 个操作:
1. 存储属性
内核先找到一个空闲的 i 节点( 。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块 。将内核缓冲区的第一块数据
复制到第一块中 ,下一块复制到下一个 ,以此类推。
3. 记录分配情况
文件内容按顺序 存放。内核在 inode 上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录

了解了这些,可是我们一般在Linux中也不用inode啊,我们都是用文件名,这是怎么弄得?

因为用户只认识文件名,操作系统只认识Inode,因此我们需要将文件名与inode一一映射,

一一映射急就是键值对的关系,我们将文件名做为key值,将Inode作为valu,以这样方式一一映射。

因此文件名不属于文件属性。那么如何查找一个文件?其实就是先获取到文件的inode,再根据inode确定分区位置,确定inode编号,确定inode_table,但是我们是一层层往下找的,先从根目录开始,找到下一个目录,依次往下,知道我们需要的文件的位置。

这样找太慢了,于是进程会将当前的目录也会保存到cache中。

挂载

一个磁盘想要被使用,就需要将他分区,而想要使用这个分区,还需要将每一个分区都需要挂载到相应的目录上,挂载其实就是将目录的数据结构与文件的数据结构联系起来。

软硬链接

建立软链接:

ln -s file.c file.c.soft.link  //给log文件建立软链接

此时ls -li可以看到inode变化,对应的

建立硬链接:

ln test.c test.c.hard.link //后者硬链接前者

再查看此时的inode没发生变化,但对应的inode编号数字增加了。链接数也发生了变化。

因此我们可以得出,软链接是一个独立的文件,硬链接不是一个独立的文件。因为没有独立的inode编号.

那么什么是软硬链接?

首先我们肯定队快捷方式很熟悉,我们的电脑是双击图标就可以把程序加载到内存中,开始运行这个程序,但事实上我们都是到我们在运行程序时都是运行它的exe文件,一般这个文件在目录下很深的地方,用户每次想加载该文件都需要找到他太国麻烦,因此就可以用一个链接文件当作exe文件,把它拿出来当作我们的快捷方式来加载程序。

而链接分了两种方式:

1.软链接就相当一个wndows的快捷方式,就是一个普通文件,可以快速定位文件。

2.硬链接不是一个独立的文件,从inode往后和目标文件是一个东西,本质上就是在指定目录内部的一组映射关系。

普通文件链接后链接数为1,而空目录连接后,默认链接数位2,这是因为他有两个隐藏文件:

.当前目录  ..上一级目录,他们都记录着对应的inode。

用户无法对目录建立硬链接,对于隐藏文件 .. 和.是系统创建的硬链接,但不允许用户来创建。 

二,动静态库

什么是动静态库?

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

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

注意:

1.一个与动态库链接的文件,仅仅包含它会使用到的函数接口地址的一个表,而不是函数所在文件的整个机器码

2.在程序运行之前,外部函数的机器码由操作系统从磁盘的动态库复制到内存当中,这个过程称为动态链接。

3.动态库可以在多个文件间共享,因此动态链接使得可执行文件更小,可以节省磁盘的空间,操作系统利用虚拟地址机制,使得物理空间对应的一份动态库可以被所有进程共享,节省了磁盘空间与内存。

为了更好的了解动静态库,我们分三个角度看待动静态库:

1.站在库的制作者角度

静态库:

制作一个静态库

首先库中是没有主函数的,比如我们现在搞一个加减乘除的一个库,先创建对应的头文件与.c文件

创建并完成这些文件的代码编写,之后再创建一个maintest.c,用于测试。

完成书写后,将所有文件编译成一个可执行:

gcc -o TEST Add.c Sub.c Mul.c Dic.c Maintest.c

我们运行TEST,可以看到没什么问题,

我们以这种方式编译成可执行文件,但是这种放式并不是最佳方式,我们一般将所有.c文件都先编译成.o文件,再将我们需要的.o文件链接在一起。

gcc -c 源文件 -o目标.o文件
//一般我们直接使用gcc -c源文件 会自动生成同名的.o文件

编写makefile以这样的方式去编译可执行: 

TEST:Add.o Sub.o Mul.o Div.o Maintest.ogcc -o $@ $^
%.o:%.cgcc -c $<
#$<代表一个个的去取文件列表的文件.PHONY:clean
clean:rm -f *.o TEST

于是我们为了其他人想要使用对应的.o文件,我们将所有的.o文件与头文件都打包在一起,用的时候直接链接某一个你需要的.o文件。

故一般在编译文件时,我们只编译成.o文件即可,比如这里我们就将所由.o文件与头文件放在一个test目录下。该目录就当作是用户的目录,此时有一个Maintest.c是你的主程序,我们想将她编译成.o文件,之后想要链接哪一个.o文件,就一起gcc .o文件即可。

这里再将所有的.o文件打个包就是我们所说的库。、

打包.o文件

这里用ar(gnu归档工具进行打包)指令打包:

ar -rc .o文件 -o 目标文件 

 如打包我们这里的.o文件,我们在makefile中打包:

#这里我们用一个变量表示库的名字
static-lib=libmymath.a
$(static-lib):Add.o Sub.o Mul.o Div.oar rc $@ $^%.o:%.cgcc -c $<
.PHONY:clean
clean:rm -f *.o *.a

之后创建的libmymath.a就是我们的静态库。

2.站在静态库使用者的角度

有了静态库,那么我们现在要去使用它,首先将头文件在拿到用户需要编译的目录下,头文件一般都是开放的,之后直接编译,我们发现它会报链接的错误,有了静态库和头文件还不行,我们还需要链接,我们自己写的第三方库gcc是不认识的,我们需要指定连接:

gcc 目标.c文件 -l 静态库  //链接静态库到目标文件中
这里哭的名字一般是要去掉lib与.a才是真实名字如果还是不认识
gcc 目标.c文件 -l 名字 -L .  //当前下的该库

链接完成之后就会形成一个a.out的可执行文件。

此时对于gcc来说c标准库可以直接用,不需要指明链接,也就是第二方库。

gcc默认是动态链接的,有序使用动态库连接,如果子提供了静态库,还是会把库链接起来。

但是这又是是静态库,又是头文件,太挫了,所以最终呈现给用户的形式是将库文件与头文件分开的两个目录,并且在一个目录下,我们在makefile中进行分隔:

static-lib=libmymath.a$(static-lib):Add.o Div.o Mul.o Sub.oar -rc $@ $^
%.o:%.cgcc -c $<.PHONY:output
output:mkdir -p mymath_lib/includemkdir -p mymath_lib/libcp -f *.h mymath_lib/includecp -f *.a mymath_lib/lib.PHONY:clean
clean:rm -rf *.o *.a mymath_lib

之后我们还可以将该目录压缩,其他用户需要用的是后给他压缩包,解压之后就有了该目录,使用的时候,使用指令:

gcc Maintest.c -I mymath-lib/include

然后再链接库即可:

ac ar Maintest.c -l mymath -L mymath-lib/lib

 当然我们可以一起使用:

gcc Maintest.c -I mymath-lib/include -l mymath -L mymath-lib/lib

动态库

作为制作者

动态库与静态库在这里的区别就是打包方式的不同:

首先打包时使用的指令为

gcc -shared -o 目标文件 源文件

之后编译时我们还需要添加选想 -fPIC

gcc -fPIC -c .c文件

动态库生成的Makefile为:

dy-lib=libmymath.so$(dy-lib):Add.o Div.o Mul.o Sub.ogcc -shared -o  $@  $^   #打包围为动态库
%.o:%.cgcc -fPIC -c $<    #增加一个选项生成位置无关码.PHONY:output
output:mkdir -p mymath_lib/includemkdir -p mymath_lib/libcp -f *.h mymath_lib/includecp -f *.so mymath_lib/lib.PHONY:clean
clean:rm -rf *.o *.so  mymath_lib

作为使用者

与静态库不同,动态库生成之后就没有链接错误了,不过有编译错误,会报错头文件找不到,因此只需要与静态库链接一样的指令,在编译时指定链接(link)我们的动态库:

 gcc Maintest.c -I mymath_lib/include/ -l mymath -L mymath_lib/lib/

生成了可执行文件还是要依靠动态库,否则无法运行,而之前,我们指定动态库是编译器知道的,一旦生成完,可执行与动态库还是相互不认识,ldd查看动态库就会发现,找不到。这里有四种方法:

1.下载到我们的系统库里。直接将库拷贝到lib64里,之后gcc -l 指定的库,就可以成功运行了。

2.将我们的库与该路径下的一个文件建立软链接。在将该软链接移动到库上。这样只要有谁想找到这个库,我们就会迅速定位到该库。

当然我们建立软连接时最好给这个库的绝对路径,例如我这个:

3.在Linux中,还存在一个加载项LD_LIBRARY_PATH,加载库路径的环境变量。因此我们可以把动态库的路径找出来,然后导入环境变量。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home//danchengwei/myfile/file2/test/mymath_lib/lib/libmymath.so

4.直接更改系统配置文件

在系统目录下游体格etc/ld.so.conf.d/的配置文件目录,在目录里我们会创建一个文件里面用来存放你要找的动态库的路径。

如果我们的库既有动态也有静态库,默认使用动态库。

当然我们就可以使用别人的库,

ncurses库

下载之后就可以在lib64下找到它的动态库文件。

使用外部库
系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况 的函数(ncurses 库)
gcc -Wall calc.c -o calc -lm
-lm 表示要链接 libm.so 或者 libm.a 库文件

 动态库加载

将方法的地址与可执行程序链接起来,因此动态库连接的程序不仅仅程序要加载,动态库也要加载,态库是一种共享库,它可以在程序运行时加载。在Linux中,您可以使用dlopen函数打开动态链接库,使用dlsym函数获取函数执行地址,使用dlclose函数关闭动态链接库。这些函数都在dlfcn.h头文件中声明.

 在编译的时候,代码就有了虚拟地址,用基地址+偏移量表示,并一一映射,这种模式称为平坦模式。

 

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

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

相关文章

80.网游逆向分析与插件开发-背包的获取-自动化助手显示物品数据1

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;升级Notice类获得背包基址-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;3be017de38c50653b…

操作日志应记录编辑的前后内容变化

总体思路是增加一个注解类&#xff0c;将注解加到要进行记录变化的Java类属性上却可。 上代码&#xff1a; 1. 实现注解类&#xff1a; Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface FieldName {String value();boolean isIgnoreNull()…

day34_js

今日内容 0 复习昨日 1 事件 1.1 事件介绍 1.2 事件绑定方式 1.3 不同事件的演示 2 DOM操作 2.1 概述 2.2 查找元素 2.3 元素内容的查找和设置 2.4 元素属性的查找和设置 2.5 元素CSS样式的查找和设置 2.6 创建元素 2.7 创建文本节点 2.8 追加元素 2.9 删除元素 3 案例练习 0 复…

TCP 异常断开连接【重点】

参考链接 https://xiaolincoding.com/network/3_tcp/tcp_down_and_crash.html https://xiaolincoding.com/network/3_tcp/tcp_unplug_the_network_cable.html#%E6%8B%94%E6%8E%89%E7%BD%91%E7%BA%BF%E5%90%8E-%E6%9C%89%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93 关键词&#xff1a…

无际线复选框

效果演示 实现了一个网格布局&#xff0c;其中每个网格是一个复选框&#xff0c;可以选择是否显示。每个复选框都有一个漂浮的天花板&#xff0c;表示它是一个房间的天花板。每个房间的天花板都有一个不同的形状和颜色&#xff0c;分别对应不同的房间。整个页面的背景是一个由两…

echarts多个折线图共用X轴,实现tooltip合并和分离

echarts共享X轴案例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</…

DevSecOps核心流程基本组成分析

目录 一、DevSecOps核心流程基本组成 1.1 核心流程概述 1.2 DevSecOps 核心流程说明 1.2.1 核心流程图 1.2.2 流程说明 1.2.2.1 持续开发 1.2.2.2 持续构建 1.2.2.3 持续运维 1.2.2.4 持续监控 二、DevSecOps核心流程经典场景 2.1 Azure DevSecOps核心流程 2.1.1 核…

HCIA-HarmonyOS设备开发认证-3.内核基础

目录 前言目标一、进程与线程待续。。。 前言 对于任何一个操作系统而言&#xff0c;内核的运行机制与原理是最为关键的部分。本章内容从多角度了解HarmonyOS的内核运行机制&#xff0c;涵盖进程与线程的概念&#xff0c;内存管理机制&#xff0c;网络特性&#xff0c;文件系统…

HTTP连接池在Java中的应用:轻松应对网络拥堵

网络拥堵是现代生活中无法避免的问题&#xff0c;尤其是在我们这个“点点点”时代&#xff0c;网页加载速度直接影响到我们的心情。此时&#xff0c;我们需要一位“救世主”——HTTP连接池。今天&#xff0c;就让我们一起探讨一下&#xff0c;这位“救世主”如何在Java中大显神…

Linux下安装openresty

Linux下安装openresty 十一、Linux下安装openresty11.1.概述11.2.下载OpenResty并安装相关依赖&#xff1a;11.3.使用wget下载:11.4.解压缩:11.5.进入OpenResty目录:11.6.编译和安装11.7.进入OpenResty的目录&#xff0c;找到nginx&#xff1a;11.8.在conf目录下的nginx.conf添…

C# 获取计算机信息

目录 一、本机信息 1、本机名 2、获得本机MAC地址 3、获得计算机名 4、显示器分辨率 5、主显示器分辨率 6、系统路径 二、操作系统信息 1、操作系统类型 2、获得操作系统位数 3、获得操作系统版本 三、处理器信息 1 、处理器个数 四、CPU信息 1、CPU的个数 2、…

【分布式技术专题】「探索高性能远程通信」基于Netty的分布式通信框架实现(附通信协议和代码)(上)

基于Netty的分布式通信框架实现 前提介绍回顾Dubbo分布式通信框架组成元素程序执行流程消息协议设计实现机制ChannelInboundHandlerAdapter自定义事件处理 ChannelOutboundHandlerAdapter 编(解)码处理器编码过程阶段ChannelOutboundHandlerAdapter序列化实现ChannelOutboundHa…

wireshark利用sshdump自身组件进行远程实时抓包过滤

引言 以前在不了解wireshark可以远程抓包的时间&#xff0c;经常通过tcpdump在远程linux主机将抓包文件保存下来后&#xff0c;然后拖拽入windows中再打开&#xff0c;进行分析查看。 此过程比较繁琐&#xff0c;也不够实时。比较常用的抓包动作是仅出现某特征的报文后&#…

数据结构与算法:复杂度

友友们大家好啊&#xff0c;今天开始正式学习数据结构与算法有关内容&#xff0c;后续不断更新数据结构有关知识内容&#xff0c;希望多多支持&#xff01; 数据结构&#xff1a; 数据结构是用于存储和组织数据的方式&#xff0c;以便可以有效地访问和修改数据。不同的数据结构…

1.23神经网络框架(sig函数),逆向参数调整法(梯度下降法,链式法则(理解,及处理多层神经网络的方式))

框架 输入层 隐藏层 存在一个阈值&#xff0c;如果低于某一阈值就不激活&#xff1b;高于了就激活 输出层 逆向参数调整方法 初始阶段&#xff0c;随机设置权重值w1,w2 依据训练集 两个数学方法 &#xff08;梯度下降、链式法则&#xff09; 调参借助两个数学方法 当导数为…

2024.1.27 GNSS 学习笔记

1.精确的描述轨道的一组数据(星历)是实现精确定位与导航的基础。 2.GNSS卫星广播星历的提供方式一般有两种&#xff1a;一种是提供开普勒轨道参数和必要的轨道摄动改正项参数&#xff0c;如GPS、BDS、Galileo三大系统采用此种模式&#xff0c;还有QZSS系统&#xff1b;另一种是…

Spring Cloud 之Config详解

大家好&#xff0c;我是升仔 在微服务架构中&#xff0c;统一的配置管理是维护大规模分布式系统的关键。Spring Cloud Config为微服务提供集中化的外部配置支持&#xff0c;它可以与各种源代码管理系统集成&#xff0c;如Git、SVN等。本文将详细介绍如何搭建配置服务器、管理客…

用ASM HEMT模型提取GaN器件的参数

标题&#xff1a;Physics-Based Multi-Bias RF Large-Signal GaNHEMT Modeling and Parameter Extraction Flow (JEDS 17年) 模型描述 该模型的核心是对表面势&#xff08;ψ&#xff09;及其随施加的栅极电压&#xff08;Vg&#xff09;和漏极电压&#xff08;Vd&#xff09…

C++ STL中list迭代器的实现

list 的模拟实现中&#xff0c;重难点在于迭代器功能的实现&#xff0c;因此本文只围绕 iterator 及 const_iterator 的设计进行介绍&#xff0c;其余如增删查改则不再赘述——在C语言的基础上&#xff0c;这些都非常简单。 与 string / vector 不同&#xff0c;list 的节点原生…

【时间安排】

最近刚刚回到家&#xff0c;到家就是会有各种事情干扰&#xff0c;心里变乱人变懒的&#xff0c;而要做的事情也要继续&#xff0c;写论文&#xff0c;改简历&#xff0c;学习新技能。。 明天后天两天写论文改简历 周一&#xff08;早上去城市书房&#xff0c;可能吵一点戴个耳…