32位arm架构磁盘超过16TB分区

背景:

        最近在解决一个22TB大小的Raid5的阵列分区信息消失的问题的时候,按照既定的策略对写入分区信息的阵列位置进行清零操作 --- 即在①阵列节点头部从LAB0的位置往后2MB进行写0操作,同时,②对阵列尾部最后2MB进行写0操作。这个两个位置是写入GPT分区的位置,清零操作的原因是:gpt信息的大小不够一个Raid条带,当其中某些磁盘存在脏数据时,掉盘之后,Raid5无法通过校验盘来恢复正确的数据。

操作:

        open一下阵列节点md0,ope属性是RW,然后用一个longlong类型来记录偏移,便于lseek64到阵列末尾,写入2MB大小的0数据。为了确认数据确实写入到对应的位置,放开了blockdump和内核打印信息。

echo 1 > /proc/sys/vm/block_dump  /*开始记录IO*/
echo 0 > /proc/sys/vm/block_dump  /*关闭blockdump*/

echo 9 > /proc/sys/kernel/printk  /*放开最高等级内核打印*/

        放开信息之后,开始执行清零操作,通过打印信息发现头部写入的数据和偏移是正确的,而尾部写入的数据偏移总是在6TB的位置,加打印确认偏移大小是没有问题的。lseek64返回的信息也是正确的,仔细想了一下应该是32位系统seek寻址最大是16TB的问题导致的,16TB+6TB正好是阵列大小。可是,为什么32位系统只能寻址到16TB呢?难道无法支持16TB以上的写入?显然,这个问题是常见问题,不可能是我第一个发现的,一定是哪里没有了解清楚。

思考:

        ①为什么要对大于16TB的阵列进行分区?

        ②为什么明明使用了lseek64,64位的寻址还是不能超过16TB?

        ③有什么方法可以解决这个问题?

解决:

        首先去了解了一下linux 系统下一次IO的过程,

        用户态只是调用接口,没有实质性的对硬盘操作。

       内核态: 

        文件系统层:VFS层:我们知道Linux分为用户态和内核态,用户态请求硬件资源需要调用System Call通过内核态去实现。用户的这些文件相关操作都有对应的System Call函数接口,接口调用 VFS对应的函数。 不同的文件系统实现了VFS的这些函数,通过指针注册到VFS里面。所以,用户的操作通过VFS转到各种文件系统。文件系统把文件读写命令转化为对磁盘LBA的操作,起了一个翻译和磁盘管理的作用。 

缓存层:文件系统底下有缓存,Page Cache,加速性能。对磁盘LBA的读写数据缓存到这里。

块设备层:块设备接口Block Device是用来访问磁盘LBA的层级,主要是对读写命令组合,后插入到IO命令队列,磁盘的驱动从队列读命令执行。

磁盘驱动层:磁盘的驱动程序把对LBA的读写命令转化为各自的协议,比如变成ATA命令,SCSI命令,或者是自己硬件可以识别的自定义命令,发送给磁盘控制器。

        分析可知,最有可能影响到写磁盘的位置应该是文件系统层。查阅资料可知(可以搜索linux IO栈),文件系统层中包含了vfs和块设备文件系统(ext4,ext2等),在vfs层中的page cache 是一段用于缓存磁盘文件的内存(关于page cache 介绍 可搜索 linux page cache)。其内存管理结构是 struct page 结构:

struct page {
    unsigned long flags;
    atomic_t count;  
    atomic_t _mapcount; 
    struct list_head lru;
    struct address_space *mapping;
    unsigned long index;         
    ...  
} 
  • flags表示page frame的状态或者属性,包括和内存回收相关的PG_active, PG_dirty, PG_writeback, PG_reserved, PG_locked, PG_highmem等。其实flags是身兼多职的,它还有其他用途,这将在下文中介绍到。
  • count表示引用计数。当count值为0时,该page frame可被free掉;如果不为0,说明该page正在被某个进程或者内核使用,调用page_count()可获得count值。
  • _mapcount表示该page frame被映射的个数,也就是多少个page table entry中含有这个page frame的PFN
  • lru是"least recently used"的缩写,根据page frame的活跃程度(使用频率),一个可回收的page frame要么挂在active_list双向链表上,要么挂在inactive_list双向链表上,以作为页面回收的选择依据,lru中包含的就是指向所在链表中前后节点的指针(参考这篇文章)。
  • 如果一个page是属于某个文件的(也就是在page cache中),则mapping指向文件inode对应的address_space(这个结构体虽然叫address_space,但并不是进程地址空间里的那个address space),index表示该page在文件内的offset(以page size为单位)

 注意这个index 的类型是unsigned long 在32位系统中是2^31大小,在64位中是2^63大小。因为每个page frame都需要一个struct page来描述,一个page frame占4KB。所以在内存中可以管理的文件的最大偏移是 index * 4KB,所以32位系统中采用page cache的缓存方式的IO的偏移范围是

2^31 * 4KB = 16TB 。既然找到了最大偏移限制是由于page cache在作怪,那么有没有办法可以无缓冲读写文件呢?

        这里其实就是常说的BIO和DIO的不同的地方。pen函数的flag参数中有O_DIRECT参数可以使用DIO写和读来操作硬盘。同时这个也是验证上述结论是否正确的一个方法。

        此时也能解释为什么lseek64偏移超过16TB之后为什么会定位到6TB的位置了 -- 由于范围超出了16TB,导致数据高位被截断,从而偏移回环,到了第6TB的位置。

总结:

        有时候简单的问题中蕴含着比较深层次的道理,以前的时候只知道32位系统支持的最大容量是16TB,原因是因为32位数据类型导致的,但是没有去验证这个结论的由来。

        总结如下:

        ①32位系统内存寻址范围是4GB,但是由于page 页大小4KB缓存的原因,所以可以支持的虚拟磁盘容量远超4GB。

        ②硬盘操作的BIO和DIO写都有自己的优缺点,要在合适的时候使用。

        ③代码实现时不能只依靠函数返回值来判断是否执行成功,要依赖于执行的结果。而结果可以通过多个维度去验证。

        ④设计和实现时注意32位和64位系统中数据类型的区别

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值