0

0

聊聊zfs中的write

絕刀狂花

絕刀狂花

发布时间:2025-07-17 10:00:27

|

881人浏览过

|

来源于php中文网

原创

以下是关于zfs和zpool的伪原创内容,保持了原文的结构和大意,同时进行了改写:

// 创建一个zpool
$ modprobe zfs
$ zpool create -f -m /sample sample -o ashift=12 /dev/sdc
$ zfs create sample/fs1 \
  -o mountpoint=/sample/fs1 \
  -o atime=off \
  -o canmount=on \
  -o compression=lz4 \
  -o quota=100G \
  -o recordsize=8k \
  -o logbias=throughput

// 手动卸载和挂载 $ umount /sample/fs1 $ zfs mount sample/fs1

zpool和zfs参数获取:

// 获取zfs pool的默认参数
$ zpool get all

// 获取zfs pool挂载文件系统的参数 $ zfs get all

zfs和内核之间的桥梁super_operations:

const struct super_operations zpl_super_operations = {
.alloc_inode = zpl_inode_alloc,
.destroy_inode = zpl_inode_destroy,
.dirty_inode = zpl_dirty_inode,
.write_inode = NULL,
.evict_inode = zpl_evict_inode,
.put_super = zpl_put_super,
.sync_fs = zpl_sync_fs,
.statfs = zpl_statfs,
.remount_fs = zpl_remount_fs,
.show_devname = zpl_show_devname,
.show_options = zpl_show_options,
.show_stats = NULL,
};

struct file_system_type zpl_fs_type = { .owner = THIS_MODULE, .name = ZFS_DRIVER, .mount = zpl_mount, .kill_sb = zpl_kill_sb, };

inode_operations:

extern const struct inode_operations zpl_inode_operations;
extern const struct inode_operations zpl_dir_inode_operations;
extern const struct inode_operations zpl_symlink_inode_operations;
extern const struct inode_operations zpl_special_inode_operations;
extern dentry_operations_t zpl_dentry_operations;
extern const struct address_space_operations zpl_address_space_operations;
extern const struct file_operations zpl_file_operations;
extern const struct file_operations zpl_dir_file_operations;

/ zpl_super.c / extern void zpl_prune_sb(int64_t nr_to_scan, void *arg); extern const struct super_operations zpl_super_operations; extern const struct export_operations zpl_export_operations; extern struct file_system_type zpl_fs_type;

const struct inode_operations zpl_inode_operations = { .setattr = zpl_setattr, .getattr = zpl_getattr,

ifdef HAVE_GENERIC_SETXATTR

.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,

endif

.listxattr = zpl_xattr_list,

if defined(CONFIG_FS_POSIX_ACL)

if defined(HAVE_SET_ACL)

.set_acl = zpl_set_acl,

endif / HAVE_SET_ACL /

.get_acl = zpl_get_acl,

endif / CONFIG_FS_POSIX_ACL /

};

const struct inode_operations zpl_dir_inode_operations = { .create = zpl_create, .lookup = zpl_lookup, .link = zpl_link, .unlink = zpl_unlink, .symlink = zpl_symlink, .mkdir = zpl_mkdir, .rmdir = zpl_rmdir, .mknod = zpl_mknod,

if defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS)

.rename = zpl_rename2,

else

.rename = zpl_rename,

endif

ifdef HAVE_TMPFILE

.tmpfile = zpl_tmpfile,

endif

.setattr = zpl_setattr,
.getattr = zpl_getattr,

ifdef HAVE_GENERIC_SETXATTR

.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,

endif

.listxattr = zpl_xattr_list,

if defined(CONFIG_FS_POSIX_ACL)

if defined(HAVE_SET_ACL)

.set_acl = zpl_set_acl,

endif / HAVE_SET_ACL /

.get_acl = zpl_get_acl,

endif / CONFIG_FS_POSIX_ACL /

};

const struct inode_operations zpl_symlink_inode_operations = {

ifdef HAVE_GENERIC_READLINK

.readlink = generic_readlink,

endif

if defined(HAVE_GET_LINK_DELAYED) || defined(HAVE_GET_LINK_COOKIE)

.get_link = zpl_get_link,

elif defined(HAVE_FOLLOW_LINK_COOKIE) || defined(HAVE_FOLLOW_LINK_NAMEIDATA)

.follow_link = zpl_follow_link,

endif

if defined(HAVE_PUT_LINK_COOKIE) || defined(HAVE_PUT_LINK_NAMEIDATA)

.put_link = zpl_put_link,

endif

.setattr = zpl_setattr,
.getattr = zpl_getattr,

ifdef HAVE_GENERIC_SETXATTR

.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,

endif

.listxattr = zpl_xattr_list,

};

const struct inode_operations zpl_special_inode_operations = { .setattr = zpl_setattr, .getattr = zpl_getattr,

ifdef HAVE_GENERIC_SETXATTR

.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,

endif

.listxattr = zpl_xattr_list,

if defined(CONFIG_FS_POSIX_ACL)

if defined(HAVE_SET_ACL)

.set_acl = zpl_set_acl,

endif / HAVE_SET_ACL /

.get_acl = zpl_get_acl,

endif / CONFIG_FS_POSIX_ACL /

};

dentry_operations_t zpl_dentry_operations = { .d_revalidate = zpl_revalidate, };

file_operations:

extern const struct inode_operations zpl_inode_operations;
extern const struct inode_operations zpl_dir_inode_operations;
extern const struct inode_operations zpl_symlink_inode_operations;
extern const struct inode_operations zpl_special_inode_operations;
extern dentry_operations_t zpl_dentry_operations;
extern const struct address_space_operations zpl_address_space_operations;
extern const struct file_operations zpl_file_operations;
extern const struct file_operations zpl_dir_file_operations;

const struct address_space_operations zpl_address_space_operations = { .readpages = zpl_readpages, .readpage = zpl_readpage, .writepage = zpl_writepage, .writepages = zpl_writepages, .direct_IO = zpl_direct_IO, };

const struct file_operations zpl_file_operations = { .open = zpl_open, .release = zpl_release, .llseek = zpl_llseek,

ifdef HAVE_VFS_RW_ITERATE

ifdef HAVE_NEW_SYNC_READ

.read = new_sync_read,
.write = new_sync_write,

endif

.read_iter = zpl_iter_read,
.write_iter = zpl_iter_write,

ifdef HAVE_VFS_IOV_ITER

.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,

endif

else

.read = do_sync_read,
.write = do_sync_write,
.aio_read = zpl_aio_read,
.aio_write = zpl_aio_write,

endif

.mmap = zpl_mmap,
.fsync = zpl_fsync,

ifdef HAVE_FILE_AIO_FSYNC

.aio_fsync = zpl_aio_fsync,

endif

.fallocate = zpl_fallocate,
.unlocked_ioctl = zpl_ioctl,

ifdef CONFIG_COMPAT

.compat_ioctl = zpl_compat_ioctl,

endif

};

const struct file_operations zpl_dir_file_operations = { .llseek = generic_file_llseek, .read = generic_read_dir,

if defined(HAVE_VFS_ITERATE_SHARED)

.iterate_shared = zpl_iterate,

elif defined(HAVE_VFS_ITERATE)

.iterate = zpl_iterate,

else

.readdir = zpl_readdir,

endif

.fsync = zpl_fsync,
.unlocked_ioctl = zpl_ioctl,

ifdef CONFIG_COMPAT

.compat_ioctl = zpl_compat_ioctl,

endif

};

dentry_operations:

typedef const struct dentry_operations dentry_operations_t;

dentry_operations_t zpl_dops_snapdirs = { /*

  • 仅在2.6.37及更高版本的内核中支持快照的自动挂载。
  • 在此之前,ops->follow_link() 回调被用作触发挂载的hack。
  • 结果的vfsmount然后被明确地移植到名称空间中。
  • 虽然可能添加兼容代码来实现这一点,但需要相当小心。 */ .d_automount = zpl_snapdir_automount, .d_revalidate = zpl_snapdir_revalidate, };

dentry_operations_t zpl_dentry_operations = { .d_revalidate = zpl_revalidate, };

zfs io(zio) pipeline概述基本功能:

zio服务zfs的所有IO操作,负责将dva(数据虚拟地址)转换为硬件磁盘地址,通过vdevs提供动态压缩、去重、加密和校验等用户空间应用策略,实现镜像和raid z功能。

zfs文件写入过分分析:

Chatbase
Chatbase

从你的知识库中构建一个AI聊天机器人

下载

zfs系统架构

聊聊zfs中的write

  -------------------system calls---------------------------
| |
kernel                            +-------------------+
+-----------|        VFS        |------------+
| File      +-------------------+            |
| Systems            | |                     |
|         +-----------------------+          |
|         |  ZFS     | |          |          |
|         |      +---------+      |          |
|         |      |   ZIO   |      |          |
|         |      +---------+      |          |
|         |      |  vdevs  |      |          |
|         +------+---------+------+          |
|                    | |                     |
+--------------------------------------------+
| |
+----------------+
|  block device  |
+----------------+
| |
+------------+
|    SCSI    |
+------------+
| |
+------------+
|    SAS     |
+------------+
| |
V |
.-----------.
-----------
|   disk    |
-----------

linux kernelsys_write:

当应用程序执行write函数时,会触发sys_write系统调用,具体的系统调用表可参考https://www.php.cn/link/1b7314ae41df37842b9d4e5e279caec1

vfs_write:

vfs层提供统一的写接口,这里提供不同文件系统write的统一接口。

do_sync_write:

同步写接口。其中sys_write/vfs_write/do_sync_write是内核提供的抽象写接口,do_sync_write是内核4.x版本的函数,在5.x版本中是new_sync_write函数。不同版本的linux内核会导致部分系统函数存在差异。以下是参考linux kernel 5的内核代码分析:

// libc提供的write接口
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, size_t, count) {
return ksys_write(fd, buf, count);
}

// write函数的系统调用 ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count) { ret = vfs_write(f.file, buf, count, ppos); return ret; }

// 调用实际文件系统的file的write_iter函数,到这里基本完成kernel内核层面的系统调用,接下来就是实际zfs的写函数 static inline ssize_t call_write_iter(struct file file, struct kiocb kio, struct iov_iter iter) { // struct file 的f_op是struct file_operations 指针,这个指针保存每个实际文件系统的文件操作的函数,这里需要查看zfs对应的file_opertions函数操作表 return file->f_op->write_iter(kio, iter); }

zfs kernel:

zfs写数据过程分为两个阶段:open contextsync context

open context阶段通过系统调用将数据从用户态拷贝到zfs的缓冲区,同时zfs将这些脏数据缓存在DMU中;sync context阶段判断脏数据是否超过4G,如果超过则通过zio批量将数据刷新到磁盘。DMU将数据写入ZIO,在ARC中缓存特定数据,并通知DSL层追踪空间使用。

第一阶段open context阶段是从zfs_write开始。zfs_write分为一个block的全部写和部分写;整块写首先针对块加锁,然后读取,在更改的新数据关联新的buffer;如果是部分写,首先也是读取操作,更改block中的部分内容,标记为脏页。

// z_node代表zfs中的inode,zfs_uio_t 是偏移量和长度
// 函数是经过省略的部分。
int zfs_write(znode_t zp, zfs_uio_t uio, int ioflag, cred_t cr) {
int error = 0;
ssize_t start_resid = zfs_uio_resid(uio);
ssize_t n = start_resid;
if (n == 0)
return (0);

zfsvfs_t *zfsvfs = ZTOZSB(zp);
const uint64_t max_blksz = zfsvfs-youjiankuohaophpcnz_max_blksz;
if (zfs_uio_prefaultpages(MIN(n, max_blksz), uio)) {
    ZFS_EXIT(zfsvfs);
    return (SET_ERROR(EFAULT));
}

const rlim64_t limit = MAXOFFSET_T;
if (woff youjiankuohaophpcn= limit) {
    zfs_rangelock_exit(lr);
    ZFS_EXIT(zfsvfs);
    return (SET_ERROR(EFBIG));
}
if (n youjiankuohaophpcn limit - woff)
    n = limit - woff;

uint64_t end_size = MAX(zp-youjiankuohaophpcnz_size, woff + n);
zilog_t *zilog = zfsvfs-youjiankuohaophpcnz_log;

// 文件分割为多个文件chunk
while (n youjiankuohaophpcn 0) {
    woff = zfs_uio_offset(uio);
    arc_buf_t *abuf = NULL;
    if (n youjiankuohaophpcn= max_blksz && woff youjiankuohaophpcn= zp-youjiankuohaophpcnz_size && 
        P2PHASE(woff, max_blksz) == 0 && 
        zp-youjiankuohaophpcnz_blksz == max_blksz) {
        size_t cbytes;
        abuf = dmu_request_arcbuf(sa_get_db(zp-youjiankuohaophpcnz_sa_hdl), max_blksz);
        if ((error = zfs_uiocopy(abuf-youjiankuohaophpcnb_data, max_blksz, UIO_WRITE, uio, &cbytes))) {
            dmu_return_arcbuf(abuf);
            break;
        }
        ASSERT3S(cbytes, ==, max_blksz);

        // 创建一个事务,表示准备更改操作,是否有空闲的空间
        dmu_tx_t *tx = dmu_tx_create(zfsvfs-youjiankuohaophpcnz_os);
        dmu_tx_hold_sa(tx, zp-youjiankuohaophpcnz_sa_hdl, B_FALSE);
        dmu_buf_impl_t *db = (dmu_buf_impl_t *)sa_get_db(zp-youjiankuohaophpcnz_sa_hdl);
        DB_DNODE_ENTER(db);
        dmu_tx_hold_write_by_dnode(tx, DB_DNODE(db), woff, MIN(n, max_blksz));

        // 等到喜爱一个可用的事务组,检查是否有足够空间和内存
        error = dmu_tx_assign(tx, TXG_WAIT) {
            dmu_tx_wait(tx) {
                dmu_tx_delay(tx, dirty);
            }
        }
        if (error) {
            dmu_tx_abort(tx);
            if (abuf != NULL)
                dmu_return_arcbuf(abuf);
            break;
        }

        if (lr-youjiankuohaophpcnlr_length == UINT64_MAX) {
            uint64_t new_blksz;
            if (zp-youjiankuohaophpcnz_blksz youjiankuohaophpcn max_blksz) {
                new_blksz = MIN(end_size, zp-youjiankuohaophpcnz_blksz));
            } else {
                new_blksz = MIN(end_size, max_blksz);
            }
            zfs_grow_blocksize(zp, new_blksz, tx);
            zfs_rangelock_reduce(lr, woff, n);
        }

        const ssize_t nbytes = MIN(n, max_blksz - P2PHASE(woff, max_blksz));
        ssize_t tx_bytes;
        if (abuf == NULL) {
            tx_bytes = zfs_uio_resid(uio);
            zfs_uio_fault_disable(uio, B_TRUE);
            // 数据从uio写入到DMU
            error = dmu_write_uio_dbuf(sa_get_db(zp-youjiankuohaophpcnz_sa_hdl), uio, nbytes, tx);
            zfs_uio_fault_disable(uio, B_FALSE);

ifdef linux

            if (error == EFAULT) {
                dmu_tx_commit(tx);
                if (tx_bytes != zfs_uio_resid(uio))
                    n -= tx_bytes - zfs_uio_resid(uio);
                if (zfs_uio_prefaultpages(MIN(n, max_blksz), uio)) {
                    break;
                }
                continue;
            }

endif

            if (error != 0) {
                dmu_tx_commit(tx);
                break;
            }
            tx_bytes -= zfs_uio_resid(uio);
        } else {
            error = dmu_assign_arcbuf_by_dbuf(sa_get_db(zp-youjiankuohaophpcnz_sa_hdl), woff, abuf, tx) {
                dmu_assign_arcbuf_by_dnode(DB_DNODE(dbuf), offset, buf, tx) {
                    dbuf_assign_arcbuf(db, buf, tx) {
                        // 标记脏的dbuf
                        (void) dbuf_dirty(db, tx);
                    }
                }
            }
            if (error != 0) {
                dmu_return_arcbuf(abuf);
                dmu_tx_commit(tx);
                break;
            }
        }

        error = sa_bulk_update(zp-youjiankuohaophpcnz_sa_hdl, bulk, count, tx);
        // zfs log写入
        zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag, NULL, NULL);
        dmu_tx_commit(tx);
    }

    if (ioflag & (O_SYNC | O_DSYNC) || zfsvfs-youjiankuohaophpcnz_os-youjiankuohaophpcnos_sync == ZFS_SYNC_ALWAYS)
        // zfs intent log写入,更改数据写到磁盘之前必须先保证日志罗盘
        zil_commit(zilog, zp-youjiankuohaophpcnz_id);

    ZFS_EXIT(zfsvfs);
    return (0);
}

}

第二个阶段是sync context阶段,zfs会启动内核线程来同步事务组,然后进入dsl层的同步,在进入DMU层的同步,最后是ZIO的pipeline:

void txg_sync_start(dsl_pool_t dp) {
tx->tx_sync_thread = thread_create(NULL, 0, txg_sync_thread, 0) {
static void txg_sync_thread(void arg) {
for (;;) {
clock_t timeout = zfs_txg_timeout * hz;
spa_sync(spa, txg);
// 找到脏的数据集,call dsl_datasetsync->dmu_objset_sync->zio pipeline
txg_dispatch_callbacks(dp, txg);
}
}
}
}

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

757

2023.08.22

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

526

2023.09.20

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1049

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

86

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

455

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

11

2026.01.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2021

2024.08.16

AO3中文版入口地址大全
AO3中文版入口地址大全

本专题整合了AO3中文版入口地址大全,阅读专题下面的的文章了解更多详细内容。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 9.1万人学习

【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2万人学习

Node.js-前端工程化必学
Node.js-前端工程化必学

共19课时 | 3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号