Android 技术分享 (I/O知识分享)

序 

在去年公司内部组织的技术分享 ,有幸参与其中 ,准备的话题是 I/O相关的一些东西 。

 

NO.1

在工作中发现很多小伙伴对 I/O 的认识比较模糊,认为 I/O 就是调用 read() 和 write() 这样的操作。其实对于应用层开发来说,我们在跟I/O打交道中最多的也就是这些由虚拟文件系统提供的API 

NO.2

看这张简图,整个文件 I/O 操作有应用程序、文件系统、磁盘共同完成 。流程用一句话概括就是 ,首先应用程序将 I/O 命令发送给文件系统 ,然后文件系统会在合适的时机把 I/O 操作发给磁盘 。

就好比 CPU 、内存、磁盘三个小伙伴一起完成接力跑 ,最终跑完的时间很大程序上取决于最慢的小伙伴我们知道 ,CPU和内存相比磁盘是高速设备 ,整儿接力赛最终跑完的时间很大程度上取决于最慢的小伙伴 。所以很多时候文件系统性能比磁盘的性能更加重要 ,为了降低磁盘对应用程序的影响 ,文件系统做了各种各样的优化 。

不足之处在于可靠性方面出现过一些问题 。不过 ,个人感觉随着Google和华为的投入和规模化使用 ,F2FS应该是未来主流的文件系统 。

NO.3

 

 内核空间和用户空间 。操作系统的核心是内核 ,独立于普通的应用程序 ,可以访问受保护的内存空间 ,也有访问底层硬件设备的所有权限 。为了保证用户进程不能直接操作内核 ,保证内核的安全 ,操作系统将虚拟系统划分为两部分 ,一部分为内核空间 ,一部分为用户空间 。有了内核空间和用户空间 ,整个Linux内部结构可以划分为三部分 ,从最底层到最上层依次是硬件 —— 内核空间 —— 用户空间 

虚拟文件系统:主要是用于实现屏蔽具体的文件系统 ,为应用程序提供一个接口 。有什么有点?

这样可以保证就算厂商将文件系统从ext4文件系统改为F2FS,应用程序也不用做任何修改。这一点跟工厂模式有一点类似。

文件系统:文件元数据如何组织、目录和索引结构如何设计、怎么分配和清理数据 。这些都是设计一个文件系统必须要考虑的事情 。每一个文件系统都有适合自己的应用场景 ,不能说 F2FS 就一定比 ext4 好 。F2FS 在连续读取大文件上并没有优势 ,而会占用更大的空间 ,只是对于一般程序来说 ,随机 I/O 更加频繁 ,特备是在启动的场景 。

磁盘:在我们小时候听说的CD或者电脑使用的机械硬盘,还有现在比较流行的SSD固态硬盘。

 

PS :文件为什么会损坏 ?

先说两个客观数据 ,微信聊天记录使用的 SQLite 数据库大概有几万分之一的损坏率 ,系统的 SP 在跨进程读写也会有万分之一的损坏率 。在回答文件为什么会损坏之前先明确一下文件损坏的定义一个文件的格式或者内容如果没有按照应用程序写入时的结果都属于文件损坏 。它不只是文件格式的错误 ,文件内容丢失可能才是最出现的 。SP在跨进程读写的时候就经常年出现数据丢失再来考虑文件为什么会损坏 ?我们从应用程序和文件系统和磁盘三个角色来审视这个问题。

(1)应用程序: 大部分的I/O操作都不是原子操作,文件的跨进程或者多线程写入、使用一个已经关闭的文件描述符fd(系统对于所有打开的文件都是使用文件描述符fd来引用的,当打开一个现有文件或创建一个新文件时,系统返回给进程一个文件描述符fd)来操作文件,他们都有可能导致数据被覆盖或者被删除。其实,大部分文件损坏都是应用程序代码设计考虑不当造成的,问不是文件系统或者磁盘的问题。

(2)文件系统: 虽说内核崩溃或者系统突然断电都有可能导致文件损坏,不过文件系统也是做了很多的保护措施,例如增加异常和恢复机制。在文件系统这一层,更多是因为断电而导致的写入丢失。为了提升I/O性能,文件系统把数据写入到Page Cache中,然后等待何时的时机才会真正的写入到磁盘。我们也可以通过fsync,msync这些接口强制写入到磁盘中。

(3)磁盘: 手机上使用的闪存是电子式的存储设备,闪存的寿命会导致错误,闪存写过的地址必须擦除才能再次写入,而每个块擦除又有次数限制。

PS:I/O有时候为什么会很慢?

IOS 应该会好一点,Android 手机刚买的时候“如丝般顺滑” ,但是使用一年到三年吧 ,手机会变的卡顿无比 。尤其是在低端手机上,大量的卡顿都跟 I/O 有关。

直接说原因吧,第一是内存不足,当手机内存不足的时候,系统会回收 page cache 的内存,大部分的写操作会直接落盘,导致性能底下。第二是写入放大,上面我说闪存重复写入需要进行擦除操作,但是这个擦除的基本单元是block,一个page页的写入操作会将会引起整个块数据的迁移,典型的写入放大现象。

 

NO.4

 

熟悉I/O的有哪些方式 ,如果我们非常清楚文件系统和磁盘的工作机制 ,就能少走一些弯路 ,减少应用程序I/O引发的问题 。在上一页我们提到过 Page Cache 机制 ,他在很大程度上提升了磁盘 I/O 的性能 ,但是也可能导致数据丢失 。所以我们究竟有哪些I/O方式可以选择 。

      标准I/O:我们可以看到标准I/O可以很大程度减少真正读写磁盘的次数,从而提升性能。那系统究竟会在什么时机真正的把页缓存的数据写入到磁盘呢?首先Page Cache中的被修改的内存称为“脏页”,内核通过flush线程定期刷新到磁盘中(具体的条件,flush没隔5秒执行一次,内存中驻留30秒以上的数据有flush在下一次执行时写入磁盘,还有系统能拥有最大的脏页缓存的总数等等)。

      直接I/O:对于读操作来说,读操作会造成磁盘的同步度,导致进程需要很长时间才能执行完;对于写操作来说,也会同步执行导致等待。在使用直接I/O的时候一定要有一个很清楚的认识,只有在确定标准I/O的开销非常巨大的情况下才使用直接I/O。

       mmap:Android在系统启动加载dex的时候,不会把整个文件一次性全部读取到内存中,而是采用mmap的方式,微信的高性能日志  Xlog也是采用mmap来保证性能和可靠性。mmap究竟是何方神圣,她真的可以做到不丢失数据、性能还非常好?其实他是通过把文件映射到进程的地址空间,而网上很多文章在说他是绕开页缓存机制,其实这是不正确的,我们最终映射的物理内存依然在页缓存中。简单来说就是标准I/O需要有时候需要复制两次数据,如果将文件内容直接映射到用户空间的内存中,这就是mmap的想法。mmap的好处第一:减少系统调用,后续的所有调用像操作内存一样,为不会出现大量的read/write等系统调用。第二:减少数据拷贝,标准I/O需要两次,mmap只需要一次。  缺点——第一虚拟内存增大,第二磁盘延迟。

 

 

PS :完成 PPT 可以关注公众号后台回复关键字 (PPT)

 

Android 技术分享 (I/O知识分享)_第1张图片

 

你可能感兴趣的:(心得随便记录)