网站建设淄博佳铉网络,电商平台开网店,淄博网站优化推广,一个空间放2个网站裸盘是如何能达到我们日常操作目录那样#xff0c;按目录依次访问文件等#xff0c;实际上就是基于裸盘上#xff0c;用文件系统进行控制。
0#xff1a;总结。
0#xff1a;mount是入口#xff0c;一个裸盘先赋予文件系统#xff0c;然后mount后才可以用。 1#xf…裸盘是如何能达到我们日常操作目录那样按目录依次访问文件等实际上就是基于裸盘上用文件系统进行控制。
0总结。
0mount是入口一个裸盘先赋予文件系统然后mount后才可以用。 1内核提供了插入文件系统的方法register_filesystem函数和对应的struct file_system_type 结构体。
2插入内核模块的demo基于插入内核模块实现插入一个新的文件系统。
3基于文件系统实现对应的指令mount是入口mount_nodev和mount_bdev两种mount的区别才有其他后续接口关注struct super_block struct inode_operationsstruct file_operations。
1首先要了解文件系统以及挂载。
新增一个裸盘后首先需要给裸盘赋予文件系统然后就可以挂载后使用了。
#可以看到 sdb设备是新增的空闲设备 10G 虚拟机上的scsi设备
ubuntuubuntu:~/start_test$ lsblk
...
sdb 8:16 0 10G 0 disk
sr0 11:0 1 1.8G 0 rom #首先裸盘是没有文件系统的 可以给它加载文件系统
rootubuntu:/home/ubuntu/start_test# mkfs.ext4 /dev/sdb
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 2621440 4k blocks and 655360 inodes
Filesystem UUID: 96099ecd-4c7c-4006-b843-7faf4ceb07c4
Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done #对该磁盘进行挂载 挂载后可通过挂载目录对磁盘进行访问
rootubuntu:/home/ubuntu/start_test# mkdir mnt
rootubuntu:/home/ubuntu/start_test# mount -t ext4 /dev/sdb /home/ubuntu/start_test/mnt/#可以看到 已经挂载成功
rootubuntu:/home/ubuntu/start_test/mnt# ls
lostfound
rootubuntu:/home/ubuntu/start_test# df -h
...
/dev/sdb 9.8G 28K 9.3G 1% /home/ubuntu/start_test/mnt#可以借助目录对磁盘进行访问了。
rootubuntu:/home/ubuntu/start_test/mnt# mkdir 1
rootubuntu:/home/ubuntu/start_test/mnt# umount /dev/sdb
umount: /home/ubuntu/start_test/mnt: target is busy.
rootubuntu:/home/ubuntu/start_test/mnt# cd ../
rootubuntu:/home/ubuntu/start_test# umount /dev/sdb 2插入内核模块demo
本节目的是借助内核提供的文件系统接口插入一个文件系统内核模块。
2.1 插入一个内核模块观察日志。
#include linux/init.h
#include linux/module.h
#include linux/kernel.h //printkstatic int __init hello_init(void) {printk(KERN_INFO Hello, World!\n);return 0;
}static void __exit hello_exit(void) {printk(KERN_INFO Goodbye, World!\n);
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE(GPL);
MODULE_AUTHOR(hello World test);
MODULE_DESCRIPTION(A simple example kernel module);2.2 借助make对其进行编译生成ko文件。
obj-m hello_module.o
KERNELDIR ? /lib/modules/$(shell uname -r)/build
all:make -C $(KERNELDIR) M$(PWD) modules
clean:make -C $(KERNELDIR) M$(PWD) clean2.3 编译该模块加入内核和移出内核观察日志。
#插入内核模块 移除内核模块
rootubuntu:/home/ubuntu/storage/module_test# insmod hello_module.ko
rootubuntu:/home/ubuntu/storage/module_test# rmmod hello_module.ko
#使用dmesg查看系统日志 -x这里打印前面的级别
rootubuntu:/etc# dmesg -x |tail
kern :info : [526406.354334] Hello, World!
kern :err : [526413.567482] Goodbye, World!#带系统时间进行显示 可以了解dmesg相关指令参数
rootubuntu:/etc# dmesg -x -T|tail
kern :info : [Fri May 17 04:53:13 2024] Hello, World!
kern :err : [Fri May 17 04:53:20 2024] Goodbye, World!3注册文件系统通过内核模块给特定目录加入文件系统
借助mount -t 文件系统 插入文件系统内核模块后借助mount指令实现挂载可以测试文件系统
内核提供了两种挂载的方式。
mount_nodev 更适合用于创建虚拟文件系统, 如创建的一个文件夹。
mount_bdev需要的是绑定对应的块设备如硬盘硬盘上的 ext4、xfs 等常见文件系统。
3.1 注册文件系统确定mount的入口正确 通过struct file_system_type结构体register_filesystem 注册文件系统
主要了解register_filesystem 函数接口以及struct file_system_type结构体 中相关函数以及操作对应函数入口。
这里仅仅观察执行mount时能否正常日志记录。
3.1.1 代码demo借助makefiel编译
//这里如果mount时没有实现该功能 测试后会导致模块无法卸载
#include linux/init.h
#include linux/module.h#include linux/fs.h //内核下usr/src/linux-headers-5.15.0-107/include/linux///挂载虚拟文件时用 对应file_system_type中name
// mount -t filesys_test nodev ./mnt/
// mount_nodev();//挂载设备文件时用
// mount -t filesys_test /dev/nvme ./mnt/
// mount_bdev();
//借助mount -t 指令实现挂载时触发
struct dentry *filesys_mount(struct file_system_type *fstype, int flags,const char *dev_name, void *data) {
{printk(filesys mount ...\n);return NULL; //这里如果只是NULL 导致该模块无法卸载
} void filesys_kill_superblock (struct super_block *)
{printk(filesys des ... \n);
}//定义对应的文件系统结构体 对应内核模块的linux/fs.h
//文件系统看看 linux/fs.h 文件下的相关内容
struct file_system_type file_sys_st {.owner THIS_MODULE,.name filesys_test,// .fs_flags //看一下默认多少// .init_fs_context 必要的初始化相关函数// .parameters 未定义相关文件系统参数.mount filesys_mount, //挂载时对应执行的函数指针.kill_sb filesys_kill_superblock //销毁超级块时触发
};//一定要注意类型对应 参数有个void function declaration isn’t a prototype [-Werrorstrict-prototypes
static int __init filesys_init(void)
{//注册文件系统int ret register_filesystem(file_sys_st);if(ret){printk(init module: register filesys error. [%d]. \n, ret);return ret;}printk(init module: register filesys success. [%d]. \n, ret);return ret;
}static void __exit filesys_exit(void)
{//对应上文模块初始化unregister_filesystem(file_sys_st);printk(destory module: unregister filesys.\n);
}module_init(filesys_init);
module_exit(filesys_exit);
MODULE_LICENSE(GPL);
–需要了解一下fs.h中结构体和相关函数
struct file_system_type {const char *name;int fs_flags; //文件系统标志位#define FS_REQUIRES_DEV 1 //该文件系统需要一个实际设备作为其挂载点。#define FS_BINARY_MOUNTDATA 2#define FS_HAS_SUBTYPE 4#define FS_USERNS_MOUNT 8 // Can be mounted by userns root #define FS_DISALLOW_NOTIFY_PERM 16 // Disable fanotify permission events #define FS_ALLOW_IDMAP 32 // FS has been updated to handle vfs idmappings. #define FS_THP_SUPPORT 8192 // Remove once all fs converted #define FS_RENAME_DOES_D_MOVE 32768 // FS will handle d_move() during rename() internally. int (*init_fs_context)(struct fs_context *); //挂载前的动作const struct fs_parameter_spec *parameters; //文件系统参数的结构体struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); // 挂载函数指针void (*kill_sb) (struct super_block *); // 销毁超级块函数指针struct module *owner; // 所属模块指针可选struct file_system_type * next; // 链表下一个元素指针可选struct hlist_head fs_supers;struct lock_class_key s_lock_key;struct lock_class_key s_umount_key;struct lock_class_key s_vfs_rename_key;struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];struct lock_class_key i_lock_key;struct lock_class_key i_mutex_key;struct lock_class_key invalidate_lock_key;struct lock_class_key i_mutex_dir_key;
};extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *);3.1.2 对应执行和测试导致该内核模块无法卸载
rootubuntu:/home/ubuntu/storage/filesys# insmod filesys.ko
rootubuntu:/home/ubuntu/storage/filesys# mount -t filesys_test nodev ./mnt/
Killed
# 发现mount后 该内核模块无法移除对应的dmesg中有相关错误日志
rootubuntu:/home/ubuntu/storage/filesys# rmmod filesys.ko
rmmod: ERROR: Module filesys is in use
rootubuntu:/home/ubuntu/storage/filesys# lsmod|grep file
filesys 16384 1rootubuntu:/# dmesg
[537582.603611] init module: register filesys success. [0].
[537589.942759] filesys mount ...
[537589.943232] BUG: kernel NULL pointer dereference, address: 0000000000000068
[537589.943612] #PF: supervisor read access in kernel mode
[537589.943997] #PF: error_code(0x0000) - not-present page
[537589.944405] PGD 0 P4D 0
[537589.944774] Oops: 0000 [#1] SMP NOPTI3.2 实现文件系统对应mount模块使该模块正常mount_nodev和mount_bdev差别
3.2.1 必要代码模块
//个人理解mount是文件系统挂载的第一步比如把这个目录当做根目录基于该目录下去创建文件系统使支持相关文件/文件夹操作禁止外部原文件系统访问。
//mount时 需要初始化相关的超级块 需要对根目录节点进行处理以及相关初始化
const struct inode_operations filesys_inode_ops
{};const struct file_operations filesys_file_ops
{};//回调函数 对参数做初始化
//struct super_block 超级块 存储了文件系统磁盘大小系统类型inode表等
static int filesys_super_block(struct super_block * sb, void *data, int flag)
{struct inode *root_inode;printk(filesys mount super_block ...\n);//创建根目录节点 root_inode new_inode(sb); //创建一个inode节点从sb中分配//初始化第一个inode的节点信息// void inode_init_owner(struct user_namespace *mnt_userns, struct inode *inode,// const struct inode *dir, umode_t mode);// init_user_ns 命名空间 nop_mnt_idmapinode_init_owner(init_user_ns, root_inode, NULL, S_IFDIR); //S_IFDIR表示目录类型 sys/stat.h中root_inode-i_sb sb;root_inode-i_op filesys_inode_ops; //inode操作相关函数root_inode-i_fop filesys_file_ops; //文件操作相关函数//fs.h中封装的函数root_inode-i_atime root_inode-i_mtime root_inode-i_ctime current_time(root_inode);//根目录对应的结构体指针 //d_make_root函数用于在mount一个文件系统的时候,为文件系统的root inode创建对应的dentry。//dentry 是表示目录项的数据结构 该目录需要的必要信息sb-s_root d_make_root(root_inode);return 0;
}//挂载虚拟文件时用 对应file_system_type中name
// mount -t filesys_test nodev ./mnt/
// mount_nodev();//挂载设备文件时用
// mount -t filesys_test /dev/nvme ./mnt/
// mount_bdev();
//借助mount -t 指令实现挂载时触发
struct dentry *filesys_mount(struct file_system_type *fstype, int flags,const char *dev_name, void *data)
{struct dentry* re; //为了解决告警放最前面printk(filesys mount ...\n);//mount时实际上需要关注目标文件夹的一些特性 不同的文件类型不同的接口re mount_nodev(fstype, flags, data, filesys_super_block);return re; //这里如果只是NULL 导致该模块无法卸载
} void filesys_kill_superblock(struct super_block * sb)
{printk(filesys des ... \n);//这里卸载需要需求清理kill_litter_super(sb); // 调用默认的卸载函数return;
}3.2.2 执行结果能正常umount以及移除内核模块暂时该目录下创建删除等各种指令依然无法使用
rootubuntu:/home/ubuntu/storage/filesys# insmod filesys.ko
rootubuntu:/home/ubuntu/storage/filesys# mount -t filesys_test nodev ./mnt/
rootubuntu:/home/ubuntu/storage/filesys# umount ./mnt/
rootubuntu:/home/ubuntu/storage/filesys# rmmod filesys.ko rootubuntu:/home/ubuntu/storage/filesys# dmesg --clear
rootubuntu:/home/ubuntu/storage/filesys# dmesg
rootubuntu:/home/ubuntu/storage/filesys# dmesg
[ 2065.265739] init module: register filesys success. [0].
[ 2071.640164] filesys mount ...
[ 2071.640215] filesys mount super_block ...
[ 2079.303963] filesys des ...
[ 2082.553245] destory module: unregister filesys.3.3 基于mount已经实现实现其他基本指令lscdmkdir等
构造对应的结构体 struct inode_operations 和struct file_operations 实现内部接口。
3.3.1 代码demo, 相关inode_operations 对应的接口
#include linux/init.h
#include linux/module.h
#include linux/slab.h
#include linux/fs.h //内核下usr/src/linux-headers-5.15.0-107/include/linux///考虑已有的结构体和必要内容结构体 文件夹信息和文件内容如何存储和识别
//超级块》inode首节点根目录》inode节点目录/文件识别必要信息
#define MAX_FILE_NUM 1024
#define MAX_FILENAME_LEN 64typedef struct file_context_st{char *buffer; //文件内容的存储 需要研究文件数据块的存储逻辑 小文件 大文件int buf_len;char filename[MAX_FILENAME_LEN];
}FILE_CONTEXT_ST;//inode节点中i_ino唯一标识 可以与文件或者目录结构做对应关系
//inode中可以指向该文件的写入内容地址
FILE_CONTEXT_ST g_file[MAX_FILE_NUM] {0};int get_one_file_idx(void) {int i 0;for (i 0;i MAX_FILE_NUM;i ) {if (g_file[i].buffer NULL g_file[i].buf_len 0) {return i;}}return MAX_FILE_NUM;
}int filesys_inode_create(struct user_namespace *uns, struct inode *dir,struct dentry *dentry,umode_t mode, bool excl);
struct dentry *filesys_inode_lookup (struct inode *inode,struct dentry *dentry, unsigned int flags);
int filesys_inode_mkdir(struct user_namespace *uns, struct inode *dir, struct dentry *dentry, umode_t mode);
int filesys_inode_rmdir(struct inode *inode, struct dentry *dentry);
int filesys_inode_unlink(struct inode *inode, struct dentry *dentry);int filesys_file_open (struct inode *inode, struct file *filp);
int filesys_file_release(struct inode *inode, struct file *filp);
ssize_t filesys_file_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset);
ssize_t filesys_file_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset);
int filesys_file_iterate (struct file *filp, struct dir_context *ctx);const struct inode_operations filesys_inode_ops
{//创建文件 查看文件 移除文件 返回文件信息 查看属性 链接指令等.create filesys_inode_create,.lookup filesys_inode_lookup,.mkdir filesys_inode_mkdir,.rmdir filesys_inode_rmdir,.unlink filesys_inode_unlink,
};const struct file_operations filesys_file_ops
{.open filesys_file_open,.release filesys_file_release,.read filesys_file_read,.write filesys_file_write,.iterate filesys_file_iterate,
};//这里创建文件 需要考虑文件名和文件内容 文件夹的创建对应mkdir
//需要考虑逐层创建文件和文件夹的结构 struct dentry
int filesys_inode_create(struct user_namespace *uns, struct inode *dir,struct dentry *dentry,umode_t mode, bool excl)
{//编译时有校验 初始化放在前面struct inode *inode;struct super_block *sb dir-i_sb;int idx 0;//从参数中获取到文件名 printk(filename: %s\n, dentry-d_name.name);//实际上就是创建一个节点保存必要的信息 inode new_inode(sb); //这里要考虑父节点 同级节点 inode-i_sb sb;inode-i_op filesys_inode_ops; //这里文件节点 应该对相关操作做限制吧inode-i_fop filesys_file_ops;//这里涉及文件名 以及文件预留内存等必要信息 idx get_one_file_idx();if(idx MAX_FILE_NUM){return -EINVAL;}g_file[idx].buffer kmalloc(1024, GFP_KERNEL);g_file[idx].buf_len 0;strncpy(g_file[idx].filename, dentry-d_name.name, MAX_FILENAME_LEN);inode-i_ino idx; //唯一标识 可以找到关联内容inode-i_private g_file[idx]; //私有空间 存储必要信息 指针//使用传递参数对inode做必要的初始化inode_init_owner(uns, inode, dir, mode);//创建dentry结构 做相关指向关联 加入目录的功能d_add(dentry, inode); //return 0;
}struct dentry *filesys_inode_lookup (struct inode *inode,struct dentry *dentry, unsigned int flags) {printk(filesys_inode_lookup\n);return NULL;}//mkdir 文件夹的创建
int filesys_inode_mkdir(struct user_namespace *uns, struct inode *dir, struct dentry *dentry, umode_t mode) {printk(filesys_inode_mkdir\n);return 0;
}// rmdir dir
int filesys_inode_rmdir(struct inode *inode, struct dentry *dentry) {printk(filesys_inode_rmdir\n);return 0;
}// rm file
int filesys_inode_unlink(struct inode *inode, struct dentry *dentry) {printk(filesys_inode_unlink\n);return 0;
} //ls 调用 open iterate release
//touch 调用 inode_lookup open release
int filesys_file_open (struct inode *inode, struct file *filp)
{printk(filesys_file_open\n);return 0;
}int filesys_file_release(struct inode *inode, struct file *filp)
{printk(filesys_file_release\n);return 0;
}ssize_t filesys_file_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{size_t len length 1024 ? 1024 : length;FILE_CONTEXT_ST *blk filp-f_path.dentry-d_inode-i_private;char *ptr blk-buffer;int buflen blk-buf_len;if (*offset buflen) {return 0;}printk(filesys_file_read len: %ld, offset: %lld\n, len, *offset);if (!ptr)return -EINVAL;ptr *offset;if (copy_to_user(buffer, ptr, len)) {return -EFAULT;}*offset len;return len;
}ssize_t filesys_file_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset)
{size_t len length 1024 ? 1024 : length;FILE_CONTEXT_ST *blk filp-f_path.dentry-d_inode-i_private;char *ptr blk-buffer;printk(filesys_file_write\n);if (!ptr)return -EINVAL;ptr *offset;if (copy_from_user(ptr, buffer, len)) {return -EFAULT;}blk-buf_len len;filp-f_inode-i_size blk-buf_len;*offset len;return len;
}int filesys_file_iterate (struct file *filp, struct dir_context *ctx)
{int count get_one_file_idx();int i 0;printk(filesys_file_iterate-- count: %d, pos: %lld\n, count, ctx-pos);if(ctx-pos count) return 0;//在目录中添加.和..if (!dir_emit_dots(filp, ctx)) {return 0;}//遍历将一个文件或子目录的信息添加到目录中的函数for (i 0;i count;i ) {dir_emit(ctx, g_file[i].filename, strlen(g_file[i].filename), i, DT_UNKNOWN);ctx-pos ;}return 0;
}
3.3.2 运行结果测试
只是测试框架以及方向其他基本指令待实现。
mount -t filesys_test nodev ./mnt/ 中filesys_test 这里是代码中注册的文件系统名。
rootubuntu:/home/ubuntu/storage/filesys# insmod filesys.ko
rootubuntu:/home/ubuntu/storage/filesys# mount -t filesys_test nodev ./mnt/
rootubuntu:/home/ubuntu/storage/filesys# touch ./mnt/1.txt
rootubuntu:/home/ubuntu/storage/filesys# echo 123456 ./mnt/1.txt
rootubuntu:/home/ubuntu/storage/filesys# cat ./mnt/1.txt
123456
rootubuntu:/home/ubuntu/storage/filesys# umount ./mnt/
rootubuntu:/home/ubuntu/storage/filesys# rmmod filesys.ko
rootubuntu:/home/ubuntu/storage/filesys# dmesg
[ 3456.748025] init module: register filesys success. [0].
[ 3464.294293] filesys mount ...
[ 3464.294353] filesys mount super_block ...
[ 3474.675258] filesys_inode_lookup
[ 3474.675264] filename: 1.txt
[ 3474.675272] filesys_file_open
[ 3474.675280] filesys_file_release
[ 3487.604103] filesys_file_open
[ 3487.604123] filesys_file_write
[ 3487.604125] filesys_file_write
[ 3487.604126] filesys_file_write
[ 3487.604128] filesys_file_release
[ 3492.519216] filesys_file_open
[ 3492.519237] filesys_file_read len: 1024, offset: 0
[ 3492.519306] filesys_file_release
[ 3498.087984] filesys des ...
...
[ 3501.761647] destory module: unregister filesys.