基础IO+文件

news2024/11/17 13:30:57

基础IO

  • 回顾文件
  • 回顾文件操作
    • 库函数调用接口
      • 写文件-w
      • 读文件-r
    • 系统调用
      • 打开文件-open
      • 写文件-write
    • 文件操作本质
    • 文件描述符fd
    • 文件描述符的分配规则
    • 重定向
  • FILE
    • 缓冲区
    • 模拟实现缓冲区
    • 总结
  • 理解文件系统
    • 磁盘
      • 物理结构
      • 存储结构
      • 逻辑结构
    • 文件系统
    • 软硬链接
  • 动静态库
    • 动态库和静态库
    • 生成静态库
    • 生成动态库
    • 动静态库的加载
      • 静态库
      • 动态库

回顾文件

  1. 空文件,也是要在磁盘中占据空间
  2. 文件=内容+属性
  3. 文件操作=内容+属性操作
  4. 标识一个文件:文件名+路径
  5. 一个文件要被访问必须先打开:先是通过进程调用接口,接着由操作系统去实现
  6. 磁盘中存在着打开的文件和未被打开的文件

文件的本质:进程和被打开文件的关系

回顾文件操作

C语言有文件操作接口,其他语言同样也有,并且都不一样
文件保存在磁盘上,磁盘属于硬件,只能通过操作系统进行访问;想要访问文件就绕不开操作系统;操作系统会提供一系列的系统调用接口进行文件访问

但是,操作系统只有一个,语言却存在很多种;所以无论语言是怎么变化的,系统调用的底层是不变化的

库函数调用接口

写文件-w

int fprintf(FILE *stream, const char *format, ...);

先介绍打印函数 fprintf,将内容写到输入流中也就是写到文件中
在这里插入图片描述

结果如下
在这里插入图片描述

在这里插入图片描述

结果如下

在这里插入图片描述

读文件-r

char *fgets(char *s, int size, FILE *stream);

介绍读取函数 fgets在文件中读取大小为 size的内容到字符串 s

在这里插入图片描述

结果如下

在这里插入图片描述

系统调用

打开文件-open

文件存在时:

int open(const char *pathname, int flags);

文件不存在时:

int open(const char *pathname, int flags, mode_t mode);

返回值

open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).
  1. pathname:文件名/文件路径
  2. flags:标识符:O_RDONLY 只读, O_WRONLY只写,O_CREAT创建,O_APPEND追加,O_TRUNC每次写时把之前的内容清空;宏所定义的;每个宏对应的数值,只有一个比特位,彼此位置不重叠
  3. mode:文件权限
  4. 返回值:成功,返回文件描述符;失败,返回-1

由于文件不存在,所以需要先创建O_CREAT;文件权限默认是0666

在这里插入图片描述

结果如下
由于掩码的存在,文件权限并不是0666

在这里插入图片描述

写文件-write

ssize_t write(int fd, const void *buf, size_t count);

将大小为 count的内容 buf写到文件中;注意这里的内容类型没有限制,无论是二进制类还是文本类,在操作系统看来都是二进制类;写入成功时返回写入的大小 ssize_t;注意在写入字符串时,这里不同于C语言的规定,不需要计算最后一个字符

在这里插入图片描述

结果如下

在这里插入图片描述

相比于库函数调用的写文件 w就相当于系统调用FILE_NAME,O_WRONLY|O_CREAT|O_TRUNC,0666;所以库函数调用本质是对系统调用的封装,追加,读取类似

在这里插入图片描述

文件操作本质

在上面已经说明文件操作的本质是进程和被打开文件的关系,接下来进行深入学习

首先一个进程可以打开多个文件,所以系统中就必然存在着大量被打开的文件,系统便需要对这些文件进行管理,管理方式:先描述再组织;为了管理这些文件,创建对应的内核结构体struct file{ },其中包含文件的大部分属性

在上面系统调用打开文件时,返回的文件描述符是数字3,为什么呢???
再观察下列代码
在这里插入图片描述
在这里插入图片描述

同时打开三个文件,返回的文件描述符是连续的额3,4,5;为什么是从3开始呢,而且还是连续的呢???

其原因是,每个文件中都存在着三个标准输出流

stdin   键盘
stdout  显示器
stderr  显示器

在这里插入图片描述

它们占据着文件描述符的前三个位置
验证结果如下

在这里插入图片描述
在这里插入图片描述

在系统调用中接受返回值(文件描述符)是使用 int;为什么在标准输出中也会存在着库函数调用接口中接受返回值的 FILE*呢???
其实 FILE本身是一个结构体,是对系统调用的封装,所以其中就有一部分是文件描述符

文件描述符fd

上面还剩一个问题没有解决,就是问什么为返回的文件描述符都是连续的呢???

下面进行图解

在这里插入图片描述

磁盘中的文件test.txt加载到内存中,系统创建结构体struct file对其进行管理;进程的结构体中存在着一个指向管理所有内存中文件的结构体,次结构体中存在这一个数组,也称进程的文件描述符表从下标为零的位置开始指向标准输出,输入,错误,紧接着就是管理文件的结构体;所以,文件描述符的本质就是数组的下标

文件描述符的分配规则

观察下列代码,当我们把文件描述符fd==0关闭时,结果会怎么样呢

在这里插入图片描述
在这里插入图片描述

如果关闭的是文件描述符fd==2(不是关闭fd==1,原因下面会解释,结果又是怎么样的呢?

在这里插入图片描述
在这里插入图片描述

由此便可以得出结论:文件描述符的分配规则是从小到大,按照顺序寻找最小的且没有被占用的文件描述符

如果关闭文件描述符fd==1,结果会怎样呢?
在这里插入图片描述
在这里插入图片描述

很奇怪!!!为什么是这个结果,不应该是打印数字1吗?
文件描述符fd==1是标准输出,也就是将结果打印到显示器上面,现在它被关闭了结果便是显示器并没有打印结果,很合理;那么本来应该被打印的结果去了哪里呢
下面进行图解

在这里插入图片描述

从图中可以看到,根据文件描述符的分配规则。当fd==1被关闭之后,管理文件的结构体就寻找到该描述符,并将其指向自己;所以原本应该打印到显示器中内容,现在都被打印到了文件中,接下来打开文件进行验证

在这里插入图片描述

结果正确,其实这里还存在着缓冲区的问题,在后面会进行学习的;本应该打印到显示器的内容通过关闭描述符,就可以改变其打印的方向,这种操作就称作重定向,下面我们就来学习有关重定向的内容吧

重定向

重定向的本质:上层调用不变的情况下,改变底层的输出方向
上面是通过关闭文件描述符的方式进行重定向,这种方式太低端;这里介绍一种新的凡是进行重定向

int dup2(int oldfd, int newfd);

将文件描述符表中指向旧文件结构体的指针改变方向,指向新的文件结构体中;若成功,返回新的文件描述符

重定向标准输出dup2(fd,2)
代码如下
在这里插入图片描述
在这里插入图片描述

图解

在这里插入图片描述

重定向追加,标准输入类似,这里就不加赘述

Linux下一切皆文件,这句话该如何理解呢?
在冯诺依曼体系中,操作系统是通过驱动进而控制硬件;每种硬件其实都是由相应的结构体进行控制的,包括其读写功能;上面学习的当文件加载到内存中时会被其对应的结构体进行管理,其实对应的结构体中包含了两个重要的属性读写函数指针;当我们向键盘输入内容时,相应的文件就会调用其函数指针指向键盘结构体对应的功能,由此便可对上面的文件结构体进行补充:每个文件打开时,都会有对应的结构体去进行管理它,同时会调用函数指针完成初始化,并且找到该文件在内核中的缓冲区;所以就在文件结构体的上层来看,所有的设备和文件,统一都是通过调用文件结构体去完成,便可以解释Linux下一切皆文件

图解如下

在这里插入图片描述

FILE

观察下列代码
在这里插入图片描述
在这里插入图片描述

由结果来看,直接打印到显示器上面时,代码正常;当重定向到文件中时,却有些不同,库函数调用接口都打印了两次,系统调用接口却只打印了一次,这又是为什么呢?

为了解决这个问题,先来学习缓存区的概念

缓冲区

缓冲区本质就是一段内存

如果要现在的快递行业极大地节省了我们的时间;如果要寄快递,只需要将快递放到寄存地点,物流就会帮我们将其送达目的地

其实物流同样也存在于操作系统中;比如我们向文件中写东西,大的角度来看是从内存将内容写到磁盘中,其底层是进程将数据写到文件中,其过程是:先将数据拷贝到缓冲区,缓冲区再将数据拷贝到文件中,缓冲区的存在极大地减少了数据写到文件的时间;这也与缓冲区的结构有关

缓冲区刷新的策略
如果存在一块数据,可以一次性全部都写入到外设中;也可以多次批量写入到外设中;缓冲区会根据具体的外设,制定相应的刷新策略

  1. 立即刷新也就是无缓冲
  2. 行刷新-行缓冲对应的外设就是显示器
  3. 当缓冲区已经满时进行刷新-全缓冲

其实还存在两种刷新方式:用户强制刷新;进程退出时都会进行缓冲区刷新

缓冲区到底在哪呢?上面代码的打印结果与缓冲区有关,从结果来看,其一定不存在于内核之中;上面学习的三个标准输出流,输入流,错误流其类型是FILE*,对应的结构体FILE包含着文件描述符,并且还包括缓冲区

现在来解释上面打印结果差异的原因:打印到显示器上面时,stdout默认是行刷新,在创建子进程之前,三条库函数调用已经将数据打印到显示器上,结构体FILE中已经不存在对应的数据;重定向到文件中时,缓冲区采用的是全缓冲,库函数调用接口,虽然含有\n,但是不足以将stdout缓冲区写满,数据并没有被刷新,创建子进程时,stdout属于父进程父进程,接着就是进程退出,父进程或子进程退出时,一定要进行缓冲区刷新,也就是进行数据修改,所有发生了写时拷贝,后一个进程退出时也会打印一次;系统调用之所以没有没打印两次,是因为write中并没有FILE结构体,所有也没有所谓的缓冲区

模拟实现缓冲区

mystdio.h

  1 #pragma once
  2 
  3 #include<assert.h>
  4 #include<stdlib.h>
  5 #include<errno.h>
  6 #include<string.h>
  7 #include<unistd.h>
  8 #include<sys/types.h>
  9 #include<sys/stat.h>
 10 #include<fcntl.h>
 11 
 12 #define SIZE 1024
 13 #define SYNC_NOW 1
 14 #define SYNC_LINE 2
 15 #define SYNC_FULL 4
 16 
 17 typedef struct _FILE{
 18   int flags;//刷新方式
 19   int fileno;//文件描述符
 20   int cap;//总容量
 21   int size;//当前大小
 22   char buffer[SIZE];
 23 }FILE_;
 24 
 25 FILE_ *fopen_(const char*path_name,const char*mode);                                                                                                                       
 26 void fwrite_(const void*ptr,int num,FILE_ *fp);
 27 void fclose_(FILE_*fp);
 28 void fflush_(FILE_*fp);

mystdio.c

  1 #include"mystdio.h"
  2 
  3 FILE_*fopen_(const char*path_name,const char*mode)
  4 {
  5   int flags=0;
  6   int defaultmode=0666;
  7   if(strcmp(mode,"r")==0)
  8   {
  9     flags|=O_RDONLY;
 10   }
 11   else if(strcmp(mode,"w")==0)
 12   {
 13     flags|=(O_WRONLY|O_CREAT|O_TRUNC);
 14   }
 15   else if(strcmp(mode,"a")==0)
 16   {
 17     flags|=(O_WRONLY|O_CREAT|O_APPEND);
 18   }
 19   else
 20   {
 21 
 22   }
 23 
 24   int fd=0;
 25   if(flags&O_RDONLY)
 26     fd=open(path_name,flags);
 27   else 
 28     fd=open(path_name,defaultmode);
 29 
 30   if(fd<0)
 31   {
 32     const char*err=strerror(errno);
 33     write(2,err,strlen(err));
 34     return NULL;
 35   }
 36 
 37   FILE_*fp=(FILE_*)malloc(sizeof(FILE_));
 38   fp->flags=SYNC_LINE;
 39 
 40   fp->fileno=fd;
 41 
 42   fp->cap=SIZE;
 43 
 44   memset(fp->buffer,0,SIZE);
 45                                                                                                                                                                            
 46   return fp;
 47 }
 48 
 49 void fwrite_(const void*ptr,int num,FILE_*fp)
 50 {
 51   //写入到缓冲区
 52   memcpy(fp->buffer+fp->size,ptr,num);
 53   fp->size+=num;
 54 
 55   //判断是否需要刷新
 56   if(fp->flags&SYNC_NOW)
 57   {
 58     write(fp->fileno,fp->buffer,fp->size);
 59     fp->size=0;//清空缓冲区
 60   }
 61   else if(fp->flags&SYNC_FULL)
 62   {
 63     if(fp->size==fp->cap)
 64     {
 65       write(fp->fileno,fp->buffer,fp->size);
 66       fp->size=0;
 67     }
 68   }
 69   else if(fp->flags&SYNC_LINE)
 70   {
 71     if(fp->buffer[fp->size-1]=='\n')
 72     {
 73       write(fp->fileno,fp->buffer,fp->size);
 74       fp->size=0;
 75     }
 76   }
 77   else
 78   {
 79 
 80   }
 81 }
 82 
 83 
 84 void fflush_(FILE_*fp)
 85 {
 86   if(fp->size>0)
 87     write(fp->fileno,fp->buffer,fp->size);
 88 
 89   fsync(fp->fileno);//强制要求刷新
 90   fp->size=0;                                                                                                                                                              
 91 }
 92 
 93 void fclose_(FILE_*fp)
 94 {
 95   fflush_(fp);
 96   close(fp->fileno);
 97 }

总结

当我们向文件中写入时,肯定不是直接将内容写入到文件中,其中还包含着许多步骤;首先将内容拷贝到库所提供的缓冲区中fwrite(),也就是FILE中的缓冲区,紧接着文件结构体struct_file通过调用其函数指针将内容拷贝到内核缓冲区write();最后由操作系统决定按照什么样的刷新策略将内容写到文件中

在这里插入图片描述

理解文件系统

如果一个文件没有被打开呢?磁盘中存在着许多没有被打开的文件,这些文件又该如何进行管理呢?

文件系统就是为了管理这些没有被打开的文件,在学习文件系统之前,先来了解磁盘的结构

磁盘

物理结构

在这里插入图片描述

磁盘是计算机中唯一的一个机械结构,同时也是外设
结构包括盘面和磁头,盘面的两面都有磁头;盘面通过马达控制旋转,磁头也通过马达控制左右摇摆,两者之间是没有任何接触的

存储结构

在这里插入图片描述

磁盘被划分为多个同心圆,每个同心圆称作磁道也称作柱面,同时每个磁道又被分为多个圆弧,每个圆弧称作扇区,数据就存储在每个扇区中,并且每个扇区存储的大小都是512 byte

在盘面上进行寻址时,磁头来回摆动确定在哪个磁道上,紧接着盘面旋转再确定在哪个扇区上,这种方法称为 CHS定位法

逻辑结构

在这里插入图片描述

磁盘物理结构上是圆盘形状,可以将其想象成线性结构,就像磁带一样,卷起来是圆形的,扯出来就是线性结构的

在这里插入图片描述

把磁盘从逻辑上看作是一个数组sector arr[n+1],每个元素是一个扇区,对磁盘的管理,转化为对数组进行管理;转化为逻辑结构之后,再进行寻址就变得简单很多,只需要知道这个扇区的下标就能定位到该扇区,在操作系统中,称这种地址为LBA地址

优点:

  1. 方便管理
  2. 避免代码和硬件强耦合

进一步理解磁盘读写数据

虽然磁盘每次访问的基本单位都是512byte,但相对来说还是太小,如果文件的大小是4字节或8字节,那么要访问磁盘8次或者16次,效率太低;所以操作系统每次访问时,会进行多个扇区的读写,以4字节作为基本单位

文件系统

文件系统为了对磁盘,进行管理,先进行分区,就相当于将每个同心圆分隔出来;再进行分组,每个同心圆中有不同的区域,每个区域所存储的内容也不同

在这里插入图片描述

文件=内容+属性
Linux的文件属性和文件内容是分批存储的;inode是用来存储文件的几乎所有属性,出来文件名,每个文件对应一个inode,大小固定,inode为了进行彼此区分,都有自己的IDdata block存储着文件内容,随着应用类型的大小在变化

  1. Super Block:保存的是整个文件系统的信息,并不是所有块组中都有,主要是为了备份
  2. Group Descriptor Table:对应分组的宏观属性信息
  3. Block Bitmap:数据块对应的位图,位图中的比特位位置和当前data block对应的数据块位置是一一对应的
  4. inode Bitmapinode对应的位图结构;位图中比特位位置和当前文件对应的inode的位置是一一对应的
  5. inode table:保存了分组内部所有的inode(已经使用或未被使用的),如果先要添加一个文件,首先要到该组中找到一个未被使用的inode进行文件属性的存储
  6. data blocks:保存分组内部所有文件的数据块

当我们查找某个文件时,统一使用的是:inode编号,先到inode Bitmap中查看该文件是否存在,如果存在,可以读取文件的属性;如果要读取文件的内容又该如何呢?

在这里插入图片描述

inode的数据结构中存储着自身的编号,大小,还有数组,当读取文件内容时,可通过该数组到data Blocks中找到对应的扇区,便可对文件内容进行读写操作

文件删除操作就较为简单,只需要修改Block Bitmapinode Bitmap即可

在这里插入图片描述

所有的文件都放在目录中,目录也是文件,也有自己的属性和内容,在Linux的操作中,对文件或目录从来没有使用过inode,而且文件名也不存储在inode中,所以目录中的数据块存储的是什么呢???

其实目录中的数据块存储的是当前目录下的文件名于inode的映射关系,这也就是解释了为什么在目录中新增文件必须有写入权限,因为新增文件时,需要在目录中写入文件名与inode的映射关系

软硬链接

在这里插入图片描述

观察上面所创建的软硬链接,可以发现:软链接具有独立的 inode可以作为独立文件,而硬链接却是和文件共用一个 inode,既然如此,那么创建硬链接有何用处呢???

首先创建硬链接,根本没有创建新文件,所使用的还是原本文件的内容和 inode,所以创建硬链接的目的就是在指定路径下,新增文件名和 inode编号的映射关系
图解如下

在这里插入图片描述

inode编号结构中存在着引用计数,记录着硬链接数,也就可以解释图中,为什么文件和硬链接的计数为2,因为两者全都指向了同一个 inode编号;所以只有当一个文件的硬链接变为0时,这个文件才真正被删除

硬链接的作用如此,那软链接呢???
如果将文件删除,结果会怎样呢?

在这里插入图片描述
从图中可以看到,软链接此时已经出问题,而硬链接正常;如果再重新创建一个新文件会发生什么?

在这里插入图片描述

由此可见,软链接是只认识文件名,在系统中只能通过特定路径进行查找文件,就类似快捷方式,方便用户使用

观察下列指令

在这里插入图片描述

创建一个文件,硬链接数为1,因为其本身的文件名和自己的inode具有映射关系,但是空的目录为什么硬链接数为2呢??

试着进入目录中,看看是否有新的发现
在这里插入图片描述

进入目录中发现,里面还存在着一个名为.的隐藏文件,而且它的inode编号还是和目录的inode的编号一样,这就可以解释为什么一个目录中的硬链接数为2;不过,还可以目录中还存在着一个名为..的文件,它的作用是什么呢???接下来慢慢揭晓

在目录yjm中创建一个空目录dir

在这里插入图片描述

图中可以发现,创建完新目录后,原本目录中的硬链接就变成了3,进入新目录中,发现文件名为..的文件inode编号与原目录的inode一样,所以这也就解释为什么硬链接数变化的原因;目录中文件名为.的文件表示当前文件,文件名为..的文件表示上级文件

关于软硬链接的内容已经结束,还剩最后一个疑问
在这里插入图片描述

为什么不允许给目录进行硬链接,却可以进行软链接?

原因很简单,如果给根目录进行硬链接,然后通过根目录寻找次链接,便会无限循环,出现问题

动静态库

动态库和静态库

  1. 静态库(.a):程序在编译链接时把库的代码链接到可执行文件中,程序运行时不再需要静态库
  2. 动态库(.so):程序在运行时才去链接动态库的代码,多个程序共享使用库的代码
  3. 一个与动态库链接的可执行文件仅仅包含他用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  4. 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接
  5. 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省内存和磁盘空间

先完成一个小任务,在一个目录中完成两个函数的实现,然后将其二进制可执行文件.o还有头文件.h拷贝到另一个目录中,使用它自己的main函数来完成最后的链接执行这个两个函数

加减函数的实现和头文件如下

在这里插入图片描述
在这里插入图片描述

通过 gcc -c生成可执行二进制文件
在这里插入图片描述

将所有 .o文件和头文件.h拷贝到另一个目录中

在这里插入图片描述

main.c

在这里插入图片描述

链接最后运行程序
在这里插入图片描述

所以如果我们不想给对方自己的源代码,只需要提供对方 .o方法的实现, .h包含的方法,对方便可使用自己的代码进行链接最后执行

如果有很多方法,那就需要拷贝多份 .o文件,是不是很麻烦呢???所以想了个办法,将所有的 .o文件进行打包,只提供对方一个文件,而这个文件也称为,库又分为静态库和动态库,接下来就来学习吧

生成静态库

先介绍两个指令
ar:是归档指令, archive将文件打包到一个文件中

rc:表示 replace and create

在这里插入图片描述

这只是将库文件进行了打包,在上面的例子中,还包括了头文件,所以还需要进行改进
output:称作发布版本,打包之后便可进行发布

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

自己生成的静态库便完成了,只需要进行发布即可

将库进行压缩,发布
在这里插入图片描述

在测试目录中进行库的下载(也就是拷贝),解压

在这里插入图片描述

在进行链接时,Linux有些不同,在之前的学习中,链接阶段都是现在当前程序中寻找头文件,然后再到其他程序中寻找头文件;还需要告知编译器,当前库所在路径,不经如此还要告知其库的名称,当然在之前的学习中这一切都是编译器默认所做到

所以,链接操作如下
-I ./mylib/include:告知编译器所包含的头文件
-L ./mylib/lib:指明库所在路径
-l math:指明库名称

在这里插入图片描述

还剩最后一个小问题,观察下列指令
在这里插入图片描述

为什么 mymath是动态链接的,并且编译器所罗列的库中并没有刚刚自己所写的库,这是为什么呢???

gcc默认是动态链接的,如果只有一个库,最后是动态链接还是静态链接却决于这个库的类型;但是如果存在很多库,当链接时如果使用了一个动态库,则就是动态链接,上面在最后链接时,库只提供了静态库,而默认是动态链接,所以只能将将代码拷贝到可执行程序中使用动态库进行链接

生成动态库

与静态库类似,有一点区别
在生成.o文件时,需要加上fPIC生成位置无关码
在这里插入图片描述

进行文件打包,这里可以直接使用gcc再加上shared生成共享库格式即可

在这里插入图片描述

分别将头文件和库文件放入相应的目录中,拷贝给测试目录
在这里插入图片描述

最后进行链接执行程序
在这里插入图片描述
在这里插入图片描述

这里与静态库有所不同,动态库已经加载到程序中,但是并没有找到;在链接时,我们已经将库文件,路径和库名称都告知了gcc,当程序链接之后,与编译器就无关了;运行时操作系统和命令行解释器也是需要知道库所在的位置,但是由于库并没有在系统的默认路径下,所以操作系统无法找到,程序也就无法进行

解决措施也很简单,只需要将库路径添加到默认搜索路径即可,在当前目录中进行软链接,程序即可运行

在这里插入图片描述

动静态库的加载

静态库

静态区并不是加载到内存中的,在程序运行时所需要使用的函数被拷贝到程序的代码段中,且必须按照相对确定的地址进行编址,称作绝对编址;再由程序拷贝到内存中,再通过虚拟地址空间映射到代码段上进行访问
图解如下

在这里插入图片描述

动态库

在这里插入图片描述

动态库的加载是相对位置的加载,在程序中存在着库中函数的偏移量,这也解释了为什么上面使用 fPIC生成位置无关码;程序加载到内存中的代码段,通过页表映射到虚拟地址空间的代码段中,当程序执行到库函数时,此时程序中只有函数的偏移量,进程便停止,将动态库加载到内存中,再通过页表映射到虚拟地址空间中的共享区,一旦库函数加载到共享区中,起始位置就确定了,然后程序通过偏移量在库中寻找对应的函数,至此动态库加载的内容结束

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

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

相关文章

Kafka上的优化经验

1. 平滑扩容 Motivation kafka扩容⼀台新机器的流程 假如集群有 3 个 broker &#xff0c;⼀共有 4 个 TP &#xff0c;每个 3 副本&#xff0c;均匀分布。现在要扩容⼀台机器&#xff0c; 新 broker 加⼊集群后需要通过⼯具进⾏ TP 的迁移。⼀共迁移 3 个 TP 的副…

JavaScript高级程序设计(第2版)——读书笔记

文章目录 第1章 JavaScript简介第2章 在HTML中使用JavaScript第3章 基本概念第4章 变量、作用域、内存问题第5章 引用类型第6章 面向对象的程序设计第7章 匿名函数第8章 BOM第9章 客户端检测第10章 DOM第11章 DOM2和DOM3第12章 事件第13章 表单脚本第14章 错误处理与调试第15章…

java 的参数传递

一、疑惑引入 首先&#xff0c;我们从一个例子来引出这个问题&#xff1a; public static void main(String[] args) throws IOException {List<String> mockList Lists.newArrayList("a", "b");System.out.println("1: " mockList);L…

【GAMES101】04 Viewing Transformation

1.View/Camera Transformation&#xff08;视图变换&#xff09; 1、将准备拍摄的对象移动到场景中指定位置。&#xff08;模型变换&#xff0c;Model Transform&#xff09; - 模型导入场景中从模型坐标系转换成世界坐标系 2、将相机移动到准备拍摄的位置&#xff0c;将它对准…

【网络】-- TCP协议

其中TCP就属于传输层&#xff0c;并且端口号也是在传输层起作用。 目录 TCP协议报头 可靠性 32位序号 16位窗口大小 六个标记位 三次握手四次挥手 RST PSH URG 16位紧急指针 FIN socksetopt 可靠性机制 确认应答(ACK)机制 超时重传机制 连接管理机制 三大机…

03-角色维护 尚筹网

一、分页操作 目标 将角色数据进行分页显示 思路 点击后台主页面的权限管理->角色维护&#xff0c;通过view-controller进入角色分页显示的页面&#xff0c;浏览器加载页面的数据并初始化一些数据&#xff08;页码、页大小、关键词等&#xff09;&#xff0c;调用分页函…

基于Open3D的点云处理3-可视化

可视化接口 API open3d.visualization.draw_geometries(*args, **kwargs)重载函数1 draw_geometries(geometry_list, window_name’Open3D’, width1920, height1080, left50, top50, point_show_normalFalse, mesh_show_wireframeFalse, mesh_show_back_faceFalse)geometry…

B树

文章目录 B树的定义和性质为什么需要B树B树的定义 B树的模拟实现节点的数据结构B树的插入B树的删除 B树的模拟实现 B树的定义和性质 我们之前已经对 平衡搜索二叉树有了一定的了解&#xff0c;学习了两种树——AVL树 和 红黑树&#xff0c;下面介绍一下B树 为什么需要B树 数…

Nacos 服务网格⽣态

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面…

为一加七Pro(LineageOs17.1 4.14内核版本)编译KernelSu

编译内核 因为一加七的内核版本是4.14&#xff0c;所以想使用kernelsu&#xff0c;需要自己将kernelsu编译到内核里。 我使用的系统是&#xff1a;LineageOS17.1&#xff0c;对于之后的lineage版本同样适用&#xff0c;只是拉取的源代码不一样。刷机教程请看&#xff1a;wsl2…

vue diff算法与虚拟dom知识整理(3) 了解h函数和虚拟节点概念 实现虚拟节点上dom树

虚拟dom之前我们也有了基本的了解 简单说 就是用js数据结构来描述html的dom结构树 首先 为什么要用虚拟dom啊&#xff1f; 官方给出的回答是 diff最小量精细化算法是发生在虚拟dom上的 也就是 我们之前说的 节点与节点比较 并不是发生在html元素上的 而是发生在js中的虚拟dom上…

【C++学习】类和对象--多态【待补充】

多态的基本语法 多态是C面向对象三大特性之一 静态多态&#xff1a; 函数重载和运算符重载属于静态多态&#xff0c;复用函数名&#xff08;函数地址早绑定&#xff0c;编译阶段确定函数地址&#xff09; 动态多态&#xff1a; 派生类和虚函数实现运行时多态&#xff08;函数地…

centos7.6 yum 安装mysql

目录 1. 删 mariadb / 自带MySQL 2 安装wget命名 3 下载并安装MySQL官方的 Yum Repository 4 使用yum安装mysql 5 启动 6 获取密码 7 登录 -> 没有获取到 就直接按回车 不输入 8 设置密码 及权限 --> root 账号所有语句报错 9 参考 1. 删 mariadb / 自带MySQL…

《计算机网络——自顶向下方法》精炼——2.7.2(TCP套接字编程)

“学习的敌人是自己的满足。” —— 叶圣陶 文章目录 TCP套接字编程TCP套接字编程概述客户进程服务器进程 TCP套接字编程 TCP套接字编程概述 TCP是一个面向连接的运输层协议&#xff0c;因此可以分为发起连接的阶段和传输阶段。 发起连接时&#xff0c;客户进程创建一个客户…

【LeetCode】221.最大正方形

221.最大正方形&#xff08;中等&#xff09; 题解 对于在矩阵内搜索正方形或长方形的题型&#xff0c;一种常见的做法是&#xff1a;定义一个二维 dp 数组&#xff0c;其中 dp[i][j] 表示满足题目条件的、以&#xff08;i,j&#xff09;为右下角的正方形或长方形属性。在本题中…

【备战蓝桥杯国赛-国赛真题】费用报销

题目链接&#xff1a;https://www.dotcpp.com/oj/problem2696.html 思路 读完题&#xff0c;再看一眼数据范围&#xff0c;这道题的做法也就确定了——DP。 DP的题目往往很容易辨识出来&#xff0c;所以我们就往DP上想了&#xff0c;第一要素是选出的所有票据里面&#xff0c…

【LeetCode】64. 最小路径和

64. 最小路径和&#xff08;中等&#xff09; 方法一&#xff1a;常规动态规划 思路 定义一个二维 dp 数组&#xff0c;其中 dp[i][j]表示从左上角开始到&#xff08;i, j&#xff09;位置的最优路径的数字和。因为每次都只能向下或者向右移动&#xff0c;所以很容易发现 dp数组…

汽车行业V模型开发详解

在新能源汽车开发过程中&#xff0c;通常会采用V模型&#xff08;V-Model&#xff09;进行系统开发。V模型是一种基于需求分析、体系架构设计、硬件和软件开发、集成测试以及产品验证的系统工程方法。 下面简要介绍新能源汽车V模型开发的主要阶段&#xff1a; V模型开发&…

encrypted勒索病毒攻击nas服务器,服务器中了勒索病毒解密数据恢复

近年来&#xff0c;勒索病毒的攻击技术不断升级&#xff0c;各种加密型的病毒不断出现&#xff0c;给我们工作和生活带来了很大困扰。其中&#xff0c;encrypted勒索病毒攻击NAS网络存储设备已经变得越来越常见。而这次我们将为大家探讨如何预防encrypted勒索病毒攻击NAS服务器…

springboot+vue教师人事档案管理系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的教师人事档案管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1…