虚拟文件系统的数据结构

文章目录

  • 虚拟文件系统的数据结构
  • 超级快
  • 挂载描述符
  • 文件系统类型
  • 索引节点
  • 目录项
  • 文件的打开实例和打开文件表

虚拟文件系统的数据结构

虽然不同文件系统类型的物理结构不同,但是虚拟文件系统定义了一套统一的数据结构。

(1)超级块。文件系统的第一块是超级块,描述文件系统的总体信息,挂载文件系统的时候在内存中创建超级块的副本:结构体 super_block。
(2)虚拟文件系统在内存中把目录组织为一棵树。一个文件系统,只有挂载到内存中目录树的一个目录下,进程才能访问这个文件系统。每次挂载文件系统,虚拟文件系统就会创建一个挂载描述符:mount 结构体,并且读取文件系统的超级块,在内存中创建超级块的一个副本。
(3)每种文件系统的超级块的格式不同,需要向虚拟文件系统注册文件系统类型file_system_type,并且实现 mount 方法用来读取和解析超级块。
(4)索引节点。每个文件对应一个索引节点,每个索引节点有一个唯一的编号。当内核访问存储设备上的一个文件时,会在内存中创建索引节点的一个副本:结构体 inode。
(5)目录项。文件系统把目录看作文件的一种类型,目录的数据是由目录项组成的,每个目录项存储一个子目录或文件的名称以及对应的索引节点号。当内核访问存储设备上的一个目录项时,会在内存中创建该目录项的一个副本:结构体 dentry。
(6)当进程打开一个文件的时候,虚拟文件系统就会创建文件的一个打开实例:file结构体,然后在进程的打开文件表中分配一个索引,这个索引称为文件描述符,最后把文件描述符和 file 结构体的映射添加到打开文件表中。

超级快

文件系统的第一块是超级块,用来描述文件系统的总体信息。当我们把文件系统挂载到内存中目录树的一个目录下时,就会读取文件系统的超级块,在内存中创建超级块的副本:结构体 super_block,主要成员如下:

include/linux/fs.h 
struct super_block { struct list_head s_list; dev_t s_dev; unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes;struct file_system_type *s_type; const struct super_operations *s_op; … unsigned long s_flags; unsigned long s_iflags; /*内部 SB_I_* 标志 */ unsigned long s_magic; struct dentry *s_root; … struct hlist_bl_head s_anon; struct list_head s_mounts; struct block_device *s_bdev; struct backing_dev_info *s_bdi; struct mtd_info *s_mtd; struct hlist_node s_instances;void *s_fs_info;}; 

(1)成员 s_list 用来把所有超级块实例链接到全局链表 super_blocks。
(2)成员 s_dev 和 s_bdev 保存文件系统所在的块设备,前者保存设备号,后者指向内存中的一个 block_device 实例。
(3)成员 s_blocksize 是块长度,成员 s_blocksize_bits 是块长度以 2 为底的对数。
(4)成员 s_maxbytes 是文件系统支持的最大文件长度。
(5)成员 s_flags 是标志位。
(6)成员 s_type 指向文件系统类型。
(7)成员 s_op 指向超级块操作集合。
(8)成员 s_magic 是文件系统类型的魔幻数,每种文件系统类型被分配一个唯一的魔幻数。
(9)成员 s_root 指向根目录的结构体 dentry。
(10)成员 s_fs_info 指向具体文件系统的私有信息。
(11)成员 s_instances 用来把同一个文件系统类型的所有超级块实例链接在一起,链表的头节点是结构体 file_system_type 的成员 fs_supers。

超级块操作集合的数据结构是结构体 super_operations,主要成员如下:

include/linux/fs.h 
struct super_operations { struct inode *(*alloc_inode)(struct super_block *sb); void (*destroy_inode)(struct inode *); void (*dirty_inode) (struct inode *, int flags); int (*write_inode) (struct inode *, struct writeback_control *wbc); int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *); void (*put_super) (struct super_block *); int (*sync_fs)(struct super_block *sb, int wait);int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*umount_begin) (struct super_block *);}; 

(1)成员 alloc_inode 用来为一个索引节点分配内存并且初始化。
(2)成员 destroy_inode 用来释放内存中的索引接点。
(3)成员 dirty_inode 用来把索引节点标记为脏。
(4)成员 write_inode 用来把一个索引节点写到存储设备。
(5)成员 drop_inode 用来在索引节点的引用计数减到 0 时调用。
(6)成员 evict_inode 用来从存储设备上的文件系统中删除一个索引节点。
(7)成员 put_super 用来释放超级块。
(8)成员 sync_fs 用来把文件系统修改过的数据同步到存储设备。
(9)成员 statfs 用来读取文件系统的统计信息。
(10)成员 remount_fs 用来在重新挂载文件系统的时候调用。
(11)成员 umount_begin 用来在卸载文件系统的时候调用。

挂载描述符

一个文件系统,只有挂载到内存中目录树的一个目录下,进程才能访问这个文件系统。每次挂载文件系统,虚拟文件系统就会创建一个挂载描述符:mount 结构体。挂载描述符用来描述文件系统的一个挂载实例,同一个存储设备上的文件系统可以多次挂载,每次挂载到不同的目录下。挂载描述符的主要成员如下:

fs/mount.h 
struct mount { struct hlist_node mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount mnt; union { struct rcu_head mnt_rcu; struct llist_node mnt_llist; }; 
#ifdef CONFIG_SMP struct mnt_pcp __percpu *mnt_pcp; 
#else int mnt_count; int mnt_writers; 
#endif struct list_head mnt_mounts; struct list_head mnt_child; struct list_head mnt_instance; const char *mnt_devname; struct list_head mnt_list; … struct mnt_namespace *mnt_ns; struct mountpoint *mnt_mp; struct hlist_node mnt_mp_list;} 

假设我们把文件系统2挂载到目录“/a”下,目录a属于文件系统1。目录a称为挂载点,文件系统2的mount实例是文件系统1的 mount 实例的孩子,文件系统1的 mount 实例是文件系统2的 mount 实例的父亲。

(1)成员 mnt_parent 指向父亲,即文件系统1的 mount 实例。
(2)成员 mnt_mountpoint 指向作为挂载点的目录,即文件系统1的目录a,目录a的dentry实例的成员d_flags设置了标志位DCACHE_MOUNTED。
(3)成员 mnt 的类型如下:

struct vfsmount { struct dentry *mnt_root; struct super_block *mnt_sb; int mnt_flags; 
};

mnt_root 指向文件系统2的根目录,mnt_sb指向文件系统2的超级块。
(4)成员 mnt_hash 用来把挂载描述符加入全局散列表 mount_hashtable,关键字是{父挂载描述符,挂载点}。
(5)成员 mnt_mounts 是孩子链表的头节点。
(6)成员 mnt_child 用来加入父亲的孩子链表。
(7)成员 mnt_instance 用来把挂载描述符添加到超级块的挂载实例链表中,同一个存储设备上的文件系统,可以多次挂载,每次挂载到不同的目录下。
(8)成员 mnt_devname 指向存储设备的名称。
(9)成员 mnt_ns 指向挂载命名空间。
(10)成员 mnt_mp 指向挂载点,类型如下:

struct mountpoint { struct hlist_node m_hash; struct dentry *m_dentry; struct hlist_head m_list; int m_count; 
}; 

m_dentry 指向作为挂载点的目录,m_list 用来把同一个挂载点下的所有挂载描述符链接起来。为什么同一个挂载点下会有多个挂载描述符?这和挂载命名空间有关。
(11)成员 mnt_mp_list 用来把挂载描述符加入同一个挂载点的挂载描述符链表,链表的头节点是成员 mnt_mp 的成员 m_list。

文件系统类型

因为每种文件系统的超级块的格式不同,所以每种文件系统需要向虚拟文件系统注册文件系统类型 file_system_type,并且实现 mount 方法用来读取和解析超级块。结构体 file_system_type 如下:

include/linux/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 
#define FS_RENAME_DOES_D_MOVE 32768 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;}; 

(1)成员 name 是文件系统类型的名称。
(2)方法 mount 用来在挂载文件系统的时候读取并且解析超级块。
(3)方法 kill_sb 用来在卸载文件系统的时候释放超级块。
(4)多个存储设备上的文件系统的类型可能相同,成员 fs_supers 用来把相同文件系统
类型的超级块链接起来。

索引节点

在文件系统中,每个文件对应一个索引节点,索引节点描述两类信息。
(1)文件的属性,也称为元数据(metadata),例如文件长度、创建文件的用户的标识符、上一次访问的时间和上一次修改的时间,等等。
(2)文件数据的存储位置。
每个索引节点有一个唯一的编号。
当内核访问存储设备上的一个文件时,会在内存中创建索引节点的一个副本:结构体inode,主要成员如下:

include/linux/fs.h 
struct inode { umode_t i_mode; unsigned short i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags; #ifdef CONFIG_FS_POSIX_ACL struct posix_acl *i_acl; struct posix_acl *i_default_acl; 
#endif const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping; … unsigned long i_ino; union { const unsigned int i_nlink; unsigned int __i_nlink; }; dev_t i_rdev; loff_t i_size; struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; spinlock_t i_lock; unsigned short i_bytes; unsigned int i_blkbits; blkcnt_t i_blocks; … struct hlist_node i_hash; struct list_head i_io_list; … struct list_head i_lru; struct list_head i_sb_list; struct list_head i_wb_list; union { struct hlist_head i_dentry; struct rcu_head i_rcu; }; u64 i_version; atomic_t i_count; atomic_t i_dio_count; atomic_t i_writecount; 
#ifdef CONFIG_IMA atomic_t i_readcount; 
#endif const struct file_operations *i_fop; struct file_lock_context *i_flctx; struct address_space i_data; struct list_head i_devices; union { struct pipe_inode_info *i_pipe; struct block_device *i_bdev; struct cdev *i_cdev; char *i_link; unsigned i_dir_seq; };void *i_private; }; 

i_mode 是文件类型和访问权限,i_uid 是创建文件的用户的标识符,i_gid 是创建文件的用户所属的组标识符。

i_ino 是索引节点的编号。

i_size 是文件长度;i_blocks 是文件的块数,即文件长度除以块长度的商;i_bytes 是文件长度除以块长度的余数;i_blkbits 是块长度以 2 为底的对数,块长度是 2 的 i_blkbits 次幂。

i_atime(access time)是上一次访问文件的时间,i_mtime(modified time)是上一次修改文件数据的时间,i_ctime(change time)是上一次修改文件索引节点的时间。

i_sb 指向文件所属的文件系统的超级块。

i_mapping 指向文件的地址空间。

i_count 是索引节点的引用计数,i_nlink 是硬链接计数。

如果文件的类型是字符设备文件或块设备文件,那么 i_rdev 是设备号,i_bdev 指向块设备,i_cdev 指向字符设备。

文件分为以下几种类型。
(1)普通文件(regular file):就是我们通常说的文件,是狭义的文件。
(2)目录:目录是一种特殊的文件,这种文件的数据是由目录项组成的,每个目录项
存储一个子目录或文件的名称以及对应的索引节点号。
(3)符号链接(也称为软链接):这种文件的数据是另一个文件的路径。
(4)字符设备文件。
(5)块设备文件。
(6)命名管道(FIFO)。
(7)套接字(socket)。
字符设备文件、块设备文件、命名管道和套接字是特殊的文件,这些文件只有索引节点,没有数据。字符设备文件和块设备文件用来存储设备号,直接把设备号存储在索引节点中。

内核支持两种链接。
(1)软链接,也称为符号链接,这种文件的数据是另一个文件的路径。
(2)硬链接,相当于给一个文件取了多个名称,多个文件名称对应同一个索引节点,索引节点的成员 i_nlink 是硬链接计数。

索引节点的成员 i_op 指向索引节点操作集合 inode_operations,成员 i_fop 指向文件操作集合 file_operations。两者的区别是:inode_operations 用来操作目录(在一个目录下创建或删除文件)和文件属性,file_operations 用来访问文件的数据。索引节点操作集合的数据结构是结构体 inode_operations,主要成员如下:

include/linux/fs.h 
struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); int (*create) (struct inode *,struct dentry *, umode_t, bool); int (*link) (struct dentry *,struct inode *,struct dentry *); int (*unlink) (struct inode *,struct dentry *); int (*symlink) (struct inode *,struct dentry *,const char *); int (*mkdir) (struct inode *,struct dentry *,umode_t); int (*rmdir) (struct inode *,struct dentry *); int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t); int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*setattr) (struct dentry *, struct iattr *); int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,u64 len); int (*update_time)(struct inode *, struct timespec *, int); int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*set_acl)(struct inode *, struct posix_acl *, int); 
} ____cacheline_aligned;

lookup 方法用来在一个目录下查找文件。

系统调用 open 和 creat 调用 create 方法来创建普通文件,系统调用 link 调用 link 方法来创建硬链接,系统调用 symlink 调用 symlink 方法来创建符号链接,系统调用 mkdir 调用 mkdir 方法来创建目录,系统调用 mknod 调用 mknod 方法来创建字符设备文件、块设备文件、命名管道和套接字。

系统调用 unlink 调用 unlink 方法来删除硬链接,系统调用 rmdir 调用 rmdir 方法来删除目录。
系统调用 rename 调用 rename 方法来给文件换一个名字。
系统调用 chmod 调用 setattr 方法来设置文件的属性,系统调用 stat 调用 getattr 方法来读取文件的属性。
系统调用 listxattr 调用 listxattr 方法来列出文件的所有扩展属性。

目录项

文件系统把目录当作文件,这种文件的数据是由目录项组成的,每个目录项存储一个子目录或文件的名称以及对应的索引节点号。

当内核访问存储设备上的一个目录项时,会在内存中创建目录项的一个副本:结构体dentry,主要成员如下:

include/linux/dcache.h 
struct dentry { /* RCU查找访问的字段 */ unsigned int d_flags; seqcount_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr d_name; struct inode *d_inode; unsigned char d_iname[DNAME_INLINE_LEN]; /* 引用查找也访问下面的字段 */ struct lockref d_lockref; const struct dentry_operations *d_op; struct super_block *d_sb; unsigned long d_time; void *d_fsdata; union { struct list_head d_lru; wait_queue_head_t *d_wait; }; struct list_head d_child; struct list_head d_subdirs; /** d_alias和d_rcu可以共享内存*/ union { struct hlist_node d_alias; struct hlist_bl_node d_in_lookup_hash; struct rcu_head d_rcu; } d_u; 
}; 

d_name 存储文件名称,qstr 是字符串的包装器,存储字符串的地址、长度和散列值;如果文件名称比较短,把文件名称存储在 d_iname;d_inode 指向文件的索引节点。

d_parent 指向父目录,d_child 用来把本目录加入父目录的子目录链表。
d_lockref 是引用计数。
d_op 指向目录项操作集合。
d_subdirs 是子目录链表。
d_hash 用来把目录项加入散列表 dentry_hashtable。
d_lru 用来把目录项加入超级块的最近最少使用(Least Recently Used,LRU)链表 s_dentry_lru 中,当
目录项的引用计数减到 0 时,把目录项添加到超级块的 LRU 链表中。
d_alias 用来把同一个文件的所有硬链接对应的目录项链接起来。

以文件“/a/b.txt”为例,目录项和索引节点的关系如图所示。

在这里插入图片描述

目录项操作集合的数据结构是结构体 dentry_ operations,其代码如下:

include/linux/dcache.h 
struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); void (*d_release)(struct dentry *); void (*d_prune)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *, unsigned int); 
} ____cacheline_aligned; 

d_revalidate 对网络文件系统很重要,用来确认目录项是否有效。
d_hash 用来计算散列值。
d_compare 用来比较两个目录项的文件名称。
d_delete 用来在目录项的引用计数减到 0 时判断是否可以释放目录项的内存。
d_release 用来在释放目录项的内存之前调用。
d_iput 用来释放目录项关联的索引节点。

文件的打开实例和打开文件表

当进程打开一个文件的时候,虚拟文件系统就会创建文件的一个打开实例:file 结构体,主要成员如下。

include/linux/fs.h 
struct file { union { struct llist_node fu_llist; struct rcu_head fu_rcuhead; } f_u; struct path f_path; struct inode *f_inode; const struct file_operations *f_op; spinlock_t f_lock; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; struct mutex f_pos_lock; loff_t f_pos; struct fown_struct f_owner; const struct cred *f_cred;void *private_data; … struct address_space *f_mapping; 
} __attribute__((aligned(4)));

(1)f_path 存储文件在目录树中的位置,类型如下:

struct path { struct vfsmount *mnt; struct dentry *dentry; 
}; 

mnt 指向文件所属文件系统的挂载描述符的成员 mnt,dentry 是文件对应的目录项。
(2)f_inode 指向文件的索引节点。
(3)f_op 指向文件操作集合。
(4)f_count 是 file 结构体的引用计数。
(5)f_mode 是访问模式。
(6)f_pos 是文件偏移,即进程当前正在访问的位置。
(7)f_mapping 指向文件的地址空间。

文件的打开实例和索引节点的关系如图所示。

在这里插入图片描述

文件系统信息结构体的主要成员如下:

include/linux/fs_struct.h 
struct fs_struct { … struct path root, pwd; 
};

成员 root 存储进程的根目录,成员 pwd 存储进程的当前工作目录。

假设首先调用系统调用 chroot,把目录“/a”设置为进程的根目录,然后创建子进程,子进程继承父进程的文件系统信息,那么把子进程能看到的目录范围限制为以目录“/a”为根的子树。当子进程打开文件“/b.txt”(文件路径是绝对路径,以“/”开头)时,真实的文件路径是“/a/b.txt”。

假设调用系统调用 chdir,把目录“/c”设置为进程的当前工作目录,当子进程打开文件“d.txt”(文件路径是相对路径,不以“/”开头)时,真实的文件路径是“/c/d.txt”。

打开文件表也称为文件描述符表,数据结构如图所示,结构体 files_struct 是打开文件表的包装器,主要成员如下:

在这里插入图片描述

include/linux/fdtable.h 
struct files_struct { atomic_t count; … struct fdtable __rcu *fdt; struct fdtable fdtab; spinlock_t file_lock ____cacheline_aligned_in_smp; unsigned int next_fd; unsigned long close_on_exec_init[1]; unsigned long open_fds_init[1]; unsigned long full_fds_bits_init[1]; struct file __rcu * fd_array[NR_OPEN_DEFAULT]; 
};

成员 count 是结构体 files_struct 的引用计数。
成员 fdt 指向打开文件表。
当进程刚刚创建的时候,成员 fdt 指向成员 fdtab。运行一段时间以后,进程打开的文件数量超过NR_OPEN_DEFAULT,就会扩大打开文件表,重新分配 fdtable 结构体,成员fdt 指向新的 fdtable 结构体。
打开文件表的数据结构如下:

include/linux/fdtable.h 
struct fdtable { unsigned int max_fds; struct file __rcu **fd; unsigned long *close_on_exec; unsigned long *open_fds; unsigned long *full_fds_bits; struct rcu_head rcu; 
}; 

(1)成员 max_fds 是打开文件表的当前大小,即成员 fd 指向的 file 指针数组的大小。随着进程打开文件的数量增加,打开文件表逐步扩大。
(2)成员 fd 指向 file 指针数组。当进程调用 open 打开文件的时候,返回的文件描述符是 file 指针数组的索引。
(3)成员 close_on_exec 指向一个位图,指示在执行 execve()以装载新程序的时候需要关闭哪些文件描述符。
(4)成员 open_fds 指向文件描述符位图,指示哪些文件描述符被分配。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/1093.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter Ping 检查服务器通讯信号强度

Flutter Ping 检查服务器通讯信号强度 前言 对通讯敏感的程序中,我们除了检查当前网络通道外,还要检查与服务器实际的型号强度。 一般我们采用 ping 的方式返回型号的强度和稳定程度。 dart_ping 包 https://pub-web.flutter-io.cn/packages/dart_ping …

Debezium系列之:监控 Debezium 实例

Debezium系列之:监控 Debezium 实例 一、概述二、实现步骤三、执行四、打开Grafana UI五、关闭集群 Debezium JMX相关的技术博客: Debezium系列之:安装jmx导出器监控debezium指标Debezium系列之:为Debezium集群JMX页面增加监控&a…

「一本通 3.2 练习 6」汽车加油行驶

目录 第一步,二维转一维(此步仅为方便,可以省略) 第二步,建边(啥都行,只要死不了) 第三部,bfs(你要dfs也行) 第一步 第二步 第三步 可CA呢…

fatal error: ‘type_traits‘ file not found错误解决

错误如下 In file included from ../test_opencv_qt/main.cpp:1: In file included from ../../Qt/6.5.1/android_x86_64/include/QtGui/QGuiApplication:1: In file included from ../../Qt/6.5.1/android_x86_64/include/QtGui/qguiapplication.h:7: In file included from .…

在?聊聊浏览器事件循环机制

目录 前言 同步/异步编程模型 同步 异步 JS异步模型 调用栈 任务队列 宏任务队列 微任务队列 微任务API 事件循环 队列优先级 混合队列 事件循环实现 总结 参考文章 Event-Loop可视化工具 前言 JS是单线程语言,在某个时间段只能执行一段代码。这…

SpringBoot 实现 PDF 添加水印有哪些方案?

简介 PDF(Portable Document Format,便携式文档格式)是一种流行的文件格式,它可以在多个操作系统和应用程序中进行查看和打印。在某些情况下,我们需要对 PDF 文件添加水印,以使其更具有辨识度或者保护其版…

.NET 8 Preview 4 中的 ASP.NET Core 更新

作者:Daniel Roth - Principal Program Manager, ASP.NET 翻译:Alan Wang 排版:Alan Wang .NET 8 Preview 4 现已可用,并包括了许多对 ASP.NET Core 的新改进。 以下是本预览版本中的新内容摘要: Blazor 使用 Blazor …

【软考网络管理员】2023年软考网管初级常见知识考点(26)- HTML常见属性标签、表格、表单详解

涉及知识点 Html的概念,html常见标签,html常见属性,html表格,html表单,软考网络管理员常考知识点,软考网络管理员网络安全,网络管理员考点汇总。 原创于:CSDN博主-《拄杖盲学轻声码…

基于matlab使用 YOLO V2深度学习进行多类对象检测(附源码)

一、前言 此示例演示如何训练多类对象检测器。 深度学习是一种强大的机器学习技术,可用于训练强大的多类对象检测器,例如 YOLO v2、YOLO v4、SSD 和 Faster R-CNN。此示例使用该函数训练 YOLO v2 多类室内对象检测器。经过训练的物体检测器能够检测和识…

设计模式—“领域规则”

在特定领域中,某些变化虽然频繁,但可以抽象为某种规则。这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案。 典型模式有:Interpreter Interpreter 动机 在软件构建过程中,如果某一个特定领域的问题比较复杂,类似的结构不断重复出现,…

SpringBoot + Vue前后端分离项目实战 || 五:用户管理功能后续

系列文章: SpringBoot Vue前后端分离项目实战 || 一:Vue前端设计 SpringBoot Vue前后端分离项目实战 || 二:Spring Boot后端与数据库连接 SpringBoot Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接 SpringBoot V…

数字图像处理(三)

目录 实验六、图像分割方法 实验七、图像识别与分类 实验六、图像分割方法 一、实验目的 了解图像分割技术相关基础知识;掌握几种经典边缘检测算子的基本原理、实现步骤理解阈值分割、区域分割等的基本原理、实现步骤。理解分水岭分割方法的基本原理、实现方法。…