标准IO和系统IO的相关知识积累
文件IO
知识点补给
1.FAT32与NTFS文件系统的区别?
答:NTFS和FAT32是两种不同的文件系统格式,它们在功能、安全性和性能等方面存在一些区别。
1、功能和性能:NTFS是一种高度可恢复的文件系统,具有许多高级功能,如数据恢复、加密、压缩、磁盘配额等。相比之下,FAT32文件系统在功能和性能方面较为有限。
2、磁盘分区大小:NTFS支持的磁盘分区大小最大可达2TB(2048GB),而FAT32支持的分区大小最大为32GB。因此,对于需要使用大容量磁盘的用户来说,NTFS是更好的选择。
3、单个文件大小:NTFS突破了单个文件4GB的容量限制,目前来说似乎没容量限制,只要硬盘空间容量有多大,那么就NTFS就可以分到多大。而FAT32在实际运行中不支持单个文件大于4GB的文件,一旦超过容量限制那么系统就会提示磁盘空间不足。
4、安全性:NTFS提供了更高级的安全功能,可以对文件和文件夹进行加密和权限设置,而FAT32则不具备这样的安全功能。在NTFS下,用户可以对电脑用户对该格式下所有的文件夹、文件进行加密、修改、运行、读取目录及写入权限的设置。此外,在磁盘分区下任意文件夹或文件上右键属性,在高级属性窗口中勾选中加密内容以便保护数据即可做到加密。
5、碎片和空间利用:随着时间的推移和使用次数的增加,文件可能会产生碎片。从FAT16的文件系统格式,到之后的FAT32然后再到现在的NTFS文件系统格式,从磁盘分区的格式不同,那么所产品的磁盘碎片也是越来越小。
6、磁盘配额:NTFS支持磁盘配额,可以在一个NTFS分区上为不同用户设置不同的磁盘空间配额,而FAT32不支持磁盘配额。
7、文件恢复:NTFS具有更好的文件恢复功能,可以恢复意外删除或格式化的文件,而FAT32的文件恢复功能相对较弱。
8、压缩和加密:NTFS支持文件和文件夹的压缩和加密,而FAT32不支持。
9、日志记录:NTFS支持事务日志记录,可以记录对文件系统的更改,而FAT32不支持。
10、文件链接:NTFS支持硬链接和符号链接,而FAT32不支持。
11、文件访问控制:NTFS支持更复杂的文件访问控制,可以针对不同用户或用户组设置不同的访问权限,而FAT32不支持。
12、安全性:NTFS具有更高的安全性,可以防止恶意软件或黑客对文件进行修改或删除,而FAT32的安全性相对较低。
综上所述,NTFS和FAT32在功能、性能、磁盘分区大小、单个文件大小、安全性和碎片和空间利用等方面存在显著差异。因此,在选择文件系统格式时,需要根据实际需求和使用场景进行选择。如果需要使用大容量磁盘并需要高级安全功能和数据恢复能力,则建议使用NTFS文件系统。如果只需要一个简单的文件系统用于存储少量数据,并且不需要高级功能,则FAT32文件系统可能是一个更好的选择。
2.MMU的概述和作用
MMU(Memory Manager Unit),中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。
- 1 虚拟地址到物理地址的映射。MMU负责将应用程序生成的逻辑地址(虚拟地址)转换为物理地址,这使得程序可以使用比实际物理内存更大的地址空间。
- 2 内存保护。通过设置访问权限位,MMU可以保护内存区域,防止程序访问或修改关键数据。例如,它可以标记某些内存区域为只读或禁止访问。
- 3虚拟内存管理。MMU实现了虚拟内存的概念,允许每个进程认为自己拥有整个物理内存,而实际上物理内存可能被多个进程共享。它负责将进程的虚拟地址映射到物理内存,以便多个进程能够同时运行而不会相互干扰。
- 4存储器访问权限的控制。MMU可以控制对存储器的访问权限,包括读写权限。
- 5设置虚拟存储空间的Cache特性。MMU可以设置虚拟存储空间的缓存特性,以提高数据访问的速度和效率。
- 6处理页面缺失和页面替换算法。MMU通过TLB(Translation Lookaside Buffer)来加速虚拟地址到物理地址的转换,如果发生TLB缺失,MMU会使用页面替换算法来处理页面替换,以保持物理内存的有效使用。
总的来说,MMU是现代计算机系统中的重要组成部分,它通过虚拟内存管理、内存保护和地址映射等功能,极大地提高了计算机系统的内存管理效率和安全性。
3.简述Linux系统内核的作用
Linux内核的功能主要是将应用层请求传递给硬件,并作为底层驱动程序,以间接控制寻址系统中的各种设备和组件。
- 1.进程管理:Linux内核负责管理系统中运行的进程,包括进程的创建、调度、销毁等操作,确保进程能够正确地运行并共享系统资源。
- 2.文件系统管理:Linux内核提供文件系统的支持,包括文件的读写、权限管理、文件系统的挂载等操作,使得应用程序能够方便地访问和操作文件。
- 3.网络管理:Linux内核提供网络协议栈的支持,包括TCP/IP协议、网络设备驱动等,使得计算机能够进行网络通信。
- 4.内存管理:内存资源的使用策略对操作系统性能体现来说,尤为重要。内存在有限的内存资源上,为每一个进程建立了一个虚拟地址空间。内核的不同功能部分与内存管理子系统通过一套函数调用交互,使得通信高效简单。
4.了解Linux系统目录和文件夹的区别
Linux中的目录与Windows中的文件夹的含义是很容易混淆的,表面 上好像一致,实则不然。
- Windows中的文件夹类似于一种容器,大文件里面存放了很多文件以及子文件,子文件里面又嵌套有别的文件夹,一层套一层,就好比俄罗斯套娃,不管怎么套,里面的总比外面的小,故在Windows中子文件是不可能比外部文件还大的。
- Linux中的目录并不是一种容器,而仅是一个文件索引表,目录下的文件的真正的内容存储在分区中的数据域区域。目录中索引表的每一项称为“目录项”,里面至少存放了一个文件的名字(不含路径部分)和索引号(分区唯一)。
标准IO接口
一、打开文件
函数1:fopen
- 头文件:
#include<stdio.h>
- fopen函数的功能:
获取指定文件的文件指针。 - fopen函数作用的整体流程:
用户可以在一个程序中利用fopen函数打开多个文件,每次打开一个文件,内核就会从堆内存中申请一块FILE结构体大小的空间用来存储文件的所有信息,然后按照文件打开的顺序把每个打开的文件的结构体形成一条链表,然后使用链表头进行管理。 - 函数原型:
FILE*fopen(const char *pathname,const char *mode);
- 函数参数:fopen有两个参数
参数一:const char *pathname:指的是打开文件的路径,需要字符串,例如 “demo.txt”,注意有具体文件名的话,即固定文件,需要加“ ”。
参数二:const char *mode:指的是访问文件的权限。
参数二选择项如下:
r:以只读方式打开文件,要求文件必须存在,光标在文件开头
r+:以可读可写方式打开文件,要求文件必须存在,光标在文件开头
w:以只写方式打开文件;如果文件不存在则会创建新文件,如果存在文件则会将其内容清空;光标在文件的开头。
w+:以可读可写方式打开文件;如果文件不存在则会创建新文件,如果存在文件则会将其内容清空;光标在文件的开头。
a:以只写方式打开文件;如果文件不存在则会创建新文件;光标在文件的末尾,意味着以追加的方式写入内容。
a+:以可读可写方式打开文件;如果文件不存在则会创建新文件;光标在文件的末尾,意味着以追加的方式写入内容。 - 返回值:
成功:返回文件指针(当前被打开的文件的文件指针)
失败:返回NULL
➤ 思考fopen函数的返回值是一个指向被打开文件的FILE类型的指针,请问FILE类型是什么?
回答:FILE类型其实是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,比如包括用于实际I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。
补充:kernel采用链式结构来管理被打开的文件,原因:FILE结构体类型中有一个成员是FILE类型的指针变量chain,该指针可以指向下一个被打开文件的文件信息区,也就是可以把FILE类型当做数据结构中的链表的结点,结点中除了可以存储数据域之外,还可以利用指针域存储下一个结点的地址。
注意:
打开文件的目的无非就是对文件进行读写操作,所以每次当程序运行的时候已经有三个文件流被打开,分别是标准输入stdin、标准输出stdout、标准出错stderr,这三者在stdio.h中也是FILE指针。
2.使用标准IO的时候,是不可以反复关闭相同的文件,因为释放已经被释放的堆内存,会导致段错误!!
二、读取数据
标准C库中提供了多个读取函数,这些函数大体分为三类:字符读取(fgetc)、按行读取(fgets)、按块读取(fread)。
(1)字符读取
函数2:fgetc
- 头文件:
#include<stdio.h>
- fgetc函数的功能:
获取指定文件的一个字符 - fgetc函数作用流程:
从文件指针stream指向的文件中读取一个字符,并在读取一个字节后把文件的光标位置向后移一个字节,然后读取成功则把读取到的字符所对应的ASCII码通过返回值返回,读取失败则返回EOF。 - 函数原型:
int fgetc(FILE *stream );
- 函数参数:fgetc函数仅1个参数
参数FILE *stream:指的是文件指针 - 结束条件:
文件的光标已经到达文件末尾或者遇到读取错误时 - 返回值:
成功:返回读取字符的ASCII 码
失败:返回EOF (EOF 是一个宏定义,宏定义的值为-1)
另外,在标准库中还提供了另一个函数getc(),这个函数的作用等效于fgetc()函数,只不过getc()函数的实现是利用宏定义而已。
函数3:getc
- 头文件:
#include<stdio.h>
- getc函数的功能:
获取指定文件的一个字符 - getc函数的作用流程:
同fgetc函数作用流程一致 - 函数原型:
int getc(FILE *stream );
- 函数参数:getc函数仅1个参数
参数FILE *stream:指的是文件指针 - 结束条件:
文件的光标已经到达文件末尾或者遇到读取错误时 - 返回值:
成功:返回读取字符的ASCII 码
失败:返回EOF (EOF 是一个宏定义,宏定义的值为-1)
函数4:getchar
- 头文件:
#include<stdio.h>
- getchar函数的作用流程及功能是:
getchar函数只能从标准输入中获取一个字符。 - 函数原型:
int getchar(void );
- 函数参数:
fgetc函数无参数 - 结束条件:
文件的光标已经到达文件末尾或者遇到读取错误时 - 返回值:
成功:返回读取字符的ASCII 码
失败:返回EOF (EOF 是一个宏定义,宏定义的值为-1)
➤ 应用练习:在本地磁盘打开一个存储少量数据的文本demo.txt,利用fgetc函数把文本中的字符输出到屏幕,当文本中所有字符都输出完成后就结束程序。
(2)按行读取
函数5:fgets
- 头文件:
#include<sys/ioctl.h>;
- fgets函数的功能:从指定文件读取最多一行数据
- fgets函数的作用流程是:
从文件指针stream指向的文件中读取一行字符,并把读取的字符存储在指针s所指向的字符串内,当读取到n-1个(n-1个指的是自定义缓冲区的最大容量为n-1)字符、或者已经读取到文件末尾(EOF)、或者读取到换行符’\n’时,则函数调用停止。 - 函数原型:
char *fgets(char *s,int n,FILE *stream );
- 函数参数:fgets有三个参数
参数一:char *s指的是自定义缓冲区指针
参数二:int n指的是自定义缓冲区大小
参数三:FILE *stream指的是即将被读取数据的文件指针。 - 结束条件:
1.读取到第n-1个字符
2.读取到文件末尾
3.读取到换行符'\n' - 返回值:
成功:自定义缓冲区指针s
失败:NULL(文件stream可能已经到达末尾或者遇到错误)
函数6:gets
- 头文件:
#include<sys/ioctl.h>
- gets函数的功能:
从指定文件读取最多一行数据 - 函数作用流程是:
同fgets函数作用流程相同 - 函数原型:
char *gets(char*s);
- 函数参数:gets函数只有1个参数
参数char*s指的是自定义缓冲区指针 - 结束条件:
1.读取到第n-1个字符
2.读取到文件末尾
3.读取到换行符'\n' - 返回值:
成功:自定义缓冲区指针s
失败:NULL(文件stream可能已经到达末尾或者遇到错误) - 备注:gets()缺失从文件stdin读入数据
➤ 思考:为什么fgets函数读取到换行符\n时会结束?fgets函数中的参数n的意义是什么??
回答:用户调用fopen打开文件之后,可以把数据写入到文件中以及从文件中读取数据,但是实现读取和写入的过程中其实内核并没有直接操作文件,而是在操作指向文件的结构体指针FILE,也就是用户写入的数据和读取的数据会先存储在FILE结构体的缓冲区中,当用户调用刷新缓冲区的函数或者其他读写函数时,FILE结构体的缓冲区会被刷新,数据才会被系统写入文件。
根据IO设备的不同,可以把缓冲区分为输入缓冲区和输出缓冲区,同样,根据刷新形式的不同,可以把缓冲区分为三种:全缓冲、行缓冲、无缓冲。
全缓冲:指的是当缓冲区被填满就立即把数据冲刷到文件、或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件,一般读写文件的时候会采用
无缓冲:指的是没有缓冲区,直接输出,一般linux系统的标准出错stderr就是采用无缓冲,这样可以把错误信息直接输出。
行缓冲:指的是当缓冲区被填满(一般缓冲区为4KB,就是4096字节)或者缓冲区中遇到换行符’\n’时,或者在关闭文件、读取文件内容以及修改缓冲区类型时也会立即把数据冲刷到文件中,一般操作IO设备时会采用,比如printf函数就是采用行缓冲。
注意:对于标准输出stdout而言默认是采用行缓冲的,而对于标准出错stderr而言默认是采用无缓冲的,对于普通文件而言默认是采用全缓冲的。
(3)按块读取
函数7:fread
- 头文件:
#include<sys/ioctl.h>
- fread函数的功能:
从指定文件读取若干个数据块 - fread函数的作用流程:
从给定的文件输入流stream中读取最多nmemb个块到指针ptr指向的字符串中,每个块的大小为size字节,函数返回成功读取的块的个数,若出现错误或到达文件末尾,则可能小于nmemb。若size或nmemb为零,则fread函数返回0且不进行其他动作。 - 函数fread的原型:
size_t fread (void *ptr,size_t size,size_t nmemb,FILE *stream);
- 函数fread的参数: 函数fread有四个参数
参数一:自定义缓冲区指针
参数二:数据块的大小
参数三:数据块的个数
参数四:即将被读取数据的文件指针 - 函数fread的返回值:
成功:返回读取的数据块的个数,等于nmemb
失败:返回读取的数据块的个数,小于nmemb或等于0 - 备注:
1.当返回小于nmemb时,文件strem可能已经到达末尾,或者遇到错误
2.当读取数据发生错误时、读取部分元素没有读全的情况下,文件指示器的位置是不确定的。
➤ 思考:可以知道函数的返回值如果小于nmemb则说明可能出现读取错误或者到达文件末尾,那应该如何区分这两种情况?
回答:可以通过标准库中提供的两个函数区分,一个函数是feof(),另一个则是ferror函数。
函数8:feof
- feof函数的功能:
判断fread函数返回值小于nmemb的原因是 到达文件末尾 - 头文件:
#include<stdio.h>
- 函数feof的原型:
int feof(FILE *stream);
- 函数fread的参数:函数feof有一个参数
参数:指的是即将被读取数据的文件指针 - 函数feof的返回值:
返回0:文件指示器的位置不在末尾
返回非0:文件指示器的位置位于末尾
函数9:ferror
-
ferror函数的功能:
判断fread函数返回值小于nmemb的原因是 出现 读取错误 -
头文件:
#include<stdio.h>
-
函数ferror的原型:
ferror(FILE *stream);
-
函数ferror的参数:函数ferror有一个参数
参数:指的是即将被读取数据的文件指针 -
函数ferror的返回值:
返回0:没有出现读取错误
返回非0:出现了读取错误
三、写入文件
(1)字符写入
函数10:fputc
函数11:putc
函数12:putchar
- fputc函数的功能:
将一个字符写入一个指定的文件,补充:如果文件以追加的方式打开,则字符会追加文件的末尾 - 头文件:
#include<stdio.h>
- 函数fputc的原型:
int fputc(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);
- 函数fputc的参数:函数fputc有两个参数
参数一int c:指的是待写入的字符的ASCAII码
参数二FILE *stream:待写入的文件的指针 - 函数fputc的返回值:
成功:返回写入的字符的ASCAII码
失败:返回EOF
(2)按行写入
函数13:fputs
函数14:puts
- 函数fputs、puts的功能:
将数据写入指定的文件,补充:字符串的结束符没有被写入到文件中 - 函数fputs、puts头文件:
#include<sys/ioctl.h>
- 函数fputs、puts的原型:
int fputs(const char *s,FILE *stream);
int puts(const char *s);
- 函数fputs、puts的参数:函数fputs有两个参数,函数puts有一个参数
参数一const char *s:指的是自定义缓冲区指针
参数二FILE *stream:指的是即将被写入数据的文件指针 - 函数的返回值:
成功:返回非负整数
失败:返回EOF - 备注:puts()缺失将数据写入文件stdout
(3)按块写入
- fwrite函数的功能:
将若干块数据写入指定的文件 - fwrite头文件:
#include<sys/ioctl.h>
- 函数fwrite的原型:
size_t fwrite(const void*ptr,size_t size,size_t nmemb,FILE *stream);
- 函数fwrite的参数:函数rwrite有四个参数
参数一:自定义缓冲区指针
参数二:数据块大小
参数三:数据块的个数
参数四:即将被写入数据的文件指针 - 函数fwirte的返回值:
成功:返回写入的数据块个数,等于nmemb
失败:返回写入的数据块个数,小于nmemb或等于0
四、关闭文件
函数15:fclose
- 函数fclose的功能:
关闭指定的文件并释放其资源 - fclose头文件:
#include<stdio.h>
- 函数fclose的原型:
int fclose(FILE *stream);
- 函数fclose的参数:函数fclose有一个参数
参数:即将要关闭的文件 - 函数close的返回值:
成功:返回0
失败:返回EOF
五、文件位置
(1)设置位移
函数:16fseek
- fseek函数的功能:
设置指定文件的当前位置偏移量 - 头文件:
#include<sys/ioctl.h>
- 函数f的原seek型:
int fseek(FILE *stream,long int offset ,int whence);
- 函数fseek的参数:函数fseek有三个参数
参数一stream:需要设置位置偏移量的文件指针
参数二offset:新位置偏移量相对于基准点的偏移量(可正可负)
参数三whence(基准点):
①:SEEK_SET:文件开头处
②:SEEK_CUR:当前位置
③:SEEK_END:文件末尾处 - 函数fseek的返回值:
成功:返回0
失败:返回-1
(2)获取位移
函数17:ftell
- 函数ftell的功能:
获取指定文件的当前位置偏移量 - 函数ftell头文件:
#include<sys/ioctl.h>
- 函数ftell的原型:
long int ftell(FILE *stream);
- 函数ftell的参数:函数有一个参数
参数:需要返回当前文件位置偏移量的文件指针 - 函数的返回值:
成功:返回当前文件位置偏移量
失败:返回-1
fseek函数与ftell函数结合可用来求文件大小
六、格式访问
一般常用的关于文件IO的格式化函数有printf、fprintf、scanf、fscanf、sprintf、snprintf
系统IO接口
System Calls:系统调用(Linux内核提供的API应用程序接口),SDK:软件开发工具包,别人写好的代码案例,SDK里面有很多API
- 基本概念
Linux系统下“一切皆文件”,即Linux系统下的数据和程序都是以文件的形式存储的,故Linux内核会提供一组操作文件的函数接口,这组函数接口也被称为系统IO;
为了满足用户访问文件的需求以及提高用户程序的可移植性,标准库也提供了一组操作文件的函数接口,这组函数接口也被称为标准IO,是为了方便用户在不同的操作系统下可以调用通用的函数来实现对文件的读写访问。 - 标准IO与系统IO的区别:标准I/O可以看成是在系统I/O的基础上封装了缓冲机制
标准IO的优点是提供了缓冲区并且函数接口非常丰富,避免频繁的系统调用,提高了I/O的效率;
缺点是:没有办法针对某些类型的文件(链接文件、套接字文件)进行访问,所以一般适合访问普通文件。
系统IO的优点是:可以针对特定类型文件进行访问,所以一般适合访问数据需要实时刷新的硬件设备(LCD、触摸屏......)。
缺点是不具备输入输出缓冲区,没办法高效处理数据,原因是:因为系统调用的过程中内核要执行一系列的操作:首先内核需要捕获调用,然后再检查系统调用传递的参数的有效性,最后在用户空间和内核空间之间传输数据。
一、打开文件
函数18:open
- 函数open的功能:
一般可用于打开驱动文件,该函数在C99标准中是查不到的,可在man手册的第二章查找。 - open头文件:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
- 函数open的原型:
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,made_t mode);
- 函数open的参数:函数open有两个参数
参数一:待打开的文件 路径
参数二:
① O_RDONLY , O_WRONLY , O_RDWR 必须三选一,或者用位运算符 | 来将标志联合使用
参数三::
O_CREAT(创建标志,如果文件不存在则创建),O_EXCL(如果调用了open函数时,使用了O_CREAT和O_EXCL,并且打开的文件是存在的,则open函数调用会失败,如果文件不存在,则会创建)
注意:O_EXCL和O_CREAT一般是配套使用的,不能单独使用
补充:open函数的第三个参数mode只有在open函数的第二个参数flags使用O_CREAT或者O_TMPFILE才会使用,也就是说,打开一个已经存在的文件使用第一个版本的open函数即可,第二个版本的open函数的mode参数是指利用open函数创建新文件时给新创建的文件一个指定权限,被创建的文件的权限其实就是Linux系统下文件的权限。
注意:一般在Linux系统下可以直接使用shell命令来修改文件的权限,比如指令chmod 777 xxx.txt就是给该文档一个最高权限。
- 函数open的返回值:
成功:文件描述符:是一个小的非负整数,从一组数中选一个小的且未被使用的
失败:返回-1
二、关闭文件
函数19:close
- 函数close的功能:
关闭文件(可理解为关闭1个文件描述符) - 函数close头文件:
#include<unistd.h>
- 函数open的原型:
int close(int fd);
- 函数close的参数:函数close只有一个函数
参数int fd:文件描述符,是open的返回值。 - 函数open的返回值:
成功:返回0
失败:返回-1
三、文件读取
函数20:read
- 函数read的功能:
read函数尝试从fd对应的文件中读取最多count个字节的数据并存储到buf指向的缓冲区中 - 函数read头文件:
#include<unistd.h>
- 函数raed的原型:
ssize_t read(int fd,void *buf,size_tcount);
- 函数read的参数:函数read有三个参数
参数一int fd:文件描述符,是open的返回值。
参数二void *buf,读到指定存储空间的地址
参数三size_tcount:读取count个字节 - 函数read的返回值:返回读取的字节数量
返回0: 读取到文件末尾
返回-1:读取出错
四、文件写入
函数21:write
- 函数write的功能:
函数write()函数会把参数buf所指的内存写入count个字节到参数fd所指的文件内 - 函数write头文件:
#include<unistd.h>
- 函数write的原型:
ssize_t write(int fd,void *buf,size_tcount);
- 函数write的参数:函数write有三个参数
参数一int fd:输入文件描述符,是open的返回值。
参数二void *buf,写到指定存储空间的地址
参数三size_tcount:写count个字节 - 函数write的返回值:成功写入文件的字节数
返回0:写入文档的字节数
返回-1:写入出错
四、位置位移
函数22:lseek
- 函数lseek的功能:
设置文件的位置指示器的位移 - 函数lseek头文件:
#include<unistd.h>
#include<sys/types.h>
- 函数lseek的原型:
off_t lseek(int fd,off_t offset,int whence);
- 函数lseek的参数:函数write有三个参数
参数一int fd:输入文件描述符,是open的返回值。
参数二off_t offset:偏移量
参数三int whence:
文件开头:SEEK_SET
当前位置:SEEK_CUR
文件末尾:SEEK_END - 函数lseek的返回值:
成功:返回文件位置指示器相较于开头的偏移量,以字节为单位
失败:返回-1
热门相关:病毒抗体 孤独的爸爸-二十几岁的性伴侣 白蛇传 偷偷做的条件情事 致命金刚拳