Linux 文件系统原理 / 虚拟文件系统VFS

news2024/9/28 21:19:44

Linux 文件系统原理 / 虚拟文件系统VFS

    • 虚拟文件系统 VFS
      • VFS 定义
      • VFS 的对象演绎
        • 超级块 super_block
        • 索引节点 inode
        • 目录项 dentry
        • 文件 file
    • 打开文件流程
    • 参考文献

虚拟文件系统 VFS

VFS 定义

VFS是一个抽象层,其向上提供了统一的文件访问接口,而向下则兼容了各种不不同类型的文件系统。不仅仅是诸如Ext2、Ext3、Ext4、XFS、windows家族的NTFS和Btrfs等常规意义上的文件系统,还可以是比如上图的proc等伪文件系统和设备,也可以是诸如NFS、CIFS等网络文件系统。

VFS 采用标准的Linux系统调用读写位于不同物理介质上的不同文件系统,即为各类文件系统提供了一个统一的操作界面和应用编程接口, VFS是一个内核软件层VFS是一个可以让open()、read()、write()等系统调用不用关心底层的存储介质和文件系统类型就可以工作的 抽象层 ,如下图所示:

img

VFS 的对象演绎

虚拟文件系统在磁盘中并没有对应的存储的信息。尽管 Linux 支持多达几十种文件系统,但这些真实的文件系统并不是一下子都挂在系统中的,它们实际上是按需挂载的。另外,这些实的文件系统只有安装到系统中,VFS 才予以认可,也就是说, VFS 只管理挂载到系统中的实际文件系统

VFS 有 4 个主要对象:

  • 超级块(Superblock) :存放系统中已安装文件系统的有关信息。

  • 文件索引节点(inode) :存放关于具体文件的一般信息。

  • 目录项对象(dentry) :存放目录项与对应文件进行链接的信息。

    路径中的每一个部分被称作目录项,例如 /home/clj/myfile 中,根目录是 / ,而 home,clj 和文件 myfile 都是目录项。

  • 文件对象(file) :存放打开文件与进程之间进行交互的有关信息。

超级块是对一个 文件系统 的描述索引节点是对一个 文件物理属性 的描述而目录项是对一个 文件逻辑属性 的描述

超级块 super_block

超级块用来描述整个文件系统的信息,包括文件系统的大小、有多少是空的和已经填满的占多少,以及他们各自的总数和其他诸如此类的信息。超级块占用1号物理块,就是文件系统的控制块 ,要 使用一个分区来进行数据访问,那么第一个要访问的就是超级块 。所以,超级块坏了,那磁盘也就基本没救了。

当内核在对一个文件系统进行初始化和注册时 在内存为其 分配一个超级块 。此时的超级块为 VFS 超级块 。也就是说,VFS 超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时被自动删除。 VFS 超级块只存放在内存中

对于每个具体的文件系统来说,都有各自的超级块,如 Ext2 超级块和 Ext3 超级块,它存放在磁盘上,内容包括:文件系统的大小、空闲块数目、空闲块索引表、空闲i节点数目、空闲i节点索引表、封锁标记等。超级块是系统为文件分配存储空间、回收存储空间的依据。这一部分的拓扑结构如下图:

7ed1504eefb36427.png

其中 Block Group 存储的各部分含义如下:

  • indoe bitmap (indoe对照表): 用来记录当前文件系统的indoe哪些是已经使用的,哪些又是未使用的。
  • block bitmap (块对照表): 用来记录当前文件系统哪些block已经使用,哪些又是未使用的。
  • inode table (inode 表格):inode是用来记录文件的属性以及该文件实际数据所在的block的号码。
  • GDT(Global Descriptor Table):用来描述每个block group开始和结束的block号码以及每个区段位于哪一个block号码之间。相当于文件系统描述的是每个block group的信息。
  • data blocks:数据块,用于存放数据

超级块的数据结构定义如下:

struct super_block
{
    dev_t s_dev;    // 
    unsigned long s_blocksize;    // 以字节为单位数据块的大小
    unsigned char s_blocksize_bits;    // 块大小的值所占用的位数,
 
    ...
    
    struct list_head s_list;    // 指向超级块链表的指针
 
    struct file_system_type *s_type;    // 指向文件系统的 file_system_type 的指针
 
    struct super_operation *s_op;    // 指向具体文件系统的用于超级块操作的函数集合
 
    struct mutex s_lock;
 
    struct list_head s_dirty;
 
    ...
 
    void *s_fs_info;    // 指向具体文件系统的超级块
};

从上面定义的数据结构可知:所有的超级块对象都以双向循环链表的形式链接在一起。链表中第一个元素用 super_blocks 变量来表示。

与超级块关联的方法就是所谓的超级块操作表,其数据结构是 super_operations,定义如下:

struct super_operations
{
    void (*write_super) (struct super_block *);    // 将超级块的信息写回磁盘
 
    void (*put_super) (struct super_block *);    // 释放超级块对象
 
    void (*read_inode) (struct inode *);    // 读取某个文件系统的索引节点
 
    void (*write_inode) (struct inode *, int);    // 把索引节点写回磁盘
 
    void (*put_inode) (struct inode *);    // 逻辑上释放索引节点
 
    void (*delete_inode) (struct inode *);    // 从磁盘上删除索引节点
 
};

索引节点 inode

文件系统处理文件所需要的所有信息都存放在索引节点中。 在同一个文件系统中,每个索引节点号都是唯一的 。具体文件系统的索引节点是存放在磁盘上,是一种静态结构,要使用它,必须调入内存,填写 VFS 的索引节点,因此,也称 VFS 索引节点是 动态节点

img

我们的磁盘在进行分区、格式化的时候会分为两个区域, 一个是 数据区 ,用于存储文件中的数据另一个是 inode区 ,用于存放 inode table (inode表)inode table 中存放的是一个一个的 inode (也称为inode节点),不同的 inode 就可以表示不同的文件,每一个文件都必须对应一个 inodeinode 实质上是一个结构体,这个结构体中有很多的元素,不同的元素记录了文件了不同信息,譬如:

  • 文件字节大小
  • 文件所有者
  • 文件对应的读/写/执行权限
  • 文件时间戳(创建时间、更新时间等)
  • 文件类型
  • 文件数据存储的block(块)位置
  • ………

img

inode 结构体 定义在<linux/fs.h>中,主要包含:存放的内容如下:

struct inode
{
    struct list_head    i_hash;    // 指向哈希表的指针
    struct list_head    i_list;    // 指向索引节点链表的指针
    struct list_head    i_dentry;  // 指向目录项链表的指针
    ...
    
    unsigned long i_ino;    // 索引节点号
    umode_t    i_mode;      // 文件的类型与访问权限
    kdev_t     i_rdev;      // 实际设备标识号
    uid_t    i_uid;         // 文件拥有者标识号
    gid_t    i_gid;         // 文件拥有者所在组的标识号
    ...
    struct inode_operations *i_op;    // 指向对该节点进行操作的一组函数
 
    struct super_block     *i_sb;     // 指向该文件系统超级块的指针
 
    atomic_t    i_count;    // 当前使用该节点的进程数,计数为0时,表明该节点可丢弃或重新使用
 
    struct file_operations *i_fop;    // 指向文件操作的指针
    ...
    struct vm_area_struct *i_op;      // 指向对文件进行映射所使用的虚存区指针
 
    unsigned long    i_state;    // 索引节点的状态标志
    unsigned int     i_flags;    // 文件系统的安装标志
    
    union    // 联合结构体,其成员指向具体文件系统的 inode 结构
    {
        struct minix_inode_info     minix_i;
        struct Ext2_inode_info      Ext2_i;
    }
};

inode 的索引流程如图所示(注意, 文件名并不是记录在 inode ):

image-20230508160028492

所以由此可知, inode table 本身也需要占用磁盘的存储空间。在同一个文件系统中,每一个文件都有唯一的一个 inode ,每一个 inode 都有一个与之相对应的数字编号 ,内核可以根据索引节点号的哈希值查找其 inode 结构,前提是内核要知道索引节点号和对应文件所在文件系统的超级块对象的地址。 在Linux系统下,我们可以通过"ls -i"命令查看文件的 inode编号 ,如下所示:

image-20230508160918965

上图中 ls 打印出来的信息中,每一行前面的一个数字就表示了对应文件的 inode编号 。除此之外,还可以使用 stat 命令查看,用法如下:

image-20230508161003790

与索引节点关联的方法叫索引节点操作表,由 inode_operations 结构来描述:

struct inode_operations
{
    // 创建一个新的磁盘索引节点
    int (*create) (struct inode *, struct dentry *, int);
    
    // 查找一个索引节点所在的目录
    struct dentry * (*lookup) (struct inode *, struct dentry *);
    
    // 创建一个新的硬链接
    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 *, int);
 
    // 删除一个目录项的索引节点
    int (*rmdir) (struct inode *, struct dentry *);
};

目录项 dentry

每个文件除了有一个索引节点inode数据结构外,还有一个目录项dentry数据结构。 目录项反应了文件系统的树状结构,目前主流的操作系统基本都是用树状结构来组织文件的。linux也不例外。dentry表示一个目录项,目录项下面又有子目录。

文件系统树形结构

img

  • dentry 结构代表的是逻辑意义上的文件,描述的是文件逻辑上的属性, 目录项对象在磁盘上并没有对应的映像
  • inode 结构代表的是物理意义上的文件,记录的是物理上的属性,对于一个具体的文件系统, 其inode结构在磁盘上就有对应的映像

img

**一个索引节点对象可能对应多个目录项对象(因为路径的每一部分称作目录项,而文件的路径很长)。**目录项由dentry结构体标识,定义在<linux/dcache.h>中,主要包含:

struct dentry
{
    atomic_t d_count;            // 目录项引用器
    unsigned int d_flags;        // 目录项标志
    struct inode *d_inode;       // 与文件名关联的索引节点
    struct dentry *d_parent;     // 父目录的目录项 
 
    struct list_head d_hash;     // 目录项形成的哈希表
    struct list_head d_lru;      // 未使用的 LRU 链表
    struct list_head d_child;    // 父目录的子目录项所形成的链表
    struct list_head d_subdirs;  // 该目录项的子目录所形成的的链表
    struct list_head d_alias;    // 索引节点别名的链表
    
    int d_mounted;               // 目录项的安装点
    struct qstr d_name;          // 目录项名(可快速查找)
    struct dentry_operations *d_op;    // 操作目录项的函数
    struct super_block *d_sb;    // 目录项树的根
    unsigned long d_vfs_flags;
    void *d_fsdata;              // 具体文件系统的数据
    unsigned char d_iname[DNAME_INLINE_LEN];    // 短文件名
    ...
 
};

目录项有三种状态:

  • 被使用:该目录项指向一个有效的索引节点,并有一个或多个使用者,不能被丢弃。

  • 未被使用:也对应一个有效的索引节点,但VFS还未使用,被保留在缓存中。如果要回收内存的话,可以撤销未使用的目录项。

  • 负状态:没有对应有效的索引节点,因为索引节点被删除了,或者路径不正确,但是目录项仍被保留了。

将整个文件系统的目录结构解析成目录项,是一件费力的工作,为了节省VFS操作目录项的成本,内核会将目录项缓存起来

文件 file

文件对象是进程打开的文件在内存中的实例。Linux用户程序可以通过open()系统调用来打开一个文件,通过close()系统调用来关闭一个文件。由于多个进程可以同时打开和操作同一个文件, 所以同一个文件,在内存中也存在多个对应的文件对象,但对应的索引节点和目录项是唯一的

一个进程所处的位置是由 fs_strcut 来描述的而一个进程(或者用户)打开的文件是由 files_struct / fdtable 来描述的而整个系统所打开的文件是由 file 结构来描述的

img

文件对象由file结构体表示,file 结构形成了一个双链表,称为系统打开文件表。其定义在<linux/fs.h>中,主要包含:

struct file
{
    struct list_head        f_list;      // 所有打开的文件形成一个链表
    struct dentry           *f_dentry;   // 与文件相关的目录项对象
    struct vfsmount         *f_mount;    // 该文件所在的已安装文件系统
    struct file_operations  *f_op;       // 指向文件操作表的指针
    mode_t     f_mode;                   // 文件的打开模式
    loff_t     f_pos;                    // 文件的当前位置
    unsigned short f_flags;              // 打开文件时所指定的标志
    unsigned short f_count;              // 使用该结构的进程数
    
    ...
};

对文件进行操作的一组函数叫文件操作表,由 file_operations 结构描述,如下:

struct file_operations
{
    // 修改文件指针
    loff_t (*llseek) (struct file *, loff_t, int);
 
    // 从文件中读取若干个字节
    ssize_t (*read)  (struct file *, char *, size_t, loff_t *);
 
    // 给文件中写若干个字节
    ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
    
    // 文件到内存的映射
    int (*mmap) (struct file *, struct vm_area_struct *);
 
    // 打开文件
    int (*open) (struct inode *, struct file *);
 
    // 关闭文件时减少 f_count 计数
    int (*flush) (struct file *);
    
    // 释放 file 对象
    int (*release) (struct inode *, struct file *);
 
    // 文件在缓冲区的数据写回磁盘
    int (*fsync) (struct file *, struct dentry *, int datasync);
 
    ...
};

文件描述符是用来描述打开的文件的。每一个进程用一个 files_struct 结构来记录文件描述符的使用情况,即一个进程可以有多个文件描述符,因为一个进程可以打开多个文件。而通过 dup()、dup2() 和 fcntl() 两个文件描述符可以指向同一个打开的文件,数组的两个元素可能指向同一个文件对象。

每个进程都有自己的根目录和当前工作目录,内核使用 struct fs_struct 来记录这些信息,其定义为:

struct fs_struct
{
    atomic_t count;    // 表示共享同一 fs_struct 表进程数目
    rwlock_t lock;
    int umask;    // 为新创建的文件设置初始文件许可权
    struct dentry *root, *pwd, *altroot;    // 对目录项的描述
    struct vfsmount *rootmnt, *pwdmnt, *altrootmnt;    // 目录安装点的描述
};

除了根目录和当前工作目录,进程还需要记录自己打开的文件。进程已经打开的所有文件使用 struct files_struct 来记录,进程描述符的 files 字段便指向该进程的files_struct结构。它是进程的私有数据,其定义如下:

struct files_struct 
{
    atomic_t count;               // 共享该表的进程数
    rwlock_t file_lock;           // 保护以下的所有域
    int max_fds;                  // 当前文件对象的最大数
    int max_fdset;                // 当前文件描述符的最大数
    int next_fd;                  // 已分配的文件描述符加 1
    
    struct  file ** fd;           // 指向文件对象指针数据的指针
    fd_set *close_on_exec;        // 指向指向 exec() 时需要关闭的文件描述符
    fd_set *open_fds;             // 指向打开的文件描述符的指针
    
    fd_set close_on_exec_init;    // 执行 exec() 时需要关闭的文件描述符的初值集合
    fd_set open_fds_init;         // 文件描述符的初值集合
    struct file *fd_array[32];    // 文件对象指针的初始化数组
};

旧版本的内核中, struct files_struct 中有一个 fd字段 ,指向文件对象的指针数组。通常fd指向fd_array,如果进程打开的文件数目多于32个,内核就分配一个新的更大的文件对象的指针数组,并将其地址存放在fd字段中,这个数组所包含的元素数目存放在 max_fds字段

新版本的内核将 fdmax_fds 以及其他几个相关字段组织在一起,增加一个新的独立数据结构 struct fdtable ,称为 文件描述符表 ,定义于 include/linux/fdtable.h ,其主要数据结构定义如下所示:

struct fdtable {
    unsigned int max_fds;
    struct file __rcu **fd;      /* current fd array */
    unsigned long *close_on_exec;
    unsigned long *open_fds;
    struct rcu_head rcu;
};

打开文件流程

文件在没有被打开的情况下一般都是存放在磁盘中的,譬如电脑硬盘、移动硬盘、U盘等外部存储设备, 文件存放在磁盘文件系统中,并且以一种固定的形式进行存放,我们把他们称为 静态文件

当我们调用 open 函数去打开文件的时候,内核会申请一段内存(一段缓冲区),并且将静态文件的数据内容从磁盘这些存储设备中读取到内存中进行管理、缓存 (也 把内存中的这份文件数据叫做 动态文件 、内核缓冲区 )。 打开文件后,以后对这个文件的读写操作,都是针对内存中这一份动态文件进行相关的操作,而并不是针对磁盘中存放的静态文件

当我们对动态文件进行读写操作后,此时内存中的动态文件和磁盘设备中的静态文件就不同步了, 数据的同步工作由内核完成,内核会在之后将内存这份动态文件更新(同步)到磁盘设备中

因为磁盘、硬盘、U盘等存储设备基本都是Flash块设备,因为块设备硬件本身有读写限制等特征,块设备是以一块一块为单位进行读写的(一个块包含多个扇区,而一个扇区包含多个字节), 一个字节的改动也需要将该字节所在的block全部读取出来进行修改,修改完成之后再写入块设备中,所以导致对块设备的读写操作非常不灵活 ;而 内存可以按字节为单位来操作,而且可以随机操作任意地址数据,非常地很灵活 ,所以对于操作系统来说,会先将磁盘中的静态文件读取到内存中进行缓存,读写操作都是针对这份动态文件,而不是直接去操作磁盘中的静态文件,不但操作不灵活,效率也会下降很多,因为内存的读写速率远比磁盘读写快得多。

在Linux系统中,内核会为每个进程设置一个专门的数据结构用于管理该进程,譬如记录进程的状态信息、运行特征等,我们把这个称为 进程控制块(Process control block,PCB)

PCB数据结构体中有一个指针指向了 文件描述符表(File descriptors) ,文件描述符表中的每一个元素索引到对应的 文件表(File table) ,文件表也是一个数据结构体,其中记录了很多文件相关的信息,譬如 文件状态标志、引用计数、当前文件的读写偏移量以及i-node指针(指向该文件对应的inode)等 ,进程打开的所有文件对应的文件描述符都记录在文件描述符表中,每一个文件描述符都会指向一个对应的文件表,其示意图如下所示:

image-20230508164212232

通过以上介绍可知,打开一个文件,系统内部会将这个过程分为三步:

  1. 系统找到这个文件名所对应的 inode编号
  2. 通过 inode编号inode table 中找到对应的 inode结构体
  3. 根据 inode结构体 中记录的信息,确定文件数据所在的 block ,并读出数据。
inode table
block 位置
inode 编号
inode struct
读取 block

image-20230508204058007

参考文献

1:文件系统理论详解,Linux操作系统原理与应用_Great Macro的博客-CSDN博客

2:Linux内核的5个子系统 - schips - 博客园

3:理解linux文件系统(VFS主要数据结构及之间的关系) - 周围静地出奇 - 博客园

4:文件系统入门知识_Linux教程_Linux公社-Linux系统门户网站

5:Linux 文件系统详解_hguisu的博客-CSDN博客

6:谈谈linux内核学习:虚拟文件系统(VFS) - 知乎



如有疑问或错误,欢迎和我私信交流指正。
版权所有,未经授权,请勿转载!
Copyright © 2023.05 by Mr.Idleman. All rights reserved.


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习笔记之卷积神经网络(二)图像卷积操作与卷积神经网络

深度学习笔记之卷积神经网络——图像卷积操作与卷积神经网络 引言回顾&#xff1a;图像卷积操作补充&#xff1a;卷积核不是卷积函数 卷积神经网络卷积如何实现特征描述/提取卷积神经网络中的卷积核的反向传播过程场景构建与前馈计算卷积层关于卷积核的反向传播过程卷积层关于输…

《花雕学AI》新版必应 Bing 登场:轻松注册,一站式搜索、聊天与绘画应有尽有

引言&#xff1a; 你是否曾经在网上搜索信息时感到困惑或沮丧&#xff1f;你是否曾经想要在网上创造一些有趣或有用的内容&#xff0c;却不知道从何开始&#xff1f;你是否曾经想要用文字描述一个图像&#xff0c;却无法找到合适的图片&#xff1f;如果你的答案是肯定的&#x…

ChatGPT将抢占谁的工作,未来如何应对

“AI人工智能领域里程碑式应用” ChatGPT影响力已经越来越大&#xff0c;激起大家强烈好奇心的同时&#xff0c;也让一些人发出了“感觉自己快要失业了”的焦虑&#xff0c;今天先说一下哪些人的工作会受到 ChatGPT等AI人工智能影响 从工业时代到数字时代这100多年的发展历程来…

【华为机试】——HJ4 字符串分隔

【华为机试】——HJ5 进制转换&#x1f60e; 前言&#x1f64c;HJ4 字符串分隔方法一&#xff1a;巧用scanf的输入格式方法二&#xff1a;循环分解思想 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上…

泛型的特点和深浅拷贝的区别以及不相等对象的hashcode值的问题

永远都不为自己选择的道路而后悔&#xff0c;人生如同坐火车&#xff0c;风景再美也会后退&#xff0c;流逝的时间和邂逅的人终会渐行渐远&#xff0c;前行的始终是自己 泛型常用特点 泛型是JavaSE1.5之后的特性&#xff0c;《Java核心技术》中对泛型的定义是&#xff1a; “…

数据血缘分析工具SQLFLow自动画出数据库的 ER 模型

马哈鱼数据血缘分析器通过分析你所提供的 SQL 脚本&#xff0c;或者连接到数据库&#xff0c;可以自动画出数据库的 ER 模型&#xff0c;可视化表和字段的关系&#xff0c;帮助你迅速了解数据库的设计模型&#xff0c;进行高效的团队沟通。 马哈鱼通过两种途径来为你自动可视化…

MySQL中使用delete_at(时间戳)作为逻辑删除标记时如何使用MyBatis-Plus逻辑删除组件插入时间戳,以及如何解决自动填充失效的问题

背景 MySQL中使用delete_at&#xff08;时间戳&#xff09;作为逻辑删除标记 在业务中&#xff0c;使用逻辑删除是普遍做法&#xff0c;通常会使用一个名为deleted&#xff08;0/1&#xff09;的字段表示删除状态。 但是如果遇到有唯一约束&#xff0c;且可能反复删除和重新…

chatgpt如何引入本地知识?我们来看下emnlp 2022如何将本地图谱知识引入到任务型对话系统中

一、概述 title&#xff1a;Injecting Domain Knowledge in Language Models for Task-Oriented Dialogue Systems 论文地址&#xff1a;https://arxiv.org/abs/2212.08120 代码&#xff1a;GitHub - amazon-science/domain-knowledge-injection 1.1 Motivation 如何在PLM…

SIFT描述子实现

参考&#xff1a;SIFT图像匹配原理及python实现&#xff08;源码实现及基于opencv实现&#xff09; #include <iostream> #include <opencv2/opencv.hpp> #define _USE_MATH_DEFINES #include <math.h> #include <numeric>float mod_float(float x, f…

YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析

前言 最近我在研究YOLOv5的改进&#xff0c;一个模型的好坏、改进后效果如何都是需要一系列指标来判断的。这篇就是我将这几天学到的内容做一下总结。 友情提醒&#xff1a;本篇干货较多&#xff0c;建议先再慢慢看噢&#xff01; ​ &#x1f340;本人YOLOv5源码详解系列&…

大规模并行处理架构Doris入门操作

目录 1 基本概念1.1 Row & Column1.2 Tablet & Partition 2 创建用户2.1 创建数据库并赋予权限2.2 Doris 建表、数据导入与删除 3 建表&#xff08;Create Table&#xff09;3.1 字段类型3.2 数据划分3.3 关于 Partition 和 Bucket 的数量和数据量的建议3.4 演示单分区…

UML状态图详解

上篇文章&#xff0c;介绍了UML的基础知识&#xff0c;并详细介绍了UML中类图的知识点与画法。 本篇&#xff0c;来继续介绍UML中的另一种图——状态图。 1 UML状态图简介 状态图&#xff0c;显示一个状态机&#xff0c;属于UML活动图。 UML具有许多不同类型的图表&#xff…

<代码分享> 分支定界算法的 Python 代码框架

本文以求解整数规划模型为例&#xff0c;提供分支定界算法的 Python 代码框架&#xff0c;期待完善、指正和交流。 文件结构 具体代码 problem.py 定义问题的格式&#xff1a; from typing import Listclass Problem(object):"""problem"""de…

OneAPI 编译cp2k 9.1和cp2k 2023.1

环境信息&#xff1a; Rocky Linux 8.6 Linux server 4.18.0-425.19.2.el8_7.x86_64 gcc version 8.5.0 20210514 (Red Hat 8.5.0-16) (GCC) OneAPI 2023 Intel(R) oneAPI DPC/C Compiler 2023.0.0 (2023.0.0.20221201) ifort version 2021.8.0 一、编译cp2k 9.1 这里用的…

linux系统下常用软件的安装与操作实现

本文来介绍下如何在 Linux 上安装 Windows 上常用的软件&#xff0c;桌面更多的用的是 Ubuntu &#xff0c;下面就以 Ubuntu 为例。 目录 一、安装环境 二、安装步骤 三、总结 一、安装环境 本文使用的安装环境&#xff0c;如下所示&#xff1a; linuxylinuxy:~$ cat /proc…

【Python】【进阶篇】25、Django for标签详解

目录 25、Django for标签详解1. for标签的使用2. for标签嵌套使用3. forloop变量详解 25、Django for标签详解 通过上一节的《Django if标签详解》的讲解&#xff0c;我相信大家对于模板标签已经不再陌生&#xff0c;在本节我们再给大家隆重介绍一位最熟悉的陌生人&#xff0c…

如何熟练的使用trtexec

目录 如何熟练的使用trtexec前言1. 参数解释1.1 Model Options1.2 Build Options1.3 Inference Options1.4 Reporting Options1.5 System Options1.6 完整的参数 如何熟练的使用trtexec 前言 杜老师推出的 trtexec 工具的使用课程&#xff0c;链接。记录下个人学习笔记&#x…

禁止Windows更新自动安装驱动程序

禁止Windows更新自动安装驱动程序 问题解决方案方案1&#xff1a;修改系统设置方案2&#xff1a;修改组策略方案3&#xff1a;修改注册表方案4&#xff1a;回退驱动 问题 Windows更新时&#xff0c;会自动更新驱动程序&#xff0c;甚至有时会将驱动程序反向更新&#xff0c;替…

使用 webdriver API 编写自动化脚本的基本语法

文章目录 1. 打开和关闭浏览器1&#xff09;打开浏览器并访问 URL2&#xff09;关闭浏览器窗口 2. 元素的定位1&#xff09;以 id 定位元素2&#xff09;以 name 定位元素3&#xff09;以 tag name 定位元素4&#xff09;以 class name 定位元素5&#xff09;以 xpath 定位元素…

第九章 控制单元的功能课后习题

指令周期有四个阶段&#xff1a;取值 间址 执行 中断 &#xff0c;控制单元为了完成不同指令会发出不同的操作命令&#xff0c;这些操作信号控制着计算机所有部件有次序的完成不同的操作&#xff0c;以达到执行程序的目的。 控制单元的外特性 9.2控制单元的功能是什么?其输入…