操作系统如何处理非块设备扇区大小倍数的I/O写入?
非扇区对齐写入的处理机制(Linux内核视角)
你的理解完全正确:块设备的最小原子操作单位是扇区(比如你提到的512字节),操作系统无法直接向块设备写入小于扇区大小的数据。当执行非扇区对齐的无缓冲写入时,内核会执行**读-改-写(Read-Modify-Write)**流程,而非填充0字节覆盖整个扇区。
核心处理逻辑
- 读取目标扇区:内核先将包含待写入数据的整个扇区从磁盘读入内核内存缓冲区。
- 修改缓冲区数据:将用户空间的300字节数据拷贝到缓冲区的对应偏移位置,保留扇区内未被修改的原有数据。
- 写入完整扇区:将修改后的整个512字节缓冲区写回磁盘的对应扇区。
这种流程确实会带来额外开销:多一次磁盘读操作,且需要占用内核内存作为临时缓冲,因此非对齐写入的性能远低于扇区对齐的写入。
Linux内核中负责该流程的组件
这个逻辑主要由文件系统层和**块设备层(Block Layer)**协同处理:
- 文件系统层:以ext4为例,当处理
O_DIRECT(无缓冲)的非对齐写入时,文件系统会触发读-改-写流程,负责读取目标扇区、合并用户数据到缓冲区。 - 块设备层:负责将文件系统的I/O请求转换成磁盘能识别的扇区大小操作,通过
bio结构体管理I/O请求,最终提交给磁盘驱动执行。
Linux内核代码示例
以ext4文件系统的无缓冲I/O处理函数ext4_direct_IO(位于fs/ext4/file.c)为例,简化后的核心逻辑如下:
static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter) { struct inode *inode = file_inode(iocb->ki_filp); loff_t pos = iocb->ki_pos; size_t count = iov_iter_count(iter); unsigned int blksize = inode->i_blkbits; struct page *page; ssize_t ret = 0; // 检查写入是否扇区对齐 if (!IS_ALIGNED(pos, blksize) || !IS_ALIGNED(count, blksize)) { // 计算目标页的索引 pgoff_t index = pos >> PAGE_SHIFT; // 读取对应扇区所在的页到内核缓存 page = read_cache_page(inode->i_mapping, index, (filler_t *)ext4_readpage, NULL); if (IS_ERR(page)) { ret = PTR_ERR(page); goto out; } // 将用户数据拷贝到页的对应偏移位置 ret = copy_from_iter(page_address(page) + (pos & (PAGE_SIZE - 1)), count, iter); if (ret < 0) { put_page(page); goto out; } // 标记页为脏页,触发后续写入 set_page_dirty(page); // 构建bio结构体,提交写请求到块层 struct bio *bio = bio_alloc(GFP_KERNEL, 1); bio_set_dev(bio, inode->i_sb->s_bdev); bio->bi_iter.bi_sector = pos >> 9; // 转换为扇区地址(512B扇区) bio_add_page(bio, page, PAGE_SIZE, 0); bio_set_op_attrs(bio, REQ_OP_WRITE, 0); submit_bio(bio); put_page(page); } // ... 对齐写入的处理逻辑 out: return ret; }
块层通过submit_bio函数将构建好的bio请求提交给磁盘驱动,最终驱动会执行整个扇区的写入操作,保证磁盘数据的一致性。
内容的提问来源于stack exchange,提问作者Marco
相关产品推荐
相关产品推荐

