Linux系统中实现GPT分区读取程序

今天突然想学习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的系统调用。

fopen

1
2
3
FILE* fopen(const char* path,const char* mode)
fread/fwrite
fread(buffer,size,count,fp)/fwrite(buffer,size,count,fp)
  • buffer:一个指针,读入数据的存放地址
  • size:要读写的字节数
  • count:要读写多少size字节的数据项
  • fp:文件型指针(要读取的文件)
  • 返回值,如果读到了文件末尾,则返回值小于count

feof

1
int feof(FILE *fp)

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

1
fgetc/fputc

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

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

fgets/fputs

1
2
char* fgets(char* str,int num, FILE* fp)
fputs(char* s,FILE* fp)

ftell

1
long ftell(FILE* fp)

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

fseek

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

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

fclose

1
fclose(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“,即LBA1
20 ~ 27: EFI信息区:8Byte, EFI信息区(GPT头)的备份位置的扇区号,也就是EFI区域结束扇区号。通常是整个磁盘最末一个扇区。
28 ~ 2F: GPT分区区域的起始扇区号:8Byte,通常为”22 00 00 00 00 00 00 00”即LBA34
30 ~ 37: GPT分区区域的结束扇区号:8Byte,通常是倒数第34扇区。
38 ~ 47: 磁盘GUID:16Byte(全球唯一的标识符)
48 ~ 4F: 分区表起始扇区号:8Byte,通常为”02 00 00 00 00 00 00 00“,即LBA2
50 ~ 53: 分区表总项数:4Byte,通常限定为”80 00 00 00”,也就是128个
54 ~ 57: 每个分区表项占用字节数: 4Byte, 通常限定为”80 00 00 00“也就是128Byte
57 ~ 5B: CRC校验和:4Byte
保5C ~: 保留:5c~ 全零填充,一般至扇区结束

4546 4920 5041 5254 8 GPT头签名EFI PART
0000 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 磁盘GUID
0200 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

参考文章

  1. GPT分区结构
  2. C语言实现GPT头和分区表的读取
  3. GPT分区详解
  4. 为什么Linux的fdisk分区时第一块磁盘分区的First Sector是2048?