最近在开发用户态文件系统,有一些知识要总结一下:
1、 O_DIRECT 标志是和文件系统相关的。 比如2.6.14 上FAT 文件系统就不支持它;
读写裸设备,O_DIRECT 标志是可以支持的,但是读用O_DIRECT 标志会有问题,读的数据不正确,怀疑是2.6.14 cpu cache的问题。
2.6.24的内核就ok.
2、 O_DIRECT 标志,对于sata 或者usb 底层有dma搬移的驱动来说,可以少拷贝一次。
有些空间,比如bootargs中,mem=32M 之外,内核管理不到的内存,这种地址是不可以用O_DIRECT 的;
可以在用户态拷贝一次,然后再通过memealign来分配。 用户态的拷贝,消耗时间要远远小于用户态到内核态的拷贝。
3、 用户态的文件系统都是操作裸设备的,最好是一个设备打开多个fd。 这样,可以增加并发性。
如果只有1个fd,上层多个线程调用的话,那只能是完全互斥,不能并行,否则文件指针就飞了;
4、 对写顺序要求严格的文件系统, 写元数据最好用O_SYNC 标志,写入之后才能返回,可以保证顺序。
这需要专门的fd对应,同时,为了不影响性能,最好定时刷新或者设置触发条件。
5、 写入顺序还要考虑调度算法的影响。只要不是noop,都有可能改变写入顺序。
6、 write采用了同步方式,阻塞情况时间不一样的。平时写一次时间大概也就是ms级别。如果内存的buffer被占满了,write会阻塞好长
间,比如500ms。 当然,视内存大小而定。 内存越大, 阻塞时间愈长。
通过调节: /proc/sys/vm/dirty_ratio 来改变刷新的时机,从而使写时间更平滑。
比如,当达到内存5%时,当次写会触发刷新。
7、 通过iozone来测试文件系统的性能;
8、 同步睡眠方式的write(无o_sync,o_direct标志),写粒度大小对性能影响不大。一次4k和一次128K 速度差不多。
因为底层是dma,上层cpu只管搬就是了。只不过粒度小,cpu占有率就上去了,全耗费在上下文的频繁切换上了。
由于没跑业务,所以cpu不是瓶颈。
如果用了o_direct 或者o_sync ,则粒度的影响非常大。 在一定区间内,比如读写大小在64k以下。 粒度差一倍,性能也会差
一倍,道理是非常明显的,这里就不多说了。
9、 文件系统的基础设施有: dentry、inode, 哈希表、链表等等。
哈希表通过(devid, blockno)关键值计算,获取哈希表的位置,来看内容有没有缓存。 哈希函数可以用抑或方式来计算。
通过计算,冲突的可能性还比较小;
对于inode/dentry 之类的频繁申请释放的东东,当然要缓存了。需要有free/busy/dirty 等链表来存放。
链表采用linux 内核的方式,比如:list_head_init,list_head_add/del 方式管理, 会非常方便。 我们自己写的链表太土了。
当然内核的链表采用了gcc的扩展属性,比如typeof等等,换做别的编译器,很可能编不过。
10、 开发文件系统,可以在pc机上模拟。 建立一个超大文件,当作裸设备。 可以采用gdb、valgrind来调试,检测内存泄漏,内存越界等等。
还可以通过重载malloc、free来计数。