今天突然想学习Linux下的文件结构,就想利用C语言实现一个读取系统分区表及分区中各个Node的程序。此笔记是对实现这一过程的记录。

好久都没有写过c语言了,此时需要使用c语言实现想要的功能,表示压力山大,利用此篇文章记录一下自己实现的过程以其中碰到的一些比较有意思的点,算是顺便学习C语言了吧。本文是一篇备忘录形式的记录型文章,可能会出现逻辑混乱等问题。

C语言中的数据类型先复习下C语言的基本知识

基本类型:数值类型:

整型:short(短整型)、int(整型)、long(长整型)

浮点型:float(单精度型)、double(双精度型)

字符类型

字符: char

构造类型

数组: int p[]

结构体: struct

共用体: union

枚举类型enum

C99标准定义的数据类型

uint8_t 1Byte

uint16_t 2Byte

uint32_t 4Byte

uint64_t 8Byte在不同位数的操作系统下,每个基本数据类型所使用的字节数不一致。

文件操作函数想要读取磁盘中的数据,可以直接利用open\fopen函数打开对应的磁盘即可。利用该函数读取数据的方法与平常读取文件的方法一致。linux下面对于文件的操作有两种API:[1].(open,read,write,close),[2].(fopen,fread,fwrite,fclose)[2]是标准的C库函数,[1]是POSIX定义的,属于UNIX的系统调用。

fopen123FILE* fopen(const char* path,const char* mode)fread/fwritefread(buffer,size,count,fp)/fwrite(buffer,size,count,fp)

buffer:一个指针,读入数据的存放地址

size:要读写的字节数

count:要读写多少size字节的数据项

fp:文件型指针(要读取的文件)

返回值,如果读到了文件末尾,则返回值小于count

feof1int feof(FILE *fp)

检测当前文件流上的文件结束标识,判断是否读到了文件末尾。检测到了返回1,否则返回0

1fgetc/fputc

从指定的文件中读取或写入一个字符,成功返回读取的数值,失败返回-1

1fgetc(fp)/fputc(ch,fp)

fgets/fputs12char* fgets(char* str,int num, FILE* fp)fputs(char* s,FILE* fp)

ftell1long ftell(FILE* fp)

得到流式文件的当前读写位置,返回的是当前读写位置偏离文件头部的字节数。

fseek1int fseek(FILE* fp,long offset,int origin)

将fp文件读写位置指针移动到指定位置。origin值的是起始点,有三个常量:SEEK_SET 0 文件开头;SEEK_CUR 1 文件当前位置;SEEK_END 2 文件末尾。

fclose1fclose(FILE* fp)

输出函数有printf 和fprintf两种方法,都是C中用于输出数据的。但是fprintf 可以根据指定的format(格式)发送数据到由stream指定的文件。printf("sdfasdf")

fprintf(stderr,""%s","sdfa");

`

%d:十进制有符号整数

%u:十进制无符号整数

%f:浮点数

%s:字符串

%c:单个字符

%p:指针的值

%e:指数形式的浮点数

%x:无符号以十六进制表示的整数

%0:无符号以八进制表示整数

%llx:十六进制格式化输出uint64_t类型的数据

输出时将char转换为int输出时需要&FF的原因,C语言中,(char)整型变量 = 字符型变量,(int)字符型变量=整型变量。直接输出char可能会因为符号位的影响输出负数,而通过&FF,进行了整型提升操作,从而减小符号位的影响。

启动程序BIOS、UEFI都是固化到主板上的。MBR(只支持不超过2T的硬盘)、GPT是存在硬盘当中的,存储硬盘分区信息的结构。Windows不支持BIOS+GPT、UEFI+MBR的形式启动,windows的启动方式为BIOS+MBR或UEFI+GPT(Win8)。而Linux系统支持各种方式的启动。UEFI固化到主板上不是准确的说法,因为UEFI是一种标准,其具体的实现需要依靠各个厂商的自家实现Linux启动时,BIOS自检完成,然后加载MBR+之后将GRUB/Lilo复制到物理内存的0x7c00,GRUB就是存储在MBR中的一段程序。传统的MBR采用CHS的磁盘寻址方式,在其内部使用4个字节描述磁盘或分区的大小,最多支持4个分区,最大支持2TB的硬盘。GPT分区则允许硬盘有128个主分区,支持2TB以上容量的硬盘。

MBR分区结构:MBR占521Byte,分为主引导程序即启动代码及数据(446Byte)、硬盘分区(64Byte,每分区16Byte)、结束标志(0x55AA)三个部分。|———–446———-|—-64—-|—2—|| 启动代码及数据 | 硬盘分区 | 标志 |

16字节分区: 0x01BE:1Byte,值0x80,引导标志,指明该分区是否是活动分区,0x00为非活动分区。 0x01BF:1Byte,值0x01,开始磁头 0x01C0:6Bit,值0x01,起始扇区

GPT分区结构:|———|——-|——–|———-|————|———–|| 保护MBR | GPT头 | 分区表 | 分区区域 | 分区表备份 | GPT头备份 |

保护MBR:位于0号扇区,其中包含磁盘签名、MBR分区表、结束标志。没有结束标志,分区表中只有一个分区表项,类型是0xEE,GPT不使用该扇区记录分区表。整数以小端法表示

GPT头结构:00 ~ 07: GPT头签名: 8Byte,1号扇区,GPT头定义分区表的起始位置、结束位置、分区表项个数,大小等。”45 46 49 20 50 41 52 54”(EFI PART)08 ~ 0B: 版本号: 4Byte,0C ~ 0F: GPT头的大小:4Byte,通常为”5C 00 00 00”(0x5C),即92字节。10 ~ 13: GPT头CRC校验和:4Byte,计算时将这个字段本身看作0值。14 ~ 17: 保留:4Byte,必须为”00 00 00 00“18 ~ 1F: EFI信息区:8Byte,EFI信息区(GPT头)的起始扇区号,通常为”01 00 00 00 00 00 00 00“,即LBA120 ~ 27: EFI信息区:8Byte, EFI信息区(GPT头)的备份位置的扇区号,也就是EFI区域结束扇区号。通常是整个磁盘最末一个扇区。28 ~ 2F: GPT分区区域的起始扇区号:8Byte,通常为”22 00 00 00 00 00 00 00”即LBA3430 ~ 37: GPT分区区域的结束扇区号:8Byte,通常是倒数第34扇区。38 ~ 47: 磁盘GUID:16Byte(全球唯一的标识符)48 ~ 4F: 分区表起始扇区号:8Byte,通常为”02 00 00 00 00 00 00 00“,即LBA250 ~ 53: 分区表总项数:4Byte,通常限定为”80 00 00 00”,也就是128个54 ~ 57: 每个分区表项占用字节数: 4Byte, 通常限定为”80 00 00 00“也就是128Byte57 ~ 5B: CRC校验和:4Byte保5C ~: 保留:5c~ 全零填充,一般至扇区结束

4546 4920 5041 5254 8 GPT头签名EFI PART0000 0100 4 版本号5c00 0000 4 GPT头的大小fdd8 bada 4 GPT头校验和0000 0000 4 保留0100 0000 0000 0000 8 EFI信息区的起始扇区2f60 383a 0000 0000 8 EFI信息区备份位置的扇区号,通常是整个磁盘最后一个扇区2200 0000 0000 0000 8 GPT分区区域的起始扇区号0e60 383a 0000 0000 8 GPT分区区域的结束扇区号,通常为倒数第34扇区2b00 da13 16 磁盘GUID0200 0000 0000 0000 8 分区表起始扇区8000 0000 4 分区表总项数8000 0000 4 每个分区表占用的字节数3616 c901 4 CRC校验和

分区表GPT头(第二扇区)后面紧跟着分区表,分区表的结构如下:00 ~ 0F: 16Byte :用GUID标识的分区类型10 ~ 1F: 16Byte :用GUID标识的分区唯一标示符20 ~ 27: 8Byte :该分区的起始扇区,用LBA值表示28 ~ 2F: 8Byte :该分区的结束扇区(包含),用LBA值表示,通常是奇数30 ~ 37: 8Byte :该分区的属性标志38 ~ 7F: * :UTF-16LE编码的人类可读的分区名称,最大32个字符。

为什么第一个分区从2048扇区开始由于EFI的兴起,要流出1MB的空间给EFI的启动代码。fdisk分区时会自动留出

MBR与分区系统分区采用不同的文件系统格式化之后,在文件系统之中也会有专门的区域来存放MBR等信息,这样才能实现多重引导(在一块硬盘中安装多个操作系统)。

代码地址c_etude

参考文章

GPT分区结构

C语言实现GPT头和分区表的读取

GPT分区详解

为什么Linux的fdisk分区时第一块磁盘分区的First Sector是2048?