07_03文件系统怎么玩的
文件系统
Linux将文件系统分为了两层:VFS(虚拟文件系统)、具体文件系统,如下图所示:
VFS(Virtual Filesystem Switch)称为虚拟文件系统或虚拟文件系统转换,是一个内核软件层,在具体的文件系统之上抽象的一层,用来处理与Posix文件系统相关的所有调用,表现为能够给各种文件系统提供一个通用的接口,使上层的应用程序能够使用通用的接口访问不同文件系统,同时也为不同文件系统的通信提供了媒介。
VFS并不是一种实际的文件系统,它只存在于内存中,不存在任何外存空间,VFS在系统启动时建立,在系统关闭时消亡。
Linux系统中存在很多的文件系统,例如常见的**ext2,ext3,ext4,sysfs,rootfs,proc...**等等。
从用户的使用角度,Linux下的文件系统中宏观上主要分为三层:
1.上层的文件系统的系统调用(System-call ,产生软中断);2.虚拟文件系统VFS(Virtual File System)层,3.挂载到VFS中的各种实际文件系统。
Linux系统的User使用GLIBC啥fopen等函数(POSIX标准、GUN C运行时库)作为应用程序的运行时库,然后通过操作系统,将其转换为系统调用SCI(system-call interface),SCI是操作系统内核定义的系统调用接口,这层抽象允许用户程序的I/O操作转换为内核的接口调用。此时触发0x80软中断进入内核态 再控制VFS进行后续的操作
2. 用户如何透明的去处理文件?
我们知道每个文件系统是独立的,有自己的组织方法,操作方法。那么对于用户来说,不可能所有的文件系统都了解,那么怎么做到让用户透明的去处理文件呢?
例如:我想写文件,那就直接read就OK,不管你是什么文件系统,具体怎么去读!这里就需要引入虚拟文件系统。
所以虚拟文件系统就是:对于一个system,可以存在多个“实际的文件系统”,例如:ext2,ext3,fat32,ntfs...例如我现在有多个分区,对于每一个分区我们知道可以是不同的“实际文件系统”。
例如现在三个磁盘分区分别是:ext2,ext3,fat32,那么每个“实际的文件系统”的操作和数据结构肯定不一样,那么,用户怎么能透明使它们呢?
这个时候就需要VFS作为中间一层!用户直接和VFS打交道。
VFS是一种软件机制,只存在于内存中,每次系统初始化期间Linux都会先在内存中构造一棵VFS的目录树(也就是源码中的namespace)。
VFS主要的作用是对上层应用屏蔽底层不同的调用方法,提供一套统一的调用接口,二是便于对不同的文件系统进行组织管理。
VFS提供了一个抽象层,将POSIX API接口与不同存储设备的具体接口实现进行了分离,使得底层的文件系统类型、设备类型对上层应用程序透明。
例如read,write,那么映射到VFS中就是sys_read,sys_write,那么VFS可以根据你操作的是哪个“实际文件系统”(哪个分区)来进行不同的实际的操作!这个技术也是很熟悉的“钩子结构”技术来处理的。
其实就是VFS中提供一个抽象的struct结构体,然后对于每一个具体的文件系统要把自己的字段和函数填充进去,这样就解决了异构问题(内核很多子系统都大量使用了这种机制)。
Linux虚拟文件系统7大对象
为了对文件系统进行统一的管理与组织,Linux创建了一个公共根目录和全局文件系统树。要访问一个文件系统中的文件,必须先将这个文件系统挂载在全局文件系统树的某个根目录下,这一挂载过程被称作文件系统的挂载,所挂载的目录称为挂载点。
传统的文件系统在磁盘上的布局如下:
文件系统拿取磁盘数据详细分析: 上面这个图,和inode和文件和磁盘的对应关系
磁盘分成了一个个块,上面每个格子都是块,都有自己的块id,那我们的文件系统要读取哪一个块,直接放松对应的块id
就能拿到相应的数据内容
每一个块的大小是固定的,我们先假定为4kb,一个id的大小为64位,那大小就是8字节
如果所有文件大小都不超过4kb,那就能用磁盘块id作为文件名字
但目前情况每个文件大小不确定,需要多个块才能保存,所以就有了inode数据结构,记录了文件和多个块id的对应关系
一个块是4kb,颗粒度有些粗,不可能所有文件都是4kb的倍数给inode记录
所以在inode记录了这个文件对应磁盘的多个块和文件的大小(单位byte)和元数据
同时每个inode应该占多少块呢 inode不能太小,不然存的数据也小,也会限制到文件的大小
假设一个文件有4GB:这个文件就用了100万个块(一个块4kb,块id大小8字节),那存储id就8MB了
如果每个inode预留8MB,其实不划算
所以inode指向多级inode再指向数据块
所有inode都放在一起,保存在磁盘头部的inode表,这个表就像一个大数组每一项都对应一个inode,每个inode都大小一样
这样每个inode都用数组对应的下表作为这个inode的名字:第一个inoded就是inode0,第二个inode就是inode1
这个索引也叫inode号(inode number)
所以看得出来 磁盘中inode号就能表示一个文件
在inode中增加另一个字段来表示这个是目录inode还是普通inode,
把inode号对应的字符串名字记录在它的上级目录中,就形成了目录树
tips:inode大小和块大小完全没关系,通常一个快里面放了多个inode
图片9-4和上面的布局一一对应,超级快后面的分配信息也叫节点位图,说明了哪个块没被使用,哪个inode没被使用仅此而已
小结一下:如果要找/bin/app_run这个文件时候
1.文件系统找到根目录的inode,通常在inode表的第一个
2.根据inode找到对应文件的块id
3.读取磁盘块id获得数据,找到 bin 字符串,通过字符串找到inode号 (这时候理解还在/目录)
4.根据bin的inode号,就能知道bin的目录inode在哪 (这时候理解进入bin目录)
5.同理根据bin的inode知道了bin的数据,分析字符串app_run的inode_Id在哪
6.获得了app_run的数据
上面得知了 文件名是存在它的父目录的 ,和自身的inode没有关系
继续介绍
由上图可知,文件系统的开头通常是由一个磁盘扇区所组成的引导块,该部分的主要目的是用于对操作系统的引导。一般只在启动操作系统时使用。
随后是超级块,超级块主要存放了该物理磁盘中文件系统结构的相关信息,并且对各个部分的大小进行说明,记录了后面四个区的位置。
操作系统mount磁盘的时候也是首先读取超级块来确定后面的位置
最后由i节点位图,逻辑块位图、i节点、逻辑块这几部分分布在物理磁盘上。
Linux为了对超级块,i节点,逻辑块这三部分进行高效的管理,Linux创建了几种不同的数据结构,分别是文件系统类型、inode、dentry等几种。
超级块则是反映了文件系统整体的控制信息。超级块能够以多种的方式存在,对于基于磁盘的文件系统,它以特定的格式存在于磁盘的固定区域(取决于文件系统类型)上。在挂载文件系统时,该超级块中的内容被读入磁盘中,从而构建出位于内存中的新的超级块。
inode则反映了文件系统对象中的一般元数据信息。
dentry则是反映出某个文件系统对象在全局文件系统树中的位置(没有磁盘实体只在内存中)。
linux怎么把不同的分区进行链接呢
我们在linux输入ls可以看见下面的各种目录结构
它其实是一棵目录树(没有画全):
对于linux怎么把各种有不同文件系统的分区进行链接
现在只要记住两个重要链表:
1文件系统链表
2每一个文件系统的mount挂载点链表。
我们看到,Linux系统的文件目录树就是靠上图中的这一系列的链表穿针引线给串在一起的,就像缝制一件衣服一样,最终的成衣就是我们看到的Linux系统目录树,而缝制这件成衣的线以及指导走线的规则便是VFS本身了。
VFS之所有可以将机制大相径庭的完全不同的文件系统对外统一成一个样子,完全就是依靠了它的统一的对POSIX文件调用的接口,该接口的结构看上去是下面的样子:
结构体关系1. 超级块(super block)
超级块:一个超级块对应一个文件系统(已经安装的文件系统类型如ext2,此处是实际的文件系统,不是VFS)。
之前我们已经说了文件系统用于管理这些文件的数据格式和操作之类的,系统文件有系统文件自己的文件系统,同时对于不同的磁盘分区也有可以是不同的文件系统。那么一个超级块对于一个独立的文件系统。保存文件系统的类型、大小、状态等等。
(“文件系统”和“文件系统类型”不一样!一个文件系统类型下可以包括很多文件系统即很多的super_block) 就是之前的图文件系统链表
既然我们知道对于不同的文件系统有不同的super_block,那么对于不同的super_block的操作肯定也是不同的,所以我们在下面的super_block结构中可以看到上面说的抽象的struct结构(例如下面的:struct super_operations):
(linux内核3.14)1246 struct super_block {
1247 struct list_head s_list;//指向超级块链表指针,把所有超级快链接
1248 dev_t s_dev; search index;//块设备的设备号等
1249 unsigned char s_blocksize_bits;
1250 unsigned long s_blocksize;
1251 loff_t s_maxbytes; Max file size
1252 struct file_system_type *s_type;//这个文件系统的文件类型(fat32,ext32)
1253 const struct super_operations *s_op;//指向具体的文件系统用于对超级块的函数操作集合
1254 const struct dquot_operations *dq_op;//某个特定的具体文件系统用于限额操作的函数集合
1255 const struct quotactl_ops *s_qcop;//于配置磁盘限额的的方法
1256 const struct export_operations *s_export_op;
1257 unsigned long s_flags;
1258 unsigned long s_magic;
1259 struct dentry *s_root;
1260 struct rw_semaphore s_umount;
1261 int s_count;
1262 atomic_t s_active;
1263 #ifdef CONFIG_SECURITY
1264 void *s_security;
1265 #endif
1266 const struct xattr_handler **s_xattr;
1267
1268 struct list_head s_inodes; all inodes
1269 struct hlist_bl_head s_anon; anonymous dentries for (nfs) exporting
1270 struct list_head s_mounts; list of mounts; _not_ for fs use1271 struct block_device *s_bdev;
1272 struct backing_dev_info *s_bdi;
1273 struct mtd_info *s_mtd;
1274 struct hlist_node s_instances;
1275 struct quota_info s_dquot; Diskquota specific options
1276
1277 struct sb_writers s_writers;
1278
1279 char s_id[32]; Informational name
1280 u8 s_uuid[16]; UUID
1281
1282 void *s_fs_info; Filesystem private info
1283 unsigned int s_max_links;
1284 fmode_t s_mode;
1285
1286 Granularity of c/m/atime in ns.
1287 Cannot be worse than a second
1288 u32 s_time_gran;
1289
1290
1291 * The next field is for VFS *only*. No filesystems have any business
1292 * even looking at it. You had been warned.
1293
1294 struct mutex s_vfs_rename_mutex; Kludge
1295
1296
1297 * Filesystem subtype. If non-empty the filesystem type field
1298 * in /proc/mounts will be "type.subtype"
1299
1300 char *s_subtype;
1301
1302
1303 * Saved mount options for lazy filesystems using
1304 * generic_show_options()
1305
1306 char __rcu *s_options;
1307 const struct dentry_operations *s_d_op; default d_op for dentries
1308
1309
1310 * Saved pool identifier for cleancache (-1 means none)
1311
1312 int cleancache_poolid;
1313
1314 struct shrinker s_shrink; per-sb shrinker handle
1315
1316 Number of inodes with nlink == 0 but still referenced
1317 atomic_long_t s_remove_count;
1318
1319 Being remounted read-only
1320 int s_readonly_remount;
1321
1322 AIO completions deferred from interrupt context
1323 struct workqueue_struct *s_dio_done_wq;
1324
1325
1326 * Keep the lru lists last in the structure so they always sit on their
1327 * own individual cachelines.
1328
1329 struct list_lru s_dentry_lru ____cacheline_aligned_in_smp;
1330 struct list_lru s_inode_lru ____cacheline_aligned_in_smp;
1331 struct rcu_head rcu;
1332 };
针对上面的操作超级块的函数操作集
struct super_operations {//该函数在给定的超级块下创建并初始化一个新的索引节点对象struct inode *(*alloc_inode)(struct super_block *sb);//释放指定的索引结点 。
void (*destroy_inode)(struct inode *);//VFS在索引节点被修改时会调用此函数。void (*dirty_inode) (struct inode *, int flags);// 将指定的inode写回磁盘。
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 *);//使文件系统的数据元素与磁盘上的文件系统同步,wait参数指定操作是否同步。
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);//获取文件系统状态。把文件系统相关的统计信息放在statfs中
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*umount_begin) (struct super_block *);int (*show_options)(struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
#endif
int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t);
long (*nr_cached_objects)(struct super_block *, int);
long (*free_cached_objects)(struct super_block *, long, int);
};
结构体关系2. 索引节点(inode)
索引节点inode:保存的其实是实际的数据的一些信息,这些信息称为“元数据”(也就是对文件属性的描述)
例如:文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。
真实数据放在数据块里,可以通过索引节点找到数据块( 注意数据分成:元数据+数据本身 )
同时注意:inode有两种,一种是VFS的inode,一种是具体文件系统的inode。前者在内存中,后者在磁盘中。所以每次其实是将磁盘中的inode调进填充内存中的inode,这样才是算使用了磁盘文件inode。
inode怎样生成的?
每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定(现代OS可以动态变化),一般每2KB就设置一个inode。
一般文件系统中很少有文件小于2KB的,所以预定按照2KB分,一般inode是用不完的。所以inode在文件系统安装的时候会有一个默认数量,后期会根据实际的需要发生变化。
注意inode号:inode号是唯一的,表示不同的文件。其实在Linux内部的时候,访问文件都是通过inode号来进行的,所谓文件名仅仅是给用户容易使用的。
当我们打开一个文件的时候,首先,系统找到这个文件名对应的inode号;然后,通过inode号,得到inode信息,最后,由inode找到文件数据所在的block,现在可以处理文件数据了。
inode和文件的关系?
当创建一个文件的时候,就给文件分配了一个inode。一个inode只对应一个实际文件,一个文件也会只有一个inode。inodes最大数量就是文件的最大数量。
接下来看看inode的结构体
527 struct inode {
528 umode_t i_mode; 访问权限控制
529 unsigned short i_opflags;
530 kuid_t i_uid; 使用者的id
531 kgid_t i_gid; 使用组id
532 unsigned int i_flags; 文件系统标志
533
534 #ifdef CONFIG_FS_POSIX_ACL
535 struct posix_acl *i_acl;
536 struct posix_acl *i_default_acl;
537 #endif
538
539 const struct inode_operations *i_op; 索引节点操作表
540 struct super_block *i_sb; 相关的超级块
541 struct address_space *i_mapping; 相关的地址映射
542
543 #ifdef CONFIG_SECURITY
544 void *i_security;
545 #endif
546
547 Stat data, not accessed from path walking
548 unsigned long i_ino; 索引节点号
549
550 * Filesystems may only read i_nlink directly. They shall use the
551 * following functions for modification:
552 *
553 * (set|clear|inc|drop)_nlink
554 * inode_(inc|dec)_link_count
555
556 union {
557 const unsigned int i_nlink;
558 unsigned int __i_nlink; 硬连接数
559 };
560 dev_t i_rdev; 实际设备标识符号
561 loff_t i_size;
562 struct timespec i_atime; 最后访问时间
563 struct timespec i_mtime; 最后修改时间
564 struct timespec i_ctime; 最后改变时间
565 spinlock_t i_lock; i_blocks, i_bytes, maybe i_size
566 unsigned short i_bytes; 使用的字节数
567 unsigned int i_blkbits;
568 blkcnt_t i_blocks; 文件的块数
569
570 #ifdef __NEED_I_SIZE_ORDERED
571 seqcount_t i_size_seqcount;572 #endif
573
574 Misc
575 unsigned long i_state;
576 struct mutex i_mutex;
577
578 unsigned long dirtied_when; jiffies of first dirtying 首次修改时间
579
580 struct hlist_node i_hash; hash值,提高查找效率
581 struct list_head i_wb_list; backing dev IO list
582 struct list_head i_lru; inode LRU list 未使用的inode
583 struct list_head i_sb_list; 链接一个文件系统中所有inode的链表
584 union {
585 struct hlist_head i_dentry; 目录项链表
586 struct rcu_head i_rcu;
587 };
588 u64 i_version;
589 atomic_t i_count; 引用计数
590 atomic_t i_dio_count;
591 atomic_t i_writecount; 写者计数
592 const struct file_operations *i_fop; former ->i_op->default_file_ops 文件操作
593 struct file_lock *i_flock; 文件锁链表
594 struct address_space i_data; 表示被inode读写的页面
595 #ifdef CONFIG_QUOTA
596 struct dquot *i_dquot[MAXQUOTAS]; 节点的磁盘限额
597 #endif
598 struct list_head i_devices; 设备链表(共用同一个驱动程序的设备形成的链表。)
599 union {
600 struct pipe_inode_info *i_pipe; 管道信息
601 struct block_device *i_bdev; 块设备驱动节点
602 struct cdev *i_cdev; 字符设备驱动节点
603 };
604
605 __u32 i_generation; 索引节点版本号
606
607 #ifdef CONFIG_FSNOTIFY
608 __u32 i_fsnotify_mask; all events this inode cares about
609 struct hlist_head i_fsnotify_marks;
610 #endif
611
612 #ifdef CONFIG_IMA
613 atomic_t i_readcount; struct files open RO
614 #endif
615 void *i_private; fs or device private pointer 用户私有数据
616 };
对inode操作的函数操作集
目前感觉是看后面的图 这些结构体的关系
进程先通过 file_struct结构体找 在进程的哪个fd[]中,找到对应file结构体
再找到dentry结构体再找到inode结构体,调用里面的i_fop的函数,触发系统调用的80软中断
最后碰到了内核态和VFS
节点方法struct inode_operations {//lookup()查找指定文件的dentry
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);int (*readlink) (struct dentry *, char __user *,int);
void (*put_link) (struct dentry *, struct nameidata *, void *);int (*create) (struct inode *,struct dentry *, umode_t, bool);//create()如果该inode描述一个目录文件,那么当在该目录下创建或打开一个文件时,内核必须为这个文件创建一个inode。//VFS通过调用该inode的i_op->create()函数来完成上述新inode的创建。// 该函数的第一个参数为该目录的 inode,第二个参数为要打开新文件的dentry,//第三个参数是对该文件的访问权限。如果该inode描述的是一个普通文件,那么该inode永远都不会调用这个create函数
int (*link) (struct dentry *,struct inode *,struct dentry *);//link()用于在指定目录下创建一个硬链接。这个link函数最终会被系统调用link()调用。//该函数的第一个参数是原始文件的dentry,第二个参数即为上述指定目录的inode,第三个参数是链接文件的dentry。
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);//symlink ()在某个目录下新建mkdir()在指定的目录下创建一个子目录,当前目录的inode会调用i_op->mkdir()。该函数会被系统调用mkdir()调用。//第一个参数即为指定目录的inode,第二个参数为子目录的dentry,第三个参数为子目录权限;
int (*mkdir) (struct inode *,struct dentry *,umode_t);//mknod()在指定的目录下创建一个特殊文件,比如管道、设备文件或套接字等。
int (*rmdir) (struct inode *,struct dentry *);//rmdir ()从inode所描述的目录中删除一个指定的子目录时,该函数会被系统调用rmdir()最终调用
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,struct inode *, struct dentry *);
int (*rename2) (struct inode *, struct dentry *,struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *);
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;
结构体关系3.目录项(dentry)
目录项是描述文件的逻辑属性,只存在于内存中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。
注意不管是文件夹还是最终的文件,都是属于目录项,所有的目录项在一起构成一颗庞大的目录树。
例如:open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目录项,VFS在查找的时候,根据一层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
注意:目录也是一种文件(所以也存在对应的inode)。打开目录,实际上就是打开目录文件。
108 struct dentry {
109 RCU lookup touched fields
110 unsigned int d_flags; protected by d_lock
111 seqcount_t d_seq; per dentry seqlock
112 struct hlist_bl_node d_hash; lookup hash list
113 struct dentry *d_parent; parent directory 父目录
114 struct qstr d_name;
115 struct inode *d_inode; Where the name belongs to - NULL is
116 * negative 与该目录项关联的inode
117 unsigned char d_iname[DNAME_INLINE_LEN]; small names 短文件名
118
119 Ref lookup also touches following
120 struct lockref d_lockref; per-dentry lock and refcount
121 const struct dentry_operations *d_op; 目录项操作
122 struct super_block *d_sb; The root of the dentry tree 这个目录项所属的文件系统的超级块(目录项树的根)
123 unsigned long d_time; used by d_revalidate 重新生效时间
124 void *d_fsdata; fs-specific data 具体文件系统的数据
125
126 struct list_head d_lru; LRU list 未使用目录以LRU 算法链接的链表
127
128 * d_child and d_rcu can share memory
129
130 union {
131 struct list_head d_child; child of parent list 目录项通过这个加入到父目录的d_subdirs中
132 struct rcu_head d_rcu;
133 } d_u;
134 struct list_head d_subdirs; our children 本目录的所有孩子目录链表头
135 struct hlist_node d_alias; inode alias list 索引节点别名链表
136 };
一个有效的dentry结构必定有一个inode结构,这是因为一个目录项要么代表着一个文件,要么代表着一个目录,而目录实际上也是文件。
所以,只要dentry结构是有效的,则其指针d_inode必定指向一个inode结构。但是inode却可以对应多个。
整个结构其实就是一棵树,目录其实就是文件(kobject、inode)再加上一层封装,这里所谓的封装主要就是增加两个指针,一个是指向父目录,一个是指向该目录所包含的所有文件(普通文件和目录)的链表头。
这样才能有我们的目录操作(比如回到上次目录,只需要一个指针步骤【…】,而进入子目录需要链表索引需要多个步骤)
下面的dentry相关的操作函数集(inode里面已经包含了mkdir,rmdir,mknod之类的操作了)
struct dentry_operations {该函数判断目录对象是否有效。VFS准备从dcache中使用一个目录项时,会调用该函数.
int (*d_revalidate)(struct dentry *, unsigned int);
int (*d_weak_revalidate)(struct dentry *, unsigned int);该目录生成散列值,当目录项要加入到散列表时,VFS要调用此函数。
int (*d_hash)(const struct dentry *, struct qstr *); 该函数来比较name1和name2这两个文件名。使用该函数要加dcache_lock锁。
int (*d_compare)(const struct dentry *, const struct dentry *,unsigned int, const char *, const struct qstr *);当d_count=0时,VFS调用次函数。使用该函数要叫 dcache_lock锁。
int (*d_delete)(const struct dentry *);当该目录对象将要被释放时,VFS调用该函数。
void (*d_release)(struct dentry *);
void (*d_prune)(struct dentry *);当一个目录项丢失了其索引节点时,VFS就掉用该函数。
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool);
} ____cacheline_aligned;
结构体关系4.文件对象(file)
文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。但是由于文件是唯一的,那么inode就是唯一的,目录项也是定的!
进程其实是通过文件描述符来操作文件的,每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。
一般情况下打开文件后,打开位置都是从0开始,除非一些特殊情况。Linux用file结构体来保存打开的文件的位置,所以file称为打开的文件描述。file结构形成一个双链表,称为系统打开文件表。
结构体:file
775 struct file {
776 union {
777 struct llist_node fu_llist; 每个文件系统中被打开的文件都会形成一个双链表
778 struct rcu_head fu_rcuhead;
779 } f_u;
780 struct path f_path;
781 #define f_dentry f_path.dentry
782 struct inode *f_inode; cached value
783 const struct file_operations *f_op; 指向文件操作表的指针
784
785
786 * Protects f_ep_links, f_flags.
787 * Must not be taken from IRQ context.
788
789 spinlock_t f_lock;
790 atomic_long_t f_count; 文件对象的使用计数
791 unsigned int f_flags; 打开文件时所指定的标志
792 fmode_t f_mode; 文件的访问模式(权限等)
793 struct mutex f_pos_lock;
794 loff_t f_pos; 文件当前的位移量
795 struct fown_struct f_owner;
796 const struct cred *f_cred;
797 struct file_ra_state f_ra; 预读状态
798
799 u64 f_version; 版本号
800 #ifdef CONFIG_SECURITY
801 void *f_security; 安全模块
802 #endif
803 needed for tty driver, and maybe others
804 void *private_data; 私有数据
805
806 #ifdef CONFIG_EPOLL
807 Used by fs/eventpoll.c to link all the hooks to this file
808 struct list_head f_ep_links;
809 struct list_head f_tfile_llink;
810 #endif #ifdef CONFIG_EPOLL
811 struct address_space *f_mapping; 页缓存映射
812 #ifdef CONFIG_DEBUG_WRITECOUNT
813 unsigned long f_mnt_write_state;
814 #endif
815 } __attribute__((aligned(4))); lest something weird decides that 2 is OK
重点解释一些重要字段:
首先,f_flags、f_mode和f_pos代表的是这个进程当前操作这个文件的控制信息。
这个非常重要,因为对于一个文件,可以被多个进程同时打开,那么对于每个进程来说,操作这个文件是异步的,所以这个三个字段就很重要了。
对于引用计数f_count,当我们关闭一个进程的某一个文件描述符时候,其实并不是真正的关闭文件,仅仅是将f_count减一,当f_count=0时候,才会真的去关闭它。
对于dup,fork这些操作来说,都会使得f_count增加,具体的细节,以后再说。f_op也是很重要的!是涉及到所有的文件的操作结构体。例如:用户使用read,最终都会调用file_operations中的读操作,而file_operations结构体是对于不同的文件系统不一定相同。里面一个重要的操作函数式release函数,当用户执行close时候,其实在内核中是执行release函数,这个函数仅仅将f_count减一,这也就解释了上面说的,用户close一个文件其实是将f_count减一。只有引用计数减到0才关闭文件。
注意:对于“正在使用”和“未使用”的文件对象分别使用一个双向链表进行管理。
另一个结构体 files_struct
上面的file只是对一个文件而言,对于一个进程(用户)来说,可以同时处理多个文件,所以需要另一个结构来管理所有的files!
即:用户打开文件表--->files_struct
172 struct files_struct {
173 atomic_t count;
174 rwlock_t file_lock; Protects all the below members. Nests inside tsk->alloc_lock
175 int max_fds;
176 int max_fdset;
177 int next_fd;
178 struct file ** fd; current fd array
179 fd_set *close_on_exec;
180 fd_set *open_fds;
181 fd_set close_on_exec_init;
182 fd_set open_fds_init;
183 struct file * fd_array[NR_OPEN_DEFAULT];
184 };
解释一些字段:
字段描述count引用计数file_lock锁,保护下面的字段max_fds当前文件对象的最大的数量max_fdset文件描述符最大数next_fd已分配的最大的文件描述符+1fd指向文件对象指针数组的指针,一般就是指向最后一个字段fd_arrray,当文件数超过NR_OPEN_DEFAULT时候,就会重新分配一个数组,然后指向这个新的数组指针!close_on_exec执行exec()时候需要关闭的文件描述符open_fds指向打开的文件描述符的指针close_on_exec_init执行exec()时候需要关闭的文件描述符初始化值open_fds_init文件描述符初值集合fd_array文件对象指针的初始化数组fs_struct
那同时因为打开了文件 也会有对应的操作函数file_operations
file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};
应该是用户空间的调用总结:
每个进程都有自己的namespace。
fs_struct用于表示进程与文件系统之间的结构关系,比如当前的工作目录,进程的根目录等等。
files_struct 用于表示当前进程打开的文件。
而对于每一个打开的文件,由file对象来表示。
Linux中,常常用文件描述符(file descriptor)来表示一个打开的文件,这个描述符的值往往是一个大于或等于0的整数。而这个整数,其实就是在files_struct中file数组fd的下标。对于所有打开的文件, 这些文件描述符会存储在open_fds的位图中。
从图中可知:进程通过task_struct中的一个域files->files_struct 来了解它当前所打开的文件对象;而我们通常所说的文件描述符其实是进程打开的文件对象数组的索引值。文件对象通过域f_dentry找到它对应的dentry对象,再由dentry对象的域d_inode找到它对应的索引节点(通过索引节点又可以得到超级块的信息,也就可以得到最终操作文件的方法,在open文件的时候就是使用这样一个过程),这样就建立了文件对象与实际的物理文件的关联。文件对象所对应的文件操作函数列表是通过索引节点的域i_fop得到的,而i_fop最终又是通过struct super_operations *s_op来初始化的。
VFS文件系统中的inode和dentry与实际文件系统的inode和dentry有一定的关系,但不能等同。
真实磁盘文件的inode是存在于物理外存上的,但VFS中的inode和dentry是存在于内存中的,系统读取外存中的inode信息进行一定加工后,生成内存中的inode和dentry。
虚拟的文件系统也具有inode和dentry结构,只是这是系统根据相应的规则生成的,不存在于实际外存中。
用户空间和内核空间和系统调用和泽呢么知道这个文件的file_operation
http://wed.xjx100.cn/news/162453.html?action=onClick 目前还在看这个文章
https://blog.csdn.net/marlos/article/details/130234691
上面这个文章是vfs文件系统,怎么被应用open调用再调用到不同的文件系统的
估计还要从之前 迅维的文档看看驱动创建的时候 有个file_opeartion是不是能关联起来
https://blog.csdn.net/qq_45172832/article/details/129242285
https://blog.csdn.net/wenfengliaoshuzhai/article/details/131776900
上面两个文章都是说驱动文件的open怎么和应用程序的open对应起来
https://blog.csdn.net/uunubt/article/details/127278292
上面是主要参考的文章
分手了时间真多
相关文章:
07_03文件系统怎么玩的
文件系统 Linux将文件系统分为了两层:VFS(虚拟文件系统)、具体文件系统,如下图所示: VFS(Virtual Filesystem Switch)称为虚拟文件系统或虚拟文件系统转换,是一个内核软件层&#…...
php实战案例记录(24)不要键名只保留值的算法
php中对数组 $originalArray array( “name” > “John”, “age” > 25, “city” > “New York” )仅去除键名保留值的算法是什么 array_values() 函数 在 PHP 中,你可以使用 array_values() 函数来去掉数组的键名。该函数会返回一个新数组,…...
【交付高质量,用户高增长】-用户增长质量保证方法论 | 京东云技术团队
前言 俗话说,“测试是质量的守护者”,但单凭测试本身却远远不够。大多数情况下,测试像“一面镜子”,照出系统的面貌,给开发者提供修改代码的依据,这个“照镜子”的过程,就是质量评估的过程&…...
LMI FocalSpec 3D线共焦传感器 使用笔记1
一.硬件介绍 以上特别注意: 屏蔽线必须接地,因为在现场实际调试中,使用软件调试发现经常 弹窗 传感器丢失警告!! 以上 Position LED 的灯被钣金挡住,无法查看异常现象,能否将指示灯设置在软件界面上? 需要确认是软触发还是硬触发,理论上 硬触发比软触发速度要快.(我们目前使用…...
四、RocketMQ发送普通消息、批量消息和延迟消息
Producer发送普通消息的方式 1.同步发送消息 同步消息代表发送端发送消息到broker之后,等待消息发送结果后,再次发送消息 实现步骤 创建生产端,声明在哪个生产组注册NameServer地址构建Message实体,指定topic、tag、body启动…...
idea自定义 postfix completion提高编码效率
postfix completion的使用 详情见: https://www.cnblogs.com/expiator/p/17380495.html 自定义 postfix completion List、 String 初始化list: key: list表达式: List<$EXPR$> $END$List new ArrayList<>();字符串判空&…...
解锁学习电路设计的正确姿势!
...
【Linux】 ps命令使用
作为一个后端的程序员,我们经常用到ps -ef | grep XXX 到底什么事ps呢。 下面我们一起学习一下吧、 ps (英文全拼:process status)命令用于显示当前进程的状态,类似于 windows 的任务管理器。 ps命令 -Linux手册页 …...
打造高效的分布式爬虫系统:利用Scrapy框架实现
在大数据时代的今天,爬虫系统成为了获取和分析海量数据的重要工具。本文将介绍如何使用Scrapy框架来构建一个高效的分布式爬虫系统,以加速数据采集过程和提高系统的可扩展性。 Scrapy框架简介 Scrapy是一个基于Python的强大的开源网络爬虫框架ÿ…...
SpringCloud组件Ribbon的IRule的问题排查
最近很久没有写文章啦,刚好遇到了一个问题,其实问题也挺简单,但是还是得对源码有一定了解才能够发现。 最近在实现一个根据请求流量的标签,将请求转发到对应的节点,其实和俗称的灰度请求有点相似, 实现思…...
比较完整一些chatGPT项目代码(权威)
https://gitee.com/zccbbg/chatgpt-springboot-service yml中的配置文件无法读取,前端访问比较困难。...
Python - 生成二维码、条形码
二维码 import qrcode# 要生成的文本或链接 data "要生成的文本或链接"# 创建QR码对象 qr qrcode.QRCode(version1, # 版本号,通常设置为1error_correctionqrcode.constants.ERROR_CORRECT_L, # 错误修正级别box_size10, # 每个小方块的像素大小bor…...
8+纯生信,多组机器学习+分型探讨黑色素瘤发文思路。
今天给同学们分享一篇泛癌多组机器学习分型的生信文章“Comprehensive characterisation of immunogenic cell death in melanoma revealing the association with prognosis and tumor immune microenvironment”,这篇文章于2022年9月23日发表在Front Immunol 期刊…...
GPU高性能面试-写一个ReduceKernel
要求写一个reduceKernel 要求给出Kerne的完整调用: 1. 进行一维reduce 可以写一个最基础的,仅仅实现基础功能就行 使用share mem进行功能优化 使用shuffles指令完成block reduce操作 2.实现二维reduce...
深入探索STARK的安全性和可靠性——STARKs全面安全分析
1. 引言 non-interactive STARKs,起源于Interactive Oracle Proofs (IOPs),然后通过random oracle模式转换为非交互式。StarkWare团队 ethSTARK Documentation – Version 1.2(2023年7月)论文做了更新,给出了完整具体…...
WPF 控件分辨率自适应问题
WPF 控件分辨率自适应时,我首先想到的是使用ViewBox控件来做分辨率自适应。 ViewBox这个控件通常和其他控件结合起来使用,是WPF中非常有用的控件。定义一个内容容器。ViewBox组件的作用是拉伸或延展位于其中的组件,以填满可用空间࿰…...
CANoe创建仿真工程
CANoe创建仿真工程 写在前面仿真工程的创建创建工程添加CAN数据库添加系统变量创建面板创建网络节点为节点添加代码工程运行测试总结 写在前面 Canoe的安装不是特别方便,我是参加了松勤的培训课程,不仅需要安装软件还需要安装驱动,刚刚学习的…...
Scanner 输入回车跳不出循环的解决方法
题目要求: 输入一行内容包含字符串和数字,将字符串与数字分别提取。 解决方法: 可以使用两个Scanner对象,一个用来键入数据,另外一个用来对数据进行操作,以此来解决输入“回车”跳不出while循环的问题。 i…...
docker 启动 mysql 通过防火墙设置端口无法访问解决方案
1、问题描述:通过 docker compose 启动mysql服务,然而在防火墙添加了3306端口后却无法访问,但是关闭防火墙后又可以访问mysql数据库。 解决方案: 重启 docker 后解决:systemctl restart docker 如果没有解决问题则执…...
智能制造优化,RFID生产线管理系统解决方案
一、背景介绍 随着全球经济的发展,传统制造业面临着越来越高的成本和低利润的挑战,为了提升企业的整体利润率,优化管理流程成为必要的手段之一,在传统的制造企业中,生产线通常采用单件流生产模式,但这种模…...
【Mybatis】基于Mybatis插件+注解,实现敏感数据自动加解密
一、介绍 业务场景中经常会遇到诸如用户手机号,身份证号,银行卡号,邮箱,地址,密码等等信息,属于敏感信息,需要保存在数据库中。而很多公司会会要求对数据库中的此类数据进行加密存储。 敏感数据…...
【特纳斯电子】基于物联网的指纹密码锁系统设计-实物设计
资料下载链接:基于物联网的指纹密码锁系统设计-实物设计 - 电子校园网 编号: T3732205M-SW 设计简介: 本设计是基于单片机的指纹密码锁,主要实现以下功能: 1、可通过密码解锁 2、可通过云平台解锁 3、可通过指纹解…...
【牛客面试必刷TOP101】Day9.BM37 二叉搜索树的最近公共祖先和BM42 用两个栈实现队列
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:牛客面试必刷TOP101 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!&…...
10.12 校招 实习 内推 面经
绿*泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表格 1、校招 | 2024届秋招,美团哪些校招岗位最缺人?(内推) 校招 | 2024届秋招,美团哪些校招岗位最缺人?(内推&…...
redis 生成流水工具类
使用redis存储流水号,代码如下: import cn.hutool.core.date.DateUtil; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;Component public class RedisSerialUtil {private RedisTemplate…...
BGP服务器租用腾讯云和阿里云价格对比
BGP云服务器像阿里云和腾讯云均是BGP多线网络,速度更快延迟更低,阿里云BGP服务器2核2G3M带宽优惠价格108元一年起,腾讯云BGP服务器2核2G3M带宽95元一年起,阿腾云atengyun.com分享更多云服务器配置如2核4G、4核8G、8核16G等配置价格…...
PyTorch 深度学习之多分类问题Softmax Classifier(八)
1. Revision: Diabetes dataset 2. Design 10 outputs using Sigmoid? 2.1 Output a Distribution of prediction with Softmax 2.2 Softmax Layer Example, 2.3 Loss Function-Cross Entropy Cross Entropy in Numpy Cross Entropy in PyTorch 注意交叉熵损失,最…...
抖音直播招聘小程序可以增加职位展示,提升转化率,增加曝光度
抖音直播招聘报白是指进入抖音的白名单,允许在直播间或小视频中发布招聘或找工作等关键词。否则会断播、不推流、限流。抖音已成为短视频流量最大的平台,但招聘企业数量较少。抖音招聘的优势在于职位以视频、直播方式展示,留存联系方式更加精…...
论文阅读之《Learn to see in the dark》
Learning to See in the Dark-CVPR2018 Chen ChenUIUC(伊利诺伊大学厄巴纳-香槟分校) Qifeng Chen, Jia Xu, Vladlen Koltun Intel Labs(英特尔研究院) 文章链接:https://arxiv.org/pdf/1805.01934.pdfhttps://arxiv.org/pdf/1805.01934.p…...
Docker 生成自定义镜像并使用Docker Compose部署
Docker 生成自定义镜像并使用Docker Compose部署 Docker Compose 是一个用于定义和运行多个 Docker 容器的工具,可以轻松管理复杂的应用程序。本文将介绍如何在 Docker Compose 中使用自定义 Docker 镜像,并提供了生成自定义 Docker 镜像的步骤。 步骤…...
设计模式~调停者(中介者)模式(Mediator)-21
调停者(中介者)模式(Mediator) (1)优点 (2)缺点 (3)使用场景 (4)注意事项: (5)应用实例: 代码 调停者&a…...
计算机毕业设计选什么题目好?springboot 医院门诊在线预约挂号系统
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...
linux中使用ps查看进程的所有线程
在 Linux 系统中,可以使用 ps 命令和 ps H 命令结合来查看进程的线程信息。ps 命令用于显示系统中当前运行的进程信息,而 ps H 命令则可以显示进程中的所有线程。 使用以下命令可以查看指定进程的所有线程信息: ps H -T <PID>将 替换…...
本、硕、博区别真的辣么大吗?
61: 发际线已经说明了一切…… Super Mario: 小学,老师告诉学生:“森林里有只老虎,已经被我关在笼子里,我会带你去那个地方,然后给你一把猎枪,告诉你猎枪怎么用,并开枪…...
[Spring] SpringMVC 简介(一)
目录 一、SpringMVC 简介 1、什么是 MVC 2、什么是 SpringMVC 3、SpringMVC 实现原理 4、SpringMVC 的特点 二、简单案例 1、引入依赖 2、在 web.xml 中配置前端控制器 DispatcherServlet 3、创建 SpringMVC 的配置文件 4、创建请求控制器 5、测试页面 6、访问不到 …...
机器学习基础之《回归与聚类算法(2)—欠拟合与过拟合》
一、背景 1、上一篇说正规方程的时候,实际情况中使用很少,主要原因它不能解决过拟合。 2、训练集上表现的好,测试集上表现不好—过拟合 二、欠拟合和过拟合 1、欠拟合 训练集:有3个训练集,告诉机器都是天鹅 机器学…...
flutter dio 请求封装(空安全)
一、添加依赖 dio: ^5.3.2二、请求封装 class HttpHelper {static Dio? mDio;static BaseOptions? options;static HttpHelper? httpHelper;CancelToken cancelToken CancelToken();static const String GET get;static const String POST post;static const String PU…...
chatgpt GPT-4V是如何实现语音对话的
直接上代码 https://chat.openai.com/voice/get_token 1. 请求内容 Request:GET /voice/get_token HTTP/1.1 Host: ios.chat.openai.com Content-Type: application/json Cookie: _puiduser***Fc9T:16962276****Nph%2Fb**SU%3D; _uasid"Z0FBQUF***nPT0"; __cf_bmBUg…...
C++项目-求水仙花数
求水仙花数 #include <iostream> using namespace std;int main() {int n 100;do {int a 0;int b 0;int c 0;a n % 10; //个位b n / 10 % 10; //十位c n / 100 % 10; //百位if (a * a * a b * b * b c * c * c n) {cout << n << endl;}…...
从零开始基于LLM构建智能问答系统的方案
本文首发于博客 LLM应用开发实践 一个完整的基于 LLM 的端到端问答系统,应该包括用户输入检验、问题分流、模型响应、回答质量评估、Prompt 迭代、回归测试,随着规模增大,围绕 Prompt 的版本管理、自动化测试和安全防护也是重要的话题&#x…...
Android---Synchronized 和 ReentrantLock
Synchronized 基本使用 1. 修饰实例方法 public class SynchronizedMethods{private int sum 0;public synchronized void calculate(){sum sum 1;} } 这种情况下的锁对象是当前实例对象,因此只有同一个实例对象调用此方法才会产生互斥效果;不同的…...
【解题报告】牛客挑战赛70 maimai
题目链接 这个挑战赛的 F F F是我出的,最后 zhoukangyang 爆标了。。。orzorz 记所有有颜色的边的属性集合 S S S 。 首先在外层容斥,枚举 S ∈ [ 0 , 2 w ) S\in [0,2^w) S∈[0,2w),计算被覆盖的的边中不包含 S S S 中属性,…...
算启新程 智享未来 | 紫光展锐携手中国移动共创数智未来
10月11日-13日,2023年中国移动全球合作伙伴大会在广州举行,此次大会以“算启新程 智享未来”为主题,与合作伙伴一起共商融合创新,共创数智未来。作为中国移动每年规模最大、最具影响力的盛会,吸引了数百家世界500强企业…...
thinkphp5.1 获取缓存cache(‘cache_name‘)特别慢,php 7.0 unserialize 特别慢
thinkphp5.1 获取缓存cache(‘cache_name’)特别慢,php 7.0 unserialize 特别慢 场景: 项目中大量使用了缓存,本地运行非常快,二三百毫秒,部署到服务器后 一个表格请求就七八秒,最初猜想是数据库查询慢&am…...
【Linux】UNIX 术语中,换页与交换的区别和Linux 术语中,换页与交换的区别?
UNIX换页和交换的区别 在UNIX中,换页(Paging)是一种内存管理技术,用于在程序运行时动态地将其代码和数据从磁盘加载到内存中。当程序需要访问的页面不在内存中时,就会发生页错误(page error)&a…...
零基础学python之集合
文章目录 集合1、创建集合2、集合常见操作方法2、1 增加数据2、2 删除数据2、3 查找数据 3、总结 集合 目标 创建集合集合数据的特点集合的常见操作 1、创建集合 创建集合使用{}或set(), 但是如果要创建空集合只能使用set(),因为{}用来创建空字典。 …...
PromptScript:轻量级 DSL 脚本,加速多样化的 LLM 测试与验证
TL;DR 版本 PromptScript 是一个轻量级的 Prompt 调试用的 DSL (Yaml)脚本,以用于快速使用、构建 Prompt。 PromptScript 文档:https://framework.unitmesh.cc/prompt-script Why PromptScript ? 几个月前&…...
强化学习(Reinforcement Learning)与策略梯度(Policy Gradient)
写在前面:本篇博文的内容来自李宏毅机器学习课程与自己的理解,同时还参考了一些其他博客(懒得放链接)。博文的内容主要用于自己学习与记录。 1 强化学习的基本框架 强化学习(Reinforcement Learning, RL)主要由智能体(Agent/Actor)、环境(Environment)、…...
JUC之ForkJoin并行处理框架
ForkJoin并行处理框架 Fork/Join 它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。 类似于mapreduce 其实,在Java 8中引入的并行流计算,内部就是采用的ForkJoinPool来实现…...
【牛客面试必刷TOP101】Day8.BM33 二叉树的镜像和BM36 判断是不是平衡二叉树
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:牛客面试必刷TOP101 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!&…...