操作系统 第 39 章 插叙:文件和目录

article/2025/6/7 14:03:18

        两项关键操作系统技术的发展:进程,虚拟化的 CPU;地址空间,虚拟化的内存

        这一部分加上虚拟化拼图中最关键的一块:持久存储。永久存储设备永久地(或至少长时间地)存储信息,如传统硬盘驱动器(hard disk drive)更现代的固态存储设备(solid-state storage device)。持久存储设备与内存不同。内存在断电时,其内容会丢失,而持久存储设备会保持这些数据不变。因此,操作系统必须特别注意这样的设备:用户用它们保存真正关心的数据。

                                关键问题:如何管理持久存储设备

        操作系统应该如何管理持久存储设备?都需要哪些API实现有哪些重要方面?

         从总体上看 API:与 UNIX 文件系统交互时看到的接口。

一、文件和目录        

        存储虚拟化形成了两个关键的抽象。第一个是文件(file)。文件就是一个线性字节数组,每个字节都可以读取或写入。每个文件都有某种低级名称(low-level name),通常是某种数字。用户通常不知道这个名字。由于历史原因, 文件的低级名称通常称为 inode 号(inode number)。现在,只要假设每个文件都有一个与其关联的 inode 号

         在大多数系统中,操作系统不太了解文件的结构(例如,它是图片、文本文件还是 C 代码)。相反,文件系统的责任仅仅是将这些数据永久存储在磁盘上,并确保当你再次请求数据时,得到你原来放在那里的内容。

         第二个抽象是目录(directory)。一个目录,像一个文件一样,也有一个低级名字(即 inode 号),但是它的内容非常具体:它包含一个(用户可读名字,低级名字)对的列表。例 如,假设存在一个低级别名称为“ 10 ”的文件,它的用户可读的名称为“ foo ”。“ foo ”所在的目录因此会有条目(“foo”,“10”)将用户可读名称映射到低级名称。目录中的每个条目都指向文件其他目录。通过将目录放入其他目录中,用户可以构建任意的目录树(directory tree,或目录层次结构,directory hierarchy),在该目录树下存储所有文件和目录

         目录层次结构从根目录(root directory)开始(在基于UNIX的系统中,根目录就记为 “/”),并使用某种分隔符(separator)来命名后续子目录 (sub-directories),直到命名所需的文件或目录。例如,如果用户在根目录中创建了一个目录 foo,然后在目录 foo 中创建了一个文件 bar.txt,我们就可以通过它的绝对路径名(absolute pathname)来引用该文件,在这个例子中,它 将是 /foo/bar.txt。更复杂的目录树,请参见图39.1。示例中 的有效目录是/,/foo,/bar,/bar/bar,/bar/foo,有效的文件是/foo/bar.txt 和/bar/foo/bar.txt。目录和文件可以具有相同的名称,只要它们位于文件系统树的不同位置(例如, 图中有两个名为bar.txt的文件:/foo/bar.txt 和/bar/foo/bar.txt)

         可能还会注意到,这个例子中的文件名通常包含两部分:bar 和 txt,以句点分隔。第一部分是任意名称,而文件名的第二部分通常用于指示文件的类型(type),例如,它是 C 代码(例如.c)还是图像(例如.jpg),或音乐文件(例如.mp3)。然而,这通常只是一个惯例(convention):一般不会强制名为 main.c 的文件中包含的数据确实是C源代码。

 二、文件系统接口

        从创建访问删除文件的基础开始。 你可能会认为这很简单,但在这个过程中,你会发现用于删除文件的神秘调用,称为unlink()

三、创建文件 open

        从最基本的操作开始:创建一个文件。这可以通过 open 系统调用完成。通过调用 open() 传入 O_CREAT 标志,程序可以创建一个新文件。下面是示例代码,用于在当前工作目录中创建名为“ foo ”的文件。

int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC); 

         函数 open() 接受一些不同的标志。在本例中,程序创建文件(O_CREAT),只能写入该文件,因为以(O_WRONLY)这种方式打开,并且如果该文件已经存在,则首先将其截断为零字节大小,删除所有现有内容(O_TRUNC)

         open()的一个重要方面是它的返回值文件描述符(file descriptor)。文件描述符只是一 个整数,是每个进程私有的,在 UNIX 系统中用于访问文件。因此,一旦文件被打开,你就可以使用文件描述符来读取或写入文件,假定你有权这样做。这样,一个文件描述符就是一种权限(capability),即一个不透明的句柄,它可以让你执行某些操作。另一种看待文件描述符的方法,是将它作为指向文件类型对象的指针。一旦你有这样的对象,就可以调用其他“ 方法 ”来访问文件,如 read() 和 write() 。下面你会看到如何使用文件描述符。

四、读写文件 cat

        一旦我们有了一些文件,当然就会想要读取或写入。先读取一个现有的文件。如果在命令行键入,我们就可以用 cat 程序将文件的内容显示到屏幕上

prompt> echo hello > foo

prompt> cat foo

hello

prompt> 

         在这段代码中,将程序 echo 的输出重定向到文件 foo,然后文件中就包含单词 “hello”。然后用 cat 来查看文件的内容。但是,cat 程序如何访问文件foo

         使用非常有用的工具,来跟踪程序所做的系统调用。Linux 中,该工具称为 strace,跟踪程序在运行时所做的每个系统调用,然后将跟踪结果显示在屏幕上查看。

        cat 做的第一件事是打开文件准备读取。我们应该注意几件事情。首先,该文件仅为读取而打开(不写入),如 O_RDONLY 标志所示。其次,使用 64 位偏移量(O_LARGEFILE)。 最后,open() 调用成功并返回一个文件描述符,其值为 3

         第一次调用 open() 返回 3 的原因:每个正在运行的进程已经打开了 3 个文件:标准输入(进程可以读取以接收输入)标准输出(进程可以写入以便将信息显示到屏幕),以及标准错误(进程可以写入错误消息)。这些分别由 文件描述符0、1 和2表示。因此,当你第一次打开另一个文件时(如上例所示),它几乎肯定是文件描述符3。

         打开成功后,cat 使用 read() 系统调用重复读取文件中的一些字节。read() 的第一个参数文件描述符,从而告诉文件系统读取哪个文件。一个进程当然可以同时打开多个文件, 因此描述符使操作系统能够知道某个特定的读取引用了哪个文件。第二个参数指向一个用于放置 read() 结果的缓冲区。在上面的系统调用跟踪中,strace 显示了这时的读取结果 (“hello”)。第三个参数是缓冲区的大小,在这个例子中是4KB。对 read() 的调用也成功返回, 这里返回它读取的字节数(6,其中包括“hello”中的5个字母和一个行尾标记)。

         strace 一个有趣的结果:对 write() 系统调用的一次调用,针对文件描述符 1。如上所述,此描述符被称为标准输出,用于将单词“hello”写到屏幕上,这正是 cat 程序要做的事。但是它直接调用 write() 吗?也许(如果它是高度优化的)。但是,如果不是,那么可能会调用库例程 printf()。在内部,printf() 会计算出传递给它的所有格式化细节,并最终对标准输出调用 write,将结果显示到屏幕上。

         然后,cat 程序试图从文件中读取更多内容,但由于文件中没有剩余字节,read() 返回0, 程序知道这意味着它已经读取了整个文件。因此,程序调用 close(),传入相应的文件描述符,表明它已用完文件 “ foo "。该文件因此被关闭,对它的读取完成了

         写入文件是通过一组类似的步骤完成的。首先,打开一个文件准备写入,然后 调用write() 系统调用,对于较大的文件,可能重复调用,然后调用close()。使用 strace 追踪写入文件, 也许针对你自己编写的程序,或者追踪dd实用程序,例如dd if = foo of = bar。

五、读取和写入,但不按顺序 lseek

        到目前为止,我们已经讨论了如何读取和写入文件,但所有访问都是顺序的 (sequential)。也就是说,我们从头到尾读取一个文件,或者从头到尾写一个文件。

        有时能够读取或写入文件中的特定偏移量是有用的。例如,在文本文件上构建了索引并利用它来查找特定单词,最终可能会从文件中的某些随机(random)偏移量中读取数据。为此,我们将使用 lseek() 系统调用。下面是函数原型: 

off_t lseek(int fildes, off_t offset, int whence); 

        第一个参数是熟悉的(一个文件描述符)。第二个参数是偏移量,它将文件偏移量定位到文件中的特定位置。第三个参数,由于历史原因而被称为 whence,明确地指定了搜索的执行方式。以下摘自手册页:

         请注意,调用 lseek() 与移动磁盘臂的磁盘的寻道(seek)操作无关。对 lseek() 的调用只是改变内核中变量的值。执行I/O时,根据磁盘头的位置,磁盘可能会也可能不会执行实际的寻道来完成请求。

六、用 fsync() 立即写入

        大多数情况,当程序调用 write() 时,只是告诉文件系统:在某个时刻,将数据写入持久存储。 出于性能的原因,文件系统会将这些写入在内存中缓冲(buffer)一段时间(例如 5s 或30s)。在稍后的时间点,写入将实际发送到存储设备。从调用应用程序的角度来看,写入似乎很快完成,并且只有在极少数情况下(例如,在write()调用之后但写 入磁盘之前,机器崩溃)数据会丢失。

        但是,有些应用程序需要的不只是这种保证。例如,在数据库管理系统(DBMS)中, 开发正确的恢复协议要求能够经常强制写入磁盘

         为了支持这些类型的应用程序,大多数文件系统都提供了一些额外的控制API。在 UNIX 中,提供给应用程序的接口被称为 fsync(int fd)。当进程针对特定文件描述符调用 fsync() 时, 文件系统通过强制将所有脏(dirty)数据(即尚未写入的)写入磁盘来响应,针对指定文件描述符引用的文件。一旦所有这些写入完成,fsync() 例程就会返回。

        以下是如何使用 fsync() 的简单示例。代码打开文件 foo,向它写入一个数据块,然后调用 fsync() 以确保立即强制写入磁盘。一旦 fsync() 返回,应用程序就可以安全地继续前进, 知道数据已被保存(如果fsync()实现正确,那就是了)。

 七、文件重命名 rename

        有了一个文件后,有时需要给一个文件一个不同的名字。在命令行键入时,这是通过 mv 命令完成的。在下面的例子中,文件 foo 被重命名为 bar。

prompt> mv foo bar         

         利用 strace,可以看到 mv 使用了系统调用 rename(char * old, char * new),它只需要两个参数:文件的原来名称(old)和新名称(new)。

        rename() 调用提供了一个有趣的保证:它(通常)是一个原子(atomic)调用,不论系统是否崩溃。如果系统在重命名期间崩溃,文件将被命名为旧名称或新名称,不会出现奇怪的中间状态。因此,对于支持某些需要对文件状态进行原子更新的应用程序,rename()非常重要。

        假设正在使用文件编辑器(例如 emacs),并将一行插入到文件的中间。例如,该文件名称是 foo.txt。编辑器更新文件并确保新文件包含原有内容和插入行的方式如下:

 

        在这个例子中,编辑器做的事很简单:将文件的新版本写入临时名称(foot.txt.tmp), 使用 fsync() 将其强制写入磁盘。然后,当应用程序确定新文件的元数据和内容在磁盘上, 就将临时文件重命名为原有文件的名称。最后一步自动将新文件交换到位同时删除旧版本的文件,从而实现原子文件更新。

八、获取文件信息 stat

         除了文件访问之外,还希望文件系统能够保存关于它正在存储的每个文件的大量信息。我们通常将这些数据称为文件元数据(metadata)。要查看特定文件的元数据,我们可以使用 stat() 或 fstat() 系统调用。这些调用将一个路径名(或文件描述符)添加到一个文件中,并填充一个stat 结构,如下所示:

 

         有关于每个文件的大量信息,包括其大小(以字节为单位),其低级名称(即 inode 号),一些所有权信息以及有关何时文件被访问或修改的一些信息,等等。要查看此信息,可以使用命令行工具stat:

 

         事实表明,每个文件系统通常将这种类型的信息保存在一个名为 inode 的结构中。当讨论文件系统的实现时,会学习更多关于inode的知识。就目前而言,你应该将 inode 看作是由文件系统保存的持久数据结构,包含上述信息。

 九、删除文件 unlink

        现在知道了如何创建文件按顺序访问它们。但是,如何删除文件?如果用过 UNIX,你可能认为你知道:只需运行程序 rm。但是,rm 使用什么系统调用来删除文件

 

十、创建目录 mkdir

        除了文件外,还可以使用一组与目录相关的系统调用创建读取删除目录。请注意,你永远不能直接写入目录。因为目录的格式被视为文件系统元数据,所以只能间接更新目录,例如,通过在其中创建文件、目录或其他对象类型。通过这种方式,文件系统可以确保目录的内容始终符合预期。

        创建目录,用系统调用 mkdir()。同名的 mkdir 程序可以用来创建这样一个目录。允许 mkdir 程序时创建一个名为 foo 的简单目录时,会发生什么:

prompt> strace mkdir foo

...

mkdir(" foo ", 0777) = 0

...

prompt>

        这样的目录创建时,它被认为是“ 空的 ”,尽管它实际上包含最少的内容。具体来说,空目录有两个条目:一个引用自身的条目,一个引用其父目录的条目。前者称为 “.”(点) 目录,后者称为“..”(点-点)目录。你可以通过向程序 ls 传递一个标志(-a)来查看这些目录

 

十一、读取目录 ls

        创建了目录,也希望能够读取目录。实际上,这时 ls 程序做的事。

         不是像打开文件一样打开一个目录,而是使用一组新的调用。下面是一个打印目录内容的示例程序。该程序使用了 opendir() 、readdir() 和 closedir() 这 3个调用来完成工作,你可以看到接口有多简单。我们只需使用一个简单的循环就可以一次读取一个目录条目,并打印目录中每个文件的名称和 inode 编号

 

        下面的声明在 struct dirent 数据结构中,展示了每个目录条目中可用的信息。 

 struct dirent {

        char d_name[256]; /* filename 文件名 */

        ino_t d_ino; /* inode number inode号*/

        off_t d_off; /* offset to the next dirent 偏移量*/

        unsigned short d_reclen; /* length of this record 长度*/

        unsigned char d_type; /* type of file 类型*/ };

         由于目录只有少量的信息(基本上,只是将名称映射到 inode 号,以及少量其他细节),程序可能需要在每个文件上调用 stat() 以获取每个文件的更多信息,例如其长度或其他详细信息。实际上,这正是 ls 带有 -l 标志时所做的事情。请试着对带有和不带有 -l 标志的 ls 运行strace,自己看看结果。

 十二、删除目录 rmdir

        可以通过调用 rmdir() 来删除目录(它由相同名称的程序rmdir使用)。然而, 与删除文件不同,删除目录更加危险,因为你可以使用单个命令删除大量数据。因此,rmdir() 要求该目录在被删除之前是空的(只有“.”和“..”条目)。如果你试图删除一个非空目录, 那么对rmdir()的调用就会失败

 十三、硬链接 ln

        回到删除文件是通过unlink()的问题,理解在文件系统树中创建条目的新方法,即通过所谓的link() 系统调用。link()系统调用有两个参数:一个旧路径名和一个新路径名。当你将一个新的文件名“ 链接 ”到一个旧的文件名时,实际上创建了另一种引用同一个文件的方法。命令行程序 ln 用于执行此操作,如下面的例子所示:

 prompt> ls -i file file2

67158084 file

67158084 file2

prompt>

         通过带 -i 标志的 ls,它会打印出每个文件的 inode 编号(以及文件名)。因此,可以看到实际上已完成的链接:只是对同一个inode号(本例中为67158084)创建了新的引用。

        现在,可能开始明白 unlink() 名称的由来。创建一个文件时,实际上做了两件事。 首先,要构建一个结构(inode),它将跟踪几乎所有关于文件的信息,包括其大小、文件块在磁盘上的位置等等。其次,将人类可读的名称链接到该文件,并将该链接放入目录中

         在创建文件的硬链接之后,在文件系统中,原有文件名(file)和新创建的文件名(file2) 之间没有区别。实际上,它们都只是指向文件底层元数据的链接,可以在 inode 编号 67158084 中找到。

         因此,为了从文件系统中删除一个文件调用 unlink()。在上面的例子中,可以删除文件名file,并且仍然毫无困难地访问该文件

 prompt> rm file

removed 'file'

prompt> cat file2

hello

         这样的结果是因为当文件系统取消链接文件时,它检查 inode 号中的引用计数(reference count)。该引用计数(有时称为链接计数,link count)允许文件系统跟踪有多少不同的文件名已链接到这个 inode。调用 unlink() 时,会删除人类可读的名称(正在删除的文件)与给定 inode 号之间的“链接”,并减少引用计数。只有当引用计数达到零时,文件系统才会释放 inode 和相关数据块,从而真正“删除”该文件。

         当然,可以使用 stat() 来查看文件的引用计数。看看创建和删除文件的硬链接时,引用计数是什么。在这个例子中,我们将为同一个文件创建 3 个链接,然后删除它们。 仔细看链接计数!

 

十四、符号链接 ln -s

         还有一种非常有用的链接类型,称为符号链接(symbolic link),有时称为软链接(soft link)。事实表明,硬链接有点局限:你不能创建目录的硬链接(因为担心会在目录树中创建一个环)。你不能硬链接到其他磁盘分区中的文件(因为 inode 号在特定文件系统中是唯一的,而不是跨文件系统),等等。因此,创建了一种称为符号链接的新型链接。

         但是,除了表面相似之外,符号链接实际上与硬链接完全不同。第一个区别是符号链接本身实际上是一个不同类型的文件。我们已经讨论过常规文件和目录。符号链接是文件系统知道的第三种类型。对符号链接运行 stat 揭示了一切

 

        最后,由于创建符号链接的方式,有可能造成所谓的悬空引用(dangling reference)。

 十五、创建并挂载文件系统

        已经了解了访问文件、目录和特定类型链接的基本接口。但是还应该讨论另一个话题:如何从许多底层文件系统组建完整的目录树。这项任务的实现是先制作文件系统,然后挂载它们使其内容可以访问

         为了创建一个文件系统,大多数文件系统提供了一个工具,通常名为 mkfs(发音为“make fs”),它就是完成这个任务的。思路如下:作为输入,为该工具提供一个设备(例如磁盘分区,例如/dev/sda1),一种文件系统类型(例如 ext3),它就在该磁盘分区上写入一个空文件系统,从根目录开始。mkfs说,要有文件系统!

        但是,一旦创建了这样的文件系统,就需要在统一的文件系统树中进行访问。这个任务是通过 mount 程序实现的(它使底层系统调用 mount() 完成实际工作)。mount的作用很简单:以现有目录作为目标挂载点(mount point),本质上是将新的文件系统粘贴到目录树的这个点上

        这里举个例子可能很有用。想象一下,有一个未挂载的 ext3 文件系统存储在设备分区/dev/sda1 中,它的内容包括:一个根目录,其中包含两个子目录 a和b,每个子目录依次包含一个名为foo的文件。假设希望在挂载点/home/users上挂载此文件系统。我们会输入以下命令:

 prompt> mount -t ext3 /dev/sda1 /home/users

        如果成功,mount 就让这个新的文件系统可用了。但是,请注意现在如何访问新的文件系统。要查看那个根目录的内容,我们将这样使用ls:

 prompt> ls /home/users/

a b


http://www.hkcw.cn/article/hjOqscXFIs.shtml

相关文章

楼宇自控系统联动暖通空调:解密建筑环境舒适度提升路径

走进现代建筑,无论是办公场所、商业中心,还是医院、酒店,人们对环境舒适度的要求越来越高。暖通空调作为调节建筑室内环境的关键设备,其运行效果直接影响着人们的体验。然而,传统暖通空调独立运行、调控不灵活等问题&a…

Freemarker快速入门

Freemarker概述 FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库&#xff0c…

黑盒(功能)测试基本方法

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、黑盒测试的概念 1、什么是黑盒测试 (1)黑盒测试又称功能测试、数据驱动测试或基于规格说明书的测试,是一种从用户观点出…

[java八股文][JavaSpring面试篇]SpringCloud

了解SpringCloud吗,说一下他和SpringBoot的区别 Spring Boot是用于构建单个Spring应用的框架,而Spring Cloud则是用于构建分布式系统中的微服务架构的工具,Spring Cloud提供了服务注册与发现、负载均衡、断路器、网关等功能。 两者可以结合…

chromedriver 下载失败

问题描述 chromedriver 2.46.0 下载失败 淘宝https://registry.npmmirror.com/chromedriver/2.46/chromedriver_win32.zip无法下载 解决方法 找到可下载源 https://cdn.npmmirror.com/binaries/chromedriver/2.46/chromedriver_win32.zip ,先将其下载到本地目录(D…

74. 搜索二维矩阵 (力扣)

给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。…

CppCon 2014 学习:Rolling Your Own Circuit Simulator

这段话讲述了一个背景和动机,目的是阐明为什么开源C库变得越来越复杂且在科学和工程领域有很大的应用潜力。 关键点: 开源库的成熟: 近年来,开源C库在许多科学和工程领域变得越来越成熟和强大。这些库不再仅仅是简单的工具&…

无人机自主降落论文解析

Dynamic Landing of an Autonomous Quadrotor on a Moving Platform in Turbulent Wind Conditions 滑膜控制器 这一部分详细介绍了边界层滑模控制器(Boundary Layer Sliding Controller,BLSC)的设计和实现,特别是如何将其应用于…

.NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想

在当今数据驱动的时代,向量数据库(Vector Database)作为一种新兴的数据库技术,正逐渐成为软件开发领域的重要组成部分。特别是在 .NET 生态系统中,向量数据库的应用为开发者提供了构建智能、高效应用程序的新途径。 一…

html基础01:前端基础知识学习

html基础01&#xff1a;前端基础知识学习 1.个人建立打造 -- 之前知识的小总结1.1个人简历展示1.2简历信息填写页面 1.个人建立打造 – 之前知识的小总结 1.1个人简历展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&qu…

CentOS Stream 8 Unit network.service not found

一、问题现象 在 CentOS Stream 8 操作系统中&#xff0c;配置完静态IP 信息&#xff0c;想重启网络服务。 执行如下命令&#xff1a; systemctl restart network 提示信息如下&#xff1a; Failed to restart network.service: Unit network.service not found. 二、问题…

【Axure高保真原型】交通事故大屏可视化分析案例

今天和大家分享交通事故大屏可视化分析案例的原型模板&#xff0c;包括饼图分类分析、动态显示发生数、柱状图趋势分析、中部地图展示最新事故发现地点和其他信息、右侧列表记录发生事故的信息…… 通过多种可视化图表展示分析结果&#xff0c;具体效果可以点击下方视频观看或…

网络安全-等级保护(等保) 3-3-1 GB/T 36627-2018 附录A (资料性附录) 测评后活动、附 录 B (资料性附录)渗透测试的有关概念说明

################################################################################ GB/T 36627-2018 《信息安全技术 网络安全等级保护测试评估技术指南》对网络安全等级保护测评中的相关测评技术进行明确的分类和定义,系统地归纳并阐述测评的技术方法,概述技术性安全测试和…

wsl===windows下安装Linux系统

新近版本的windows都支持了Linux&#xff0c;操作如下&#xff1a; 控制面板中启用对应的功能 2. 打开powershell&#xff0c;以安装Ubuntu为例&#xff0c;然后执行 PS C:\Users\steven.wang> wsl.exe --update 正在安装: 适用于 Linux 的 Windows 子系统 已安装 适用于 …

云上展厅颠覆传统展览

云上展厅&#xff1a;重塑展览行业的新篇章 随着数字化技术的迅猛发展&#xff0c;传统展览模式正经历一场深刻的变革。云上展厅&#xff0c;新兴的展览形式&#xff0c;正逐步颠覆传统的实体展览模式&#xff0c;凭借其跨越时空限制、多样化展示方式、丰富互动体验及高效商业…

谷歌地图苹果版v6.138.2 - 前端工具导航

谷歌地图(Google maps)苹果版是是由谷歌官方推出的一款手机地图应用。软件功能强大&#xff0c;支持本地搜索查找世界各地的地址、地点和商家&#xff1b;支持在街景视图中查看世界各地的360度全景图&#xff1b;支持查找乘坐火车、公交车和地铁的路线&#xff0c;或者查找步行…

Microsoft前后端不分离编程新风向:cshtml

文章目录 什么是CSHTML&#xff1f;基础语法内联表达式代码块控制结构 布局页面_ViewStart.cshtml_Layout.cshtml使用布局 模型绑定强类型视图模型集合 HTML辅助方法基本表单验证 局部视图创建局部视图使用局部视图 高级特性视图组件依赖注入Tag Helpers 性能优化缓存捆绑和压缩…

事件对象以及常见属性

1.1获取事件对象 1.2语法 1.3事件对象属性

ssh登录wsl2

1. ssh服务重新安装 Ubuntu20.04子系统自带的ssh服务无法连接&#xff0c;需卸载后重新安装。 sudo apt-get remove openssh-server sudo apt-get install openssh-server2. 修改配置信息 sudo vim /etc/ssh/sshd_config修改内容&#xff1a; # 最好一模一样 Port 33 # 这…

神经符号集成-三篇综述

我会大致讲解三篇神经符号集成的综述&#xff0c;这些综述没有针对推荐系统的&#xff0c;所以大致过一下&#xff0c;下一篇帖子会介绍针对KG的两篇综述。 综述1 这篇综述主要阐述了神经符号学习系统&#xff0c;旨在将神经系统和符号系统 结合到一个统一的框架中。 核心观点…