当前位置: 首页 > news >正文

块设备的两种访问方法的区别

概述

1.当我们运行类似于“dd if=/dev/sdb1of=sdb1.img”的命令把整个/dev/sdb1裸分区复制到sdb1.img的时候,内核走的是def_blk_fops这个file_operations

2.另外一种方法是通过文件系统来访问块设备,file_operations的实现则位于文件系统内,文件系统会把针对文件的读写转换为针对块设备原始扇区的读写。ext2、fat、Btrfs等文件系统中会实现针对VFS的file_operations成员函数,设备驱动层将看不到file_operations的存在

块设备节点文件

如 /dev/sdb1,直接对应于磁盘分区或整个磁盘。这些设备文件由内核的块设备层直接管理,使用 def_blk_fops 文件操作集;在open前因为mknod调用的init_special_inode初始化节点的时候指定了inode->i_fop = &def_blk_fops;

mknoddo_mknodatvfs_mknodshmem_mknodshmem_get_inodeinit_special_inode
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{inode->i_mode = mode;if (S_ISCHR(mode)) {inode->i_fop = &def_chr_fops;inode->i_rdev = rdev;} else if (S_ISBLK(mode)) {if (IS_ENABLED(CONFIG_BLOCK))inode->i_fop = &def_blk_fops;inode->i_rdev = rdev;} else if (S_ISFIFO(mode))inode->i_fop = &pipefifo_fops;else if (S_ISSOCK(mode));	/* leave it no_open_fops */elseprintk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"" inode %s:%lu\n", mode, inode->i_sb->s_id,inode->i_ino);
}

文件系统文件

/path/to/file,位于文件系统中,由文件系统驱动程序(如 ubifs)管理,inode创建的时候赋予特定的文件操作集(如 ubifs_file_operations);如下述会通过”inode->i_fop = &ubifs_file_operations;“给inode赋予特定的文件操作函数

ubifs_new_inode

struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,umode_t mode)
{int err;struct inode *inode;struct ubifs_inode *ui;bool encrypted = false;if (ubifs_crypt_is_encrypted(dir)) {err = fscrypt_get_encryption_info(dir);if (err) {ubifs_err(c, "fscrypt_get_encryption_info failed: %i", err);return ERR_PTR(err);}if (!fscrypt_has_encryption_key(dir))return ERR_PTR(-EPERM);encrypted = true;}inode = new_inode(c->vfs_sb);ui = ubifs_inode(inode);if (!inode)return ERR_PTR(-ENOMEM);/** Set 'S_NOCMTIME' to prevent VFS form updating [mc]time of inodes and* marking them dirty in file write path (see 'file_update_time()').* UBIFS has to fully control "clean <-> dirty" transitions of inodes* to make budgeting work.*/inode->i_flags |= S_NOCMTIME;inode_init_owner(inode, dir, mode);inode->i_mtime = inode->i_atime = inode->i_ctime =current_time(inode);inode->i_mapping->nrpages = 0;switch (mode & S_IFMT) {case S_IFREG:inode->i_mapping->a_ops = &ubifs_file_address_operations;inode->i_op = &ubifs_file_inode_operations;inode->i_fop = &ubifs_file_operations;break;case S_IFDIR:inode->i_op  = &ubifs_dir_inode_operations;inode->i_fop = &ubifs_dir_operations;inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;break;case S_IFLNK:inode->i_op = &ubifs_symlink_inode_operations;break;case S_IFSOCK:case S_IFIFO:case S_IFBLK:case S_IFCHR:inode->i_op  = &ubifs_file_inode_operations;encrypted = false;break;default:BUG();}ui->flags = inherit_flags(dir, mode);ubifs_set_inode_flags(inode);if (S_ISREG(mode))ui->compr_type = c->default_compr;elseui->compr_type = UBIFS_COMPR_NONE;ui->synced_i_size = 0;spin_lock(&c->cnt_lock);/* Inode number overflow is currently not supported */if (c->highest_inum >= INUM_WARN_WATERMARK) {if (c->highest_inum >= INUM_WATERMARK) {spin_unlock(&c->cnt_lock);ubifs_err(c, "out of inode numbers");make_bad_inode(inode);iput(inode);return ERR_PTR(-EINVAL);}ubifs_warn(c, "running out of inode numbers (current %lu, max %u)",(unsigned long)c->highest_inum, INUM_WATERMARK);}inode->i_ino = ++c->highest_inum;/** The creation sequence number remains with this inode for its* lifetime. All nodes for this inode have a greater sequence number,* and so it is possible to distinguish obsolete nodes belonging to a* previous incarnation of the same inode number - for example, for the* purpose of rebuilding the index.*/ui->creat_sqnum = ++c->max_sqnum;spin_unlock(&c->cnt_lock);if (encrypted) {err = fscrypt_inherit_context(dir, inode, &encrypted, true);if (err) {ubifs_err(c, "fscrypt_inherit_context failed: %i", err);make_bad_inode(inode);iput(inode);return ERR_PTR(err);}}return inode;
}

MTD

嵌入式系统最常用的qspi-flash,一般作为MTD设备,在其上又使用UBIFS来减少闪存的磨损

mount

比如mount文件系统的时候,会挂载文件系统到一个根目录,这个目录也会通过ubifs_new_inode来创建一个inode,mount流程如下

diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 38fe20f6e..1239fad37 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -90,7 +90,11 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,{struct inode *inode;struct ubifs_inode *ui;
-
+       static bool once = true;
+       if(once){
+               dump_stack();
+               once = false;
+       }inode = new_inode(c->vfs_sb);ui = ubifs_inode(inode);if (!inode)[    4.181047] [<c00198e9>] (unwind_backtrace+0x1/0x88) from [<c0017e5f>] (show_stack+0xb/0xc)
[    4.205935] [<c0017e5f>] (show_stack+0xb/0xc) from [<c00b289f>] (ubifs_new_inode+0x17/0x218)
[    4.242626] [<c00b289f>] (ubifs_new_inode+0x17/0x218) from [<c00b2c69>] (ubifs_mkdir+0x41/0x138)
[    4.251746] [<c00b2c69>] (ubifs_mkdir+0x41/0x138) from [<c00776f7>] (vfs_mkdir+0x47/0x74)
[    4.276054] [<c00776f7>] (vfs_mkdir+0x47/0x74) from [<c00d6c1d>] (ovl_create_real+0x5d/0x94)
[    4.284503] [<c00d6c1d>] (ovl_create_real+0x5d/0x94) from [<c00d5dc3>] (ovl_workdir_create+0x83/0x100)
[    4.310915] [<c00d5dc3>] (ovl_workdir_create+0x83/0x100) from [<c00d60b7>] (ovl_fill_super+0x1a3/0x34c)
[    4.320431] [<c00d60b7>] (ovl_fill_super+0x1a3/0x34c) from [<c00732d5>] (mount_nodev+0x25/0x60)
[    4.335254] [<c00732d5>] (mount_nodev+0x25/0x60) from [<c00738a1>] (mount_fs+0x9/0x7c)
[    4.343245] [<c00738a1>] (mount_fs+0x9/0x7c) from [<c0081ea3>] (vfs_kern_mount+0x53/0xb0)
[    4.352334] [<c0081ea3>] (vfs_kern_mount+0x53/0xb0) from [<c00832cd>] (do_mount+0x509/0x5a4)
[    4.362857] [<c00832cd>] (do_mount+0x509/0x5a4) from [<c00833b9>] (SyS_mount+0x51/0x72)
[    4.372007] [<c00833b9>] (SyS_mount+0x51/0x72) from [<c0009081>] (ret_fast_syscall+0x1/0x46)

创建文件

应用层通过touch命令,或者create或者open函数调用创建文件时,本质open系统调用带O_CREAT的flag来执行创建的操作

SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
{return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
}
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{if (force_o_largefile())flags |= O_LARGEFILE;return do_sys_open(AT_FDCWD, filename, flags, mode);
}

open具体获取inode的具体流程如下

do_sys_opendo_filp_openpath_openat
static struct file *path_openat(struct nameidata *nd,const struct open_flags *op, unsigned flags)
{const char *s;struct file *file;int opened = 0;int error;file = get_empty_filp();if (IS_ERR(file))return file;file->f_flags = op->open_flag;if (unlikely(file->f_flags & __O_TMPFILE)) { /*处理临时文件*/error = do_tmpfile(nd, flags, op, file, &opened);goto out2;}if (unlikely(file->f_flags & O_PATH)) { /*打开一个文件或目录的路径,但不进行实际的读写操作*/error = do_o_path(nd, flags, file);if (!error)opened |= FILE_OPENED;goto out2;}s = path_init(nd, flags); /*初始化路径*/if (IS_ERR(s)) {put_filp(file);return ERR_CAST(s);}while (!(error = link_path_walk(s, nd)) && /* 获取不同文件系统的inode的具体实现*/(error = do_last(nd, file, op, &opened)) > 0) { /*创建或打开文件*/nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);s = trailing_symlink(nd);if (IS_ERR(s)) {error = PTR_ERR(s);break;}}terminate_walk(nd);
out2:if (!(opened & FILE_OPENED)) {BUG_ON(!error);put_filp(file);}if (unlikely(error)) {if (error == -EOPENSTALE) {if (flags & LOOKUP_RCU)error = -ECHILD;elseerror = -ESTALE;}file = ERR_PTR(error);}return file;
}

获取inode

 link_path_walk最终调用到ubiffs_iget来获取inode ;通过fast和slow两种方式来查找或者创建inode;并初始化inodef的i_fops:inode->i_fop = &ubifs_file_operations; 对不同文件属性赋值不同操作函数

link_path_walkwalk_componentlookup_fast //用缓存中的 dentry 来避免不必要的磁盘访问,从而提高路径解析的效率lookup_slow //它在缓存中找不到匹配的 dentry 时被调用,需要通过文件系统的特定方法来查找或创建 dentry 和 inodeinode->i_op->lookup(inode, dentry, flags); ubifs_lookupubifs_igetstruct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
{int err;union ubifs_key key;struct ubifs_ino_node *ino;struct ubifs_info *c = sb->s_fs_info;struct inode *inode;struct ubifs_inode *ui;dbg_gen("inode %lu", inum);inode = iget_locked(sb, inum);if (!inode)return ERR_PTR(-ENOMEM);if (!(inode->i_state & I_NEW))return inode;ui = ubifs_inode(inode);ino = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS);if (!ino) {err = -ENOMEM;goto out;}ino_key_init(c, &key, inode->i_ino);err = ubifs_tnc_lookup(c, &key, ino);if (err)goto out_ino;inode->i_flags |= S_NOCMTIME;
#ifndef CONFIG_UBIFS_ATIME_SUPPORTinode->i_flags |= S_NOATIME;
#endifset_nlink(inode, le32_to_cpu(ino->nlink));i_uid_write(inode, le32_to_cpu(ino->uid));i_gid_write(inode, le32_to_cpu(ino->gid));inode->i_atime.tv_sec  = (int64_t)le64_to_cpu(ino->atime_sec);inode->i_atime.tv_nsec = le32_to_cpu(ino->atime_nsec);inode->i_mtime.tv_sec  = (int64_t)le64_to_cpu(ino->mtime_sec);inode->i_mtime.tv_nsec = le32_to_cpu(ino->mtime_nsec);inode->i_ctime.tv_sec  = (int64_t)le64_to_cpu(ino->ctime_sec);inode->i_ctime.tv_nsec = le32_to_cpu(ino->ctime_nsec);inode->i_mode = le32_to_cpu(ino->mode);inode->i_size = le64_to_cpu(ino->size);ui->data_len    = le32_to_cpu(ino->data_len);ui->flags       = le32_to_cpu(ino->flags);ui->compr_type  = le16_to_cpu(ino->compr_type);ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum);ui->xattr_cnt   = le32_to_cpu(ino->xattr_cnt);ui->xattr_size  = le32_to_cpu(ino->xattr_size);ui->xattr_names = le32_to_cpu(ino->xattr_names);ui->synced_i_size = ui->ui_size = inode->i_size;ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0;err = validate_inode(c, inode);if (err)goto out_invalid;switch (inode->i_mode & S_IFMT) {case S_IFREG:inode->i_mapping->a_ops = &ubifs_file_address_operations;inode->i_op = &ubifs_file_inode_operations;inode->i_fop = &ubifs_file_operations;if (ui->xattr) {ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);if (!ui->data) {err = -ENOMEM;goto out_ino;}memcpy(ui->data, ino->data, ui->data_len);((char *)ui->data)[ui->data_len] = '\0';} else if (ui->data_len != 0) {err = 10;goto out_invalid;}break;case S_IFDIR:inode->i_op  = &ubifs_dir_inode_operations;inode->i_fop = &ubifs_dir_operations;if (ui->data_len != 0) {err = 11;goto out_invalid;}break;case S_IFLNK:inode->i_op = &ubifs_symlink_inode_operations;if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) {err = 12;goto out_invalid;}ui->data = kmalloc(ui->data_len + 1, GFP_NOFS);if (!ui->data) {err = -ENOMEM;goto out_ino;}memcpy(ui->data, ino->data, ui->data_len);((char *)ui->data)[ui->data_len] = '\0';break;case S_IFBLK:case S_IFCHR:{dev_t rdev;union ubifs_dev_desc *dev;ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS);if (!ui->data) {err = -ENOMEM;goto out_ino;}dev = (union ubifs_dev_desc *)ino->data;if (ui->data_len == sizeof(dev->new))rdev = new_decode_dev(le32_to_cpu(dev->new));else if (ui->data_len == sizeof(dev->huge))rdev = huge_decode_dev(le64_to_cpu(dev->huge));else {err = 13;goto out_invalid;}memcpy(ui->data, ino->data, ui->data_len);inode->i_op = &ubifs_file_inode_operations;init_special_inode(inode, inode->i_mode, rdev);break;}case S_IFSOCK:case S_IFIFO:inode->i_op = &ubifs_file_inode_operations;init_special_inode(inode, inode->i_mode, 0);if (ui->data_len != 0) {err = 14;goto out_invalid;}break;default:err = 15;goto out_invalid;}kfree(ino);ubifs_set_inode_flags(inode);unlock_new_inode(inode);return inode;out_invalid:ubifs_err(c, "inode %lu validation failed, error %d", inode->i_ino, err);ubifs_dump_node(c, ino);ubifs_dump_inode(c, inode);err = -EINVAL;
out_ino:kfree(ino);
out:ubifs_err(c, "failed to read inode %lu, error %d", inode->i_ino, err);iget_failed(inode);return ERR_PTR(err);
}

将file的fop赋值为inode的fop

里面的f->f_op = fops_get(inode->i_fop);对于/dev下的设备文件就是把上面init_special_inode里的inode->i_fop = &def_blk_fops;def_blk_fops的open为blkdev_open,它又会调用块设备驱动实现的的open;对于在文件系统中touch或者creat的文件,就是文件系统实现的fops

do_sys_opendo_filp_openpath_openatvfs_open do_dentry_openstatic int do_dentry_open(struct file *f,struct inode *inode,int (*open)(struct inode *, struct file *))
{static const struct file_operations empty_fops = {};int error;path_get(&f->f_path);f->f_inode = inode;f->f_mapping = inode->i_mapping;/* Ensure that we skip any errors that predate opening of the file */f->f_wb_err = filemap_sample_wb_err(f->f_mapping);if (unlikely(f->f_flags & O_PATH)) {f->f_mode = FMODE_PATH | FMODE_OPENED;f->f_op = &empty_fops;return 0;}/* Any file opened for execve()/uselib() has to be a regular file. */if (unlikely(f->f_flags & FMODE_EXEC && !S_ISREG(inode->i_mode))) {error = -EACCES;goto cleanup_file;}if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {error = get_write_access(inode);if (unlikely(error))goto cleanup_file;error = __mnt_want_write(f->f_path.mnt);if (unlikely(error)) {put_write_access(inode);goto cleanup_file;}f->f_mode |= FMODE_WRITER;}/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))f->f_mode |= FMODE_ATOMIC_POS;f->f_op = fops_get(inode->i_fop);if (WARN_ON(!f->f_op)) {error = -ENODEV;goto cleanup_all;}error = security_file_open(f);if (error)goto cleanup_all;error = break_lease(locks_inode(f), f->f_flags);if (error)goto cleanup_all;/* normally all 3 are set; ->open() can clear them if needed */f->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;if (!open)open = f->f_op->open;if (open) {error = open(inode, f);if (error)goto cleanup_all;}f->f_mode |= FMODE_OPENED;if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)i_readcount_inc(inode);if ((f->f_mode & FMODE_READ) &&likely(f->f_op->read || f->f_op->read_iter))f->f_mode |= FMODE_CAN_READ;if ((f->f_mode & FMODE_WRITE) &&likely(f->f_op->write || f->f_op->write_iter))f->f_mode |= FMODE_CAN_WRITE;f->f_write_hint = WRITE_LIFE_NOT_SET;f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);/* NB: we're sure to have correct a_ops only after f_op->open */if (f->f_flags & O_DIRECT) {if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)return -EINVAL;}/** XXX: Huge page cache doesn't support writing yet. Drop all page* cache for this file before processing writes.*/if ((f->f_mode & FMODE_WRITE) && filemap_nr_thps(inode->i_mapping))truncate_pagecache(inode, 0);return 0;cleanup_all:if (WARN_ON_ONCE(error > 0))error = -EINVAL;fops_put(f->f_op);if (f->f_mode & FMODE_WRITER) {put_write_access(inode);__mnt_drop_write(f->f_path.mnt);}
cleanup_file:path_put(&f->f_path);f->f_path.mnt = NULL;f->f_path.dentry = NULL;f->f_inode = NULL;return error;
}

文件相关结构体

dentry

表示目录条目,用于路径解析(将路径名转换为文件系统中的实际位置)和缓存(缓存最近访问的目录项),维护文件和目录之间的链接关系

d_name:文件名或目录名。
d_inode:指向该 dentry 对应的 inode。
d_parent:指向父目录的 dentry。
d_subdirs:指向子目录的链表头。
d_u:用于缓存或其他用途的联合体。
d_sb:指向文件系统的超级块(super_block)。
d_flags:标志位,用于控制 dentry 的行为。
d_count:引用计数。
d_lockref:锁和引用计数。
d_vfs_flags:VFS 标志。
d_alias:用于哈希表的链表头

inode

表示文件或目录的元数据,如文件类型、权限、时间戳;用于存储文件属性和操作函数。

i_ino:inode 编号,唯一标识一个 inode。
i_mode:文件类型和权限。
i_uid:文件所有者的用户 ID。
i_gid:文件所有者的组 ID。
i_size:文件大小。
i_atime:上次访问时间。
i_mtime:上次修改时间。
i_ctime:上次状态改变时间。
i_links_count:硬链接数。
i_blocks:文件占用的块数。
i_blkbits:块大小的对数值。
i_op:inode 操作指针。
i_fop:文件操作指针。
i_mapping:地址空间映射。
i_private:私有数据。
i_sb:指向文件系统的超级块(super_block)。

file

表示打开文件,用于文件操作和状态管理。

f_path:指向文件的路径(path),包含 mnt 和 dentry。
f_inode:指向文件的 inode。
f_mapping:文件的地址空间映射。
f_mode:文件的打开模式(读、写、执行)。
f_flags:文件标志(如 O_RDONLY、O_WRONLY、O_RDWR 等)。
f_pos:文件的当前读写位置。
f_op:文件操作指针。
f_owner:文件的所有者。
f_cred:文件的凭证(用户和组信息)。
f_count:引用计数。
f_version:版本号,用于检测文件状态变化。
f_wb_err:写入错误。
f_sb_err:超级块错误。

关系

一个 dentry 可以对应一个 inode,多个 dentry 也可以对应同一个 inode(硬链接)
多个 file 结构体可以对应同一个 inode,表示同一个文件被多次打开
file 结构体中的 f_op 指针指向 inode 中的 i_fop,用于执行文件操作
file 结构体通过 f_path 引用 dentry,确保文件路径的正确性
每个打开的文件描述符对应一个 file 结构体,通过 file 结构体可以访问文件的 dentry 和 inode

http://www.lryc.cn/news/485310.html

相关文章:

  • java 泛型中的 ?
  • 如何在jupyter notebook切换python环境
  • 用Python将Word文档转换为Markdown格式
  • CSV 文件
  • SpringCloud核心组件(五)
  • TCP为什么需要三次握手和四次挥手,有哪些需要注意的地方?
  • 机器学习(基础2)
  • Cpolar 内网穿透使用
  • ThreadLocal 提供线程局部变量
  • MongoDB聚合管道数组操作
  • 大数据如何助力干部选拔的公正性
  • Python_爬虫2_爬虫引发的问题
  • shell编程之编程基础
  • 24.11.15 Vue3
  • 图形几何之美系列:法向量计算之轮廓有向面积辅助法
  • CPU的性能指标总结(学习笔记)
  • Cadence安装
  • 【网络】子网掩码
  • Android Osmdroid + 天地图 (二)
  • 使用大语言模型创建 Graph 数据
  • Java poi 模板导出Word 带图片
  • SpringCloud-使用FFmpeg对视频压缩处理
  • shell bash---类似数组类型
  • IIoT(Industrial Internet of Things,工业物联网)
  • 【C++】引用(reference)
  • 学习日记_20241115_聚类方法(层次聚类)
  • 安卓开发怎么获取返回上一级activity事件
  • 神经网络与Transformer详解
  • C语言之MakeFile
  • vue项目PC端和移动端实现在线预览docx、excel、pdf文件