• 阿里某程序员吐槽:绩效375同事离职被批准,绩效325同事离职却被卡

    在大家的印象里,能力强的员工离职时会被领导挽留,能力差的员工离职则会被直接批准,但有时候却未必如此。 一个阿里程序员发帖吐槽:两个同事相继离职,绩效3.75的员工提出离职后,领导很愉快地同意了,有说有笑很快就放人走了。而绩效3.25的人提出离职,领导却一直挽留,但也不给涨薪,不给确定离职日期,拖拖拉拉,这是为什么? 先给大家科普一下,3.75和3.25是阿里体系的绩效分数,3.75是绩效前30%的人,3.5是绩效在中间60%的人,3.25就是绩效在最后10%的人。 有网友说领导这样做是因为不想得罪3.75的人,怕以后还用得上,而3.25的人却可以无限踩,忽悠一下继续压榨。 再说绩效给了3.75依然留不住的人,领导的底牌已经打光了,自知没有能力留,而3.25的那些劝一劝就继续干了。 当然也有可能是领导不喜欢3.75的员工,担心他会篡位,而3.25的员工还有领导布置的任务没完成。 还有一种可能是3.75的人情商高,会向上管理,把领导管得服服帖帖,3.25的人职场能力不足,只能被疯狂pua。 3.75的人在其他地方也会混得如鱼得水,说不定哪天就和领导碰到了,或者还可以内推领导去更好的地方,领导把他们放出去,以后自己跑路也好相见。 就像斗地主一开始会把难出的牌先出掉,把容易出的牌留在最后作为先手牌,有需要时再出掉。 至于为什么挽留3.25的人,网友说这不是因为他牛,而是因为找不到更便宜的人了。 说白了就是欺负老实人,以为他们好说话。 3.25的人还有一个重要的作用就是背锅,把他们留下还能再背个3.25,现在牛人好找,但背锅的人不好找。 还有网友戏谑地说,领导不放他走是心疼他,怕他出去找不到工作。 或者领导只是客气一下,千万不能当真。 最后有网友给3.25的人支招,直接发邮件告知要离职,一个月后走人,这是员工的正常权利,何必去求人? 或者说一些个人原因,比如照顾小孩等等,让人无法拒绝。 绩效确实在一定程度上反映了工作能力。但职场如江湖,影响绩效的不一定只有工作能力和工作量,还包括情商和性格等众多因素。 有人老实勤恳,踏踏实实,做了许多工作却因不会讨好领导而背上低绩效。有人做人灵活,左右逢源,赢得了领导喜爱和信任,即使工作能力不强也能顺利拿到高绩效。 诚然“老实人”可能会在职场中失去一些好处和福利,但“老实人”不应该成为被人欺负的理由。离职是员工的正当权利之一,企业不能为了找人背锅阻拦员工离职。 一份工作不可能干到老,总会有来有走,希望那些想走的人都能顺利离开,找到更合适的位置,那些想留下的人都能安稳待住,得到更好的晋升和待遇。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    架构师社区 程序员 离职 绩效

  • 简直不要太硬了!一文带你彻底理解文件系统

      你怎么知道哪些块是空闲的?等等问题 我们可以针对这些问题提出一个新的抽象 - 文件。进程和线程的抽象、地址空间和文件都是操作系统的重要概念。如果你能真正深入了解这三个概念,那么你就走上了成为操作系统专家的道路。 文件(Files)是由进程创建的逻辑信息单元。一个磁盘会包含几千甚至几百万个文件,每个文件是独立于其他文件的。事实上,如果你能把每个文件都看作一个独立的地址空间,那么你就可以真正理解文件的概念了。 进程能够读取已经存在的文件,并在需要时重新创建他们。存储在文件中的信息必须是持久的,这也就是说,不会因为进程的创建和终止而受影响。一个文件只能在当用户明确删除的时候才能消失。尽管读取和写入都是最基本的操作,但还有许多其他操作,我们将在下面介绍其中的一些。 文件由操作系统进行管理,有关文件的构造、命名、访问、使用、保护、实现和管理方式都是操作系统设计的主要内容。从总体上看,操作系统中处理文件的部分称为 文件系统(file system),这就是我们所讨论的。 从用户角度来说,用户通常会关心文件是由什么组成的,如何给文件进行命名,如何保护文件,以及可以对文件进行哪些操作等等。尽管是用链表还是用位图记录内存空闲区并不是用户所关心的主题,而这些对系统设计人员来说至关重要。下面我们就来探讨一下这些主题 1 文件 1.1 文件命名 文件是一种抽象机制,它提供了一种方式用来存储信息以及在后面进行读取。可能任何一种机制最重要的特性就是管理对象的命名方式。在创建一个文件后,它会给文件一个命名。当进程终止时,文件会继续存在,并且其他进程可以使用名称访问该文件。 文件命名规则对于不同的操作系统来说是不一样的,但是所有现代操作系统都允许使用 1 - 8 个字母的字符串作为合法文件名。 某些文件区分大小写字母,而大多数则不区分。UNIX 属于第一类;历史悠久的 MS-DOS 属于第二类(顺便说一句,尽管 MS-DOS 历史悠久,但 MS-DOS 仍在嵌入式系统中非常广泛地使用,因此它绝不是过时的);因此,UNIX 系统会有三种不同的命名文件:maria、Maria、MARIA 。在 MS-DOS ,所有这些命名都属于相同的文件。 这里可能需要在文件系统上预留一个位置。Windows 95 和 Windows 98 都使用了 MS-DOS 文件系统,叫做 FAT-16,因此继承了它的一些特征,例如有关文件名的构造方法。Windows 98 引入了对 FAT-16 的一些扩展,从而导致了 FAT-32 的生成,但是这两者很相似。另外,Windows NT,Windows 2000,Windows XP,Windows Vista,Windows 7 和 Windows 8 都支持 FAT 文件系统,这种文件系统有些过时。然而,这些较新的操作系统还具有更高级的本机文件系统(NTFS),有不同的特性,那就是基于 Unicode 编码的文件名。事实上,Windows 8 还配备了另一种文件系统,简称 ReFS(Resilient File System),但这个文件系统一般应用于 Windows 8 的服务器版本。下面除非我们特殊声明,否则我们在提到 MS-DOS 和 FAT 文件系统的时候,所指的就是 Windows 的 FAT-16 和 FAT-32。这里要说一下,有一种类似 FAT 的新型文件系统,叫做 exFAT。它是微软公司对闪存和大文件系统开发的一种优化的 FAT 32 扩展版本。ExFAT 是现在微软唯一能够满足 OS X读写操作的文件系统。 许多操作系统支持两部分的文件名,它们之间用 . 分隔开,比如文件名 prog.c。原点后面的文件称为 文件扩展名(file extension) ,文件扩展名通常表示文件的一些信息。例如在 MS-DOS 中,文件名是 1 - 8 个字符,加上 1 - 3 个字符的可选扩展名组成。在 UNIX 中,如果有扩展名,那么扩展名的长度将由用户来决定,一个文件甚至可以包括两个或更多的扩展名,例如 homepage.html.zip,html 表示一个 web 网页而 .zip 表示文件homepage.html 已经采用 zip 程序压缩完成。一些常用的文件扩展名以及含义如下图所示 扩展名 含义 bak 备份文件 c c 源程序文件 gif 符合图形交换格式的图像文件 hlp 帮助文件 html WWW 超文本标记语言文档 jpg 符合 JPEG 编码标准的静态图片 mp3 符合 MP3 音频编码格式的音乐文件 mpg 符合 MPEG 编码标准的电影 o 目标文件(编译器输出格式,尚未链接) pdf pdf 格式的文件 ps PostScript 文件 tex 为 TEX 格式化程序准备的输入文件 txt 文本文件 zip 压缩文件 在 UNIX 系统中,文件扩展名只是一种约定,操作系统并不强制采用。 名为 file.txt 的文件是文本文件,这个文件名更多的是提醒所有者,而不是给计算机传递信息。但是另一方面,C 编译器可能要求它编译的文件以.c 结尾,否则它会拒绝编译。然而,操作系统并不关心这一点。 对于可以处理多种类型的程序,约定就显得及其有用。例如 C 编译器可以编译、链接多种文件,包括 C 文件和汇编语言文件。这时扩展名就很有必要,编译器利用它们区分哪些是 C 文件,哪些是汇编文件,哪些是其他文件。因此,扩展名对于编译器判断哪些是 C 文件,哪些是汇编文件以及哪些是其他文件变得至关重要。 与 UNIX 相反,Windows 就会关注扩展名并对扩展名赋予了新的含义。用户(或进程) 可以在操作系统中注册扩展名,并且规定哪个程序能够拥有扩展名。当用户双击某个文件名时,拥有该文件名的程序就启动并运行文件。例如,双击 file.docx 启动了 Word 程序,并以 file.docx 作为初始文件。 1.2 文件结构 文件的构造有多种方式。下图列出了常用的三种构造方式 上图中的 a 是一种无结构的字节序列,操作系统不关心序列的内容是什么,操作系统能看到的就是字节(bytes)。其文件内容的任何含义只在用户程序中进行解释。UNIX 和 Windows 都采用这种办法。 把文件看成字节序列提供了最大的灵活性。用户程序可以向文件中写任何内容,并且可以通过任何方便的形式命名。操作系统不会为为用户写入内容提供帮助,当然也不会干扰阻塞你。对于想做特殊操作的用户来说,后者是十分重要的。所有的 UNIX 版本(包括 Linux 和 OS X)和 Windows 都使用这种文件模型。 图 b 表示在文件结构上的第一步改进。在这个模型中,文件是具有固定长度记录的序列,每个记录都有其内部结构。把文件作为记录序列的核心思想是:读操作返回一个记录,而写操作重写或者追加一个记录。第三种文件结构如上图 c 所示。在这种组织结构中,文件由一颗记录树构成,记录树的长度不一定相同,每个记录树都在记录中的固定位置包含一个key 字段。这棵树按 key 进行排序,从而可以对特定的 key 进行快速查找。 在记录树的结构中,可以取出下一个记录,但是最关键的还是根据 key 搜索指定的记录。如上图 c 所示,用户可以读出指定的 pony 记录,而不必关心记录在文件中的确切位置。用户也可以在文件中添加新的记录。但是用户不能决定添加到何处位置,添加到何处位置是由操作系统决定的。 1.3 文件类型 很多操作系统支持多种文件类型。例如,UNIX(同样包括 OS X)和 Windows 都具有常规的文件和目录。除此之外,UNIX 还具有字符特殊文件(character special file) 和 块特殊文件(block special file)。常规文件(Regular files) 是包含有用户信息的文件。用户一般使用的文件大都是常规文件,常规文件一般包括 可执行文件、文本文件、图像文件,从常规文件读取数据或将数据写入时,内核会根据文件系统的规则执行操作,写入可能被延迟,记录日志或者接受其他操作。 字符特殊文件和输入/输出有关,用于串行 I/O 类设备,如终端、打印机、网络等。块特殊文件用于磁盘类设备。我们主要讨论的是常规文件。 常规文件一般分为 ASCII 码文件或者二进制文件。ASCII 码文件由文本组成。在一些系统中,每行都会用回车符结束(ASCII 码是 13,控制字符 CR,转义字符\r。),另外一些则会使用换行符(ASCII 码是 10,控制字符 LF,转义字符\n)。一些系统(比如 Windows)两者都会使用。 ASCII 文件的优点在于显示 和 打印,还可以用任何文本编辑器进行编辑。进一步来说,如果许多应用程序使用 ASCII 码作为输入和输出,那么很容易就能够把多个程序连接起来,一个程序的输出可能是另一个程序的输入,就像管道一样。 其他与 ASCII 不同的是二进制文件。打印出来的二进制文件是无法理解的。下面是一个二进制文件的格式,它取自早期的 UNIX 。尽管从技术上来看这个文件只是字节序列,但是操作系统只有在文件格式正确的情况下才会执行。 这个文件有五个段:文件头、正文、数据、重定位位和符号表。文件头以 魔数(magic number) 为开始,表明这个文件是一个可执行文件(以防止意外执行非此格式的文件)。然后是文件各个部分的大小,开始执行的标志以及一些标志位。程序本身的正文和数据在文件头后面,他们被加载到内存中或者重定位会根据重定位位进行判断。符号表则用于调试。 二进制文件的另外一种形式是存档文件,它由已编译但没有链接的库过程(模块)组合而成。每个文件都以模块头开始,其中记录了名称、创建日期、所有者、保护码和文件大小。和可执行文件一样,模块头也都是二进制数,将它们复制到打印机将会产生乱码。 所有的操作系统必须至少能够识别一种文件类型:它自己的可执行文件。以前的 TOPS-20 系统(用于 DECsystem 20)甚至要检查要执行的任何文件的创建时间,为了定位资源文件来检查自动文件创建后是否被修改过。如果被修改过了,那么就会自动编译文件。在 UNIX 中,就是在 shell 中嵌入 make 程序。此时操作系统要求用户必须采用固定的文件扩展名,从而确定哪个源程序生成哪个二进制文件。 什么是 make 程序?在软件发展过程中,make 程序是一个自动编译的工具,它通过读取称为 Makefiles 的文件来自动从源代码构建可执行程序和库,该文件指定了如何导出目标程序。尽管集成开发环境和特定语言的编译器功能也可以用于管理构建过程,但 Make 仍被广泛使用,尤其是在 Unix 和类似 Unix 的操作系统中使用。 当程序从文件中读写数据时,请求会转到内核处理程序(kernel driver)。如果文件是常规文件,则数据由文件系统驱动程序处理,并且通常存储在磁盘或其他存储介质上的某块区域中,从文件中读取的数据就是之前在该位置写入的数据。 当数据读取或写入到设备文件时,请求会被设备驱动程序处理。每个设备文件都有一个关联的编号,该编号标示要使用的设备驱动程序。设备处理数据的工作是它自己的事儿。 块设备 也叫做块特殊文件,它的行为通常与普通文件相似:它们是字节数组,并且在给定位置读取的值是最后写入该位置的值。来自块设备的数据可以缓存在内存中,并从缓存中读取;写入可以被缓冲。块设备通常是可搜索的,块设备的概念是,相应的硬件可以一次读取或者写入整个块,例如磁盘上的一个扇区 字符设备 也称为字符特殊文件,它的行为类似于管道、串行端口。将字节写入字符设备可能会导致它在屏幕上显示,在串行端口上输出,转换为声音。 目录(Directories) 是管理文件系统结构的系统文件。它是用于在计算机上存储文件的位置。目录位于分层文件系统中,例如 Linux,MS-DOS 和 UNIX。 它显示所有本地和子目录(例如,cdn 目录中的 big 目录)。当前目录是 C 盘驱动器的根目录。之所以称为根目录,是因为该目录下没有任何内容,而其他目录都在该目录下分支。 1.4 文件访问 早期的操作系统只有一种访问方式:序列访问(sequential access)。在这些系统中,进程可以按照顺序读取所有的字节或文件中的记录,但是不能跳过并乱序执行它们。顺序访问文件是可以返回到起点的,需要时可以多次读取该文件。当存储介质是磁带而不是磁盘时,顺序访问文件很方便。 在使用磁盘来存储文件时,可以不按照顺序读取文件中的字节或者记录,或者按照关键字而不是位置来访问记录。这种能够以任意次序进行读取的称为随机访问文件(random access file)。许多应用程序都需要这种方式。 随机访问文件对许多应用程序来说都必不可少,例如,数据库系统。如果乘客打电话预定某航班机票,订票程序必须能够直接访问航班记录,而不必先读取其他航班的成千上万条记录。 有两种方法可以表示从何处开始读取文件。第一种方法是直接使用 read 从头开始读取。另一种是用一个特殊的 seek 操作设置当前位置,在 seek 操作后,从这个当前位置顺序地开始读文件。UNIX 和 Windows 使用的是后面一种方式。 1.5 文件属性 文件包括文件名和数据。除此之外,所有的操作系统还会保存其他与文件相关的信息,如文件创建的日期和时间、文件大小。我们可以称这些为文件的属性(attributes)。有些人也喜欢把它们称作 元数据(metadata)。文件的属性在不同的系统中差别很大。文件的属性只有两种状态:设置(set) 和 清除(clear)。下面是一些常用的属性 属性 含义 保护 谁可以访问文件、以什么方式存取文件 密码(口令) 访问文件所需要的密码(口令) 创建者 创建文件者的 ID 所有者 当前所有者 只读标志 0 表示读/写,1 表示只读 隐藏标志 0 表示正常,1 表示不再列表中显示 系统标志 0 表示普通文件,1 表示系统文件 存档标志 0 表示已经备份,1 表示需要备份 ASCII / 二进制标志 0 表示 ASCII 文件,1 表示二进制文件 随机访问标志 0 表示只允许顺序访问,1 表示随机访问 临时标志 0 表示正常,1 表示进程退出时删除该文件 加锁标志 0 表示未加锁,1 表示加锁 记录长度 一个记录中的字节数 键的位置 每个记录中的键的偏移量 键的长度 键字段的字节数 创建时间 创建文件的日期和时间 最后一次存取时间 上一次访问文件的日期和时间 最后一次修改时间 上一次修改文件的日期和时间 当前大小 文件的字节数 最大长度 文件可能增长到的字节数 没有一个系统能够同时具有上面所有的属性,但每个属性都在某个系统中采用。 前面四个属性(保护,口令,创建者,所有者)与文件保护有关,它们指出了谁可以访问这个文件,谁不能访问这个文件。 保护(File Protection):用于保护计算机上有价值数据的方法。文件保护是通过密码保护文件或者仅仅向特定用户或组提供权限来实现。 在一些系统中,用户必须给出口令才能访问文件。标志(flags)是一些位或者短属性能够控制或者允许特定属性。 隐藏文件位(hidden flag)表示该文件不在文件列表中出现。 存档标志位(archive flag)用于记录文件是否备份过,由备份程序清除该标志位;若文件被修改,操作系统则设置该标志位。用这种方法,备份程序可以知道哪些文件需要备份。 临时标志位(temporary flag) 允许文件被标记为是否允许自动删除当进程终止时。 记录长度(record-length)、键的位置(key-position)和键的长度(key-length)等字段只能出现在用关键字查找记录的文件中。它们提供了查找关键字所需要的信息。 不同的时间字段记录了文件的创建时间、最近一次访问时间以及最后一次修改时间,它们的作用不同。例如,目标文件生成后被修改的源文件需要重新编译生成目标文件。这些字段提供了必要的信息。 当前大小字段指出了当前的文件大小,一些旧的大型机操作系统要求在创建文件时指定文件最大值,以便让操作系统提前保留最大存储值。但是一些服务器和个人计算机却不用设置此功能。 1.6 文件操作 使用文件的目的是用来存储信息并方便以后的检索。对于存储和检索,不同的系统提供了不同的操作。以下是与文件有关的最常用的一些系统调用: Create,创建不包含任何数据的文件。调用的目的是表示文件即将建立,并对文件设置一些属性。 Delete,当文件不再需要,必须删除它以释放内存空间。为此总会有一个系统调用来删除文件。 Open,在使用文件之前,必须先打开文件。这个调用的目的是允许系统将属性和磁盘地址列表保存到主存中,用来以后的快速访问。 Close,当所有进程完成时,属性和磁盘地址不再需要,因此应关闭文件以释放表空间。很多系统限制进程打开文件的个数,以此达到鼓励用户关闭不再使用的文件。磁盘以块为单位写入,关闭文件时会强制写入最后一块,即使这个块空间内部还不满。 Read,数据从文件中读取。通常情况下,读取的数据来自文件的当前位置。调用者必须指定需要读取多少数据,并且提供存放这些数据的缓冲区。 Write,向文件写数据,写操作一般也是从文件的当前位置开始进行。如果当前位置是文件的末尾,则会直接追加进行写入。如果当前位置在文件中,则现有数据被覆盖,并且永远消失。 append,使用 append 只能向文件末尾添加数据。 seek,对于随机访问的文件,要指定从何处开始获取数据。通常的方法是用 seek 系统调用把当前位置指针指向文件中的特定位置。seek 调用结束后,就可以从指定位置开始读写数据了。 get attributes,进程运行时通常需要读取文件属性。 set attributes,用户可以自己设置一些文件属性,甚至是在文件创建之后,实现该功能的是 set attributes 系统调用。 rename,用户可以自己更改已有文件的名字,rename 系统调用用于这一目的。 2 目录 文件系统通常提供目录(directories) 或者 文件夹(folders) 用于记录文件的位置,在很多系统中目录本身也是文件,下面我们会讨论关于文件,他们的组织形式、属性和可以对文件进行的操作。 2.1 一级目录系统 目录系统最简单的形式是有一个能够包含所有文件的目录。这种目录被称为根目录(root directory),由于根目录的唯一性,所以其名称并不重要。在最早期的个人计算机中,这种系统很常见,部分原因是因为只有一个用户。下面是一个单层目录系统的例子 该目录中有四个文件。这种设计的优点在于简单,并且能够快速定位文件,毕竟只有一个地方可以检索。这种目录组织形式现在一般用于简单的嵌入式设备(如数码相机和某些便携式音乐播放器)上使用。 2.2 层次目录系统 对于简单的应用而言,一般都用单层目录方式,但是这种组织形式并不适合于现代计算机,因为现代计算机含有成千上万个文件和文件夹。如果都放在根目录下,查找起来会非常困难。为了解决这一问题,出现了层次目录系统(Hierarchical Directory Systems),也称为目录树。通过这种方式,可以用很多目录把文件进行分组。进而,如果多个用户共享同一个文件服务器,比如公司的网络系统,每个用户可以为自己的目录树拥有自己的私人根目录。这种方式的组织结构如下 根目录含有目录 A、B 和 C ,分别属于不同的用户,其中两个用户个字创建了子目录。用户可以创建任意数量的子目录,现代文件系统都是按照这种方式组织的。 2.3 路径名 当目录树组织文件系统时,需要有某种方法指明文件名。常用的方法有两种,第一种方式是每个文件都会用一个绝对路径名(absolute path name),它由根目录到文件的路径组成。举个例子,/usr/ast/mailbox 意味着根目录包含一个子目录usr,usr 下面包含了一个 mailbox。绝对路径名总是以 / 开头,并且是唯一的。在 UNIX 中,路径的组件由/分隔。在 Windows 中,分隔符为\。在 MULTICS 中,它是>。因此,在这三个系统中,相同的路径名将被编写如下 Windows \usr\ast\mailbox UNIX /usr/ast/mailbox MULTICS >usr>ast>mailbox 不论使用哪种方式,如果路径名的第一个字符是分隔符,那就是绝对路径。 另外一种指定文件名的方法是 相对路径名(relative path name)。它常常和 工作目录(working directory) (也称作 当前目录(current directory))一起使用。用户可以指定一个目录作为当前工作目录。例如,如果当前目录是 /usr/ast,那么绝对路径 /usr/ast/mailbox可以直接使用 mailbox 来引用。也就是说,如果工作目录是 /usr/ast,则 UNIX 命令 cp /usr/ast/mailbox /usr/ast/mailbox.bak 和命令 cp mailbox mailbox.bak 具有相同的含义。相对路径通常情况下更加方便和简洁。而它实现的功能和绝对路径安全相同。 一些程序需要访问某个特定的文件而不必关心当前的工作目录是什么。在这种情况下,应该使用绝对路径名。 支持层次目录结构的大多数操作系统在每个目录中有两个特殊的目录项. 和 ..,读作 dot 和 dotdot。dot 指的是当前目录,dotdot 指的是其父目录(在根目录中例外,在根目录中指向自己)。可以参考下面的进程树来查看如何使用。 一个进程的工作目录是 /usr/ast,它可采用 .. 沿树向上,例如,可用命令 cp ../lib/dictionary . 把文件 usr/lib/dictionary 复制到自己的目录下,第一个路径告诉系统向上找(到 usr 目录),然后向下到 lib 目录,找到 dictionary 文件 第二个参数 . 指定当前的工作目录,当 cp 命令用目录名作为最后一个参数时,则把全部的文件复制到该目录中。当然,对于上述复制,键入 cp /usr/lib/dictionary . 是更常用的方法。用户这里采用 . 可以避免键入两次 dictionary 。无论如何,键入 cp /usr/lib/dictionary dictionary 也可正常工作,就像键入 cp /usr/lib/dictionary /usr/lib/dictionary 一样。所有这些命令都能够完成同样的工作。 2.4 目录操作 不同文件中管理目录的系统调用的差别比管理文件的系统调用差别大。为了了解这些系统调用有哪些以及它们怎样工作,下面给出一个例子(取自 UNIX)。 Create,创建目录,除了目录项 . 和 .. 外,目录内容为空。 Delete,删除目录,只有空目录可以删除。只包含 . 和 .. 的目录被认为是空目录,这两个目录项通常不能删除 opendir,目录内容可被读取。例如,未列出目录中的全部文件,程序必须先打开该目录,然后读其中全部文件的文件名。与打开和读文件相同,在读目录前,必须先打开文件。 closedir,读目录结束后,应该关闭目录用于释放内部表空间。 readdir,系统调用 readdir 返回打开目录的下一个目录项。以前也采用 read 系统调用来读取目录,但是这种方法有一个缺点:程序员必须了解和处理目录的内部结构。相反,不论采用哪一种目录结构,readdir 总是以标准格式返回一个目录项。 rename,在很多方面目录和文件都相似。文件可以更换名称,目录也可以。 link,链接技术允许在多个目录中出现同一个文件。这个系统调用指定一个存在的文件和一个路径名,并建立从该文件到路径所指名字的链接。这样,可以在多个目录中出现同一个文件。有时也被称为硬链接(hard link)。 unlink,删除目录项。如果被解除链接的文件只出现在一个目录中,则将它从文件中删除。如果它出现在多个目录中,则只删除指定路径名的链接,依然保留其他路径名的链接。在 UNIX 中,用于删除文件的系统调用就是 unlink。 3 文件系统的实现 在对文件有了基本认识之后,现在是时候把目光转移到文件系统的实现上了。之前用户关心的一直都是文件是怎样命名的、可以进行哪些操作、目录树是什么,如何找到正确的文件路径等问题。而设计人员关心的是文件和目录是怎样存储的、磁盘空间是如何管理的、如何使文件系统得以流畅运行的问题,下面我们就来一起讨论一下这些问题。 3.1 文件系统布局 文件系统存储在磁盘中。大部分的磁盘能够划分出一到多个分区,叫做磁盘分区(disk partitioning) 或者是磁盘分片(disk slicing)。每个分区都有独立的文件系统,每块分区的文件系统可以不同。磁盘的 0 号分区称为 主引导记录(Master Boot Record, MBR),用来引导(boot) 计算机。在 MBR 的结尾是分区表(partition table)。每个分区表给出每个分区由开始到结束的地址。系统管理员使用一个称为分区编辑器的程序来创建,调整大小,删除和操作分区。这种方式的一个缺点是很难适当调整分区的大小,导致一个分区具有很多可用空间,而另一个分区几乎完全被分配。 MBR 可以用在 DOS 、Microsoft Windows 和 Linux 操作系统中。从 2010 年代中期开始,大多数新计算机都改用 GUID 分区表(GPT)分区方案。 下面是一个用 GParted 进行分区的磁盘,表中的分区都被认为是 活动的(active)。 当计算机开始引导 boot 时,BIOS 读入并执行 MBR。 引导块 MBR 做的第一件事就是确定活动分区,读入它的第一个块,称为引导块(boot block) 并执行。引导块中的程序将加载分区中的操作系统。为了一致性,每个分区都会从引导块开始,即使引导块不包含操作系统。引导块占据文件系统的前 4096 个字节,从磁盘上的字节偏移量 0 开始。引导块可用于启动操作系统。 在计算机中,引导就是启动计算机的过程,它可以通过硬件(例如按下电源按钮)或者软件命令的方式来启动。开机后,电脑的 CPU 还不能执行指令,因为此时没有软件在主存中,所以一些软件必须先被加载到内存中,然后才能让 CPU 开始执行。也就是计算机开机后,首先会进行软件的装载过程。 重启电脑的过程称为重新引导(rebooting),从休眠或睡眠状态返回计算机的过程不涉及启动。 除了从引导块开始之外,磁盘分区的布局是随着文件系统的不同而变化的。通常文件系统会包含一些属性,如下 超级块 紧跟在引导块后面的是 超级块(Superblock),超级块 的大小为 4096 字节,从磁盘上的字节偏移 4096 开始。超级块包含文件系统的所有关键参数 文件系统的大小 文件系统中的数据块数 指示文件系统状态的标志 分配组大小 在计算机启动或者文件系统首次使用时,超级块会被读入内存。 空闲空间块 接着是文件系统中空闲块的信息,例如,可以用位图或者指针列表的形式给出。 BitMap 位图或者 Bit vector 位向量 位图或位向量是一系列位或位的集合,其中每个位对应一个磁盘块,该位可以采用两个值:0 和 1,0 表示已分配该块,而 1 表示一个空闲块。下图中的磁盘上给定的磁盘块实例(分配了绿色块)可以用 16 位的位图表示为:0000111000000110。 使用链表进行管理 在这种方法中,空闲磁盘块链接在一起,即一个空闲块包含指向下一个空闲块的指针。第一个磁盘块的块号存储在磁盘上的单独位置,也缓存在内存中。 碎片 这里不得不提一个叫做碎片(fragment)的概念,也称为片段。一般零散的单个数据通常称为片段。磁盘块可以进一步分为固定大小的分配单元,片段只是在驱动器上彼此不相邻的文件片段。如果你不理解这个概念就给你举个例子。比如你用 Windows 电脑创建了一个文件,你会发现这个文件可以存储在任何地方,比如存在桌面上,存在磁盘中的文件夹中或者其他地方。你可以打开文件,编辑文件,删除文件等等。你可能以为这些都在一个地方发生,但是实际上并不是,你的硬盘驱动器可能会将文件中的一部分存储在一个区域内,另一部分存储在另外一个区域,在你打开文件时,硬盘驱动器会迅速的将文件的所有部分汇总在一起,以便其他计算机系统可以使用它。 inode 然后在后面是一个 inode(index node),也称作索引节点。它是一个数组的结构,每个文件有一个 inode,inode 非常重要,它说明了文件的方方面面。每个索引节点都存储对象数据的属性和磁盘块位置 有一种简单的方法可以找到它们 ls -lai 命令。让我们看一下根文件系统: inode 节点主要包括了以下信息 模式/权限(保护) 所有者 ID 组 ID 文件大小 文件的硬链接数 上次访问时间 最后修改时间 inode 上次修改时间 文件分为两部分,索引节点和块。一旦创建后,每种类型的块数是固定的。你不能增加分区上 inode 的数量,也不能增加磁盘块的数量。 紧跟在 inode 后面的是根目录,它存放的是文件系统目录树的根部。最后,磁盘的其他部分存放了其他所有的目录和文件。 3.2 文件的实现 最重要的问题是记录各个文件分别用到了哪些磁盘块。不同的系统采用了不同的方法。下面我们会探讨一下这些方式。分配背后的主要思想是有效利用文件空间和快速访问文件 ,主要有三种分配方案 连续分配 链表分配 索引分配 连续分配 最简单的分配方案是把每个文件作为一连串连续数据块存储在磁盘上。因此,在具有 1KB 块的磁盘上,将为 50 KB 文件分配 50 个连续块。 上面展示了 40 个连续的内存块。从最左侧的 0 块开始。初始状态下,还没有装载文件,因此磁盘是空的。接着,从磁盘开始处(块 0 )处开始写入占用 4 块长度的内存 A 。然后是一个占用 6 块长度的内存 B,会直接在 A 的末尾开始写。 注意每个文件都会在新的文件块开始写,所以如果文件 A 只占用了 3 又 1/2 个块,那么最后一个块的部分内存会被浪费。在上面这幅图中,总共展示了 7 个文件,每个文件都会从上个文件的末尾块开始写新的文件块。 连续的磁盘空间分配有两个优点。 第一,连续文件存储实现起来比较简单,只需要记住两个数字就可以:一个是第一个块的文件地址和文件的块数量。给定第一个块的编号,可以通过简单的加法找到任何其他块的编号。 第二点是读取性能比较强,可以通过一次操作从文件中读取整个文件。只需要一次寻找第一个块。后面就不再需要寻址时间和旋转延迟,所以数据会以全带宽进入磁盘。 因此,连续的空间分配具有实现简单、高性能的特点。 不幸的是,连续空间分配也有很明显的不足。随着时间的推移,磁盘会变得很零碎。下图解释了这种现象 这里有两个文件 D 和 F 被删除了。当删除一个文件时,此文件所占用的块也随之释放,就会在磁盘空间中留下一些空闲块。磁盘并不会在这个位置挤压掉空闲块,因为这会复制空闲块之后的所有文件,可能会有上百万的块,这个量级就太大了。 刚开始的时候,这个碎片不是问题,因为每个新文件都会在之前文件的结尾处进行写入。然而,磁盘最终会被填满,因此要么压缩磁盘、要么重新使用空闲块的空间。压缩磁盘的开销太大,因此不可行;后者会维护一个空闲列表,这个是可行的。但是这种情况又存在一个问题,为空闲块匹配合适大小的文件,需要知道该文件的最终大小。 想象一下这种设计的结果会是怎样的。用户启动 word 进程创建文档。应用程序首先会询问最终创建的文档会有多大。这个问题必须回答,否则应用程序就不会继续执行。如果空闲块的大小要比文件的大小小,程序就会终止。因为所使用的磁盘空间已经满了。那么现实生活中,有没有使用连续分配内存的介质出现呢? CD-ROM 就广泛的使用了连续分配方式。 CD-ROM(Compact Disc Read-Only Memory)即只读光盘,也称作只读存储器。是一种在电脑上使用的光碟。这种光碟只能写入数据一次,信息将永久保存在光碟上,使用时通过光碟驱动器读出信息。 然而 DVD 的情况会更加复杂一些。原则上,一个 90 分钟 的电影能够被编码成一个独立的、大约 4.5 GB 的文件。但是文件系统所使用的 UDF(Universal Disk Format) 格式,使用一个 30 位的数来代表文件长度,从而把文件大小限制在 1 GB。所以,DVD 电影一般存储在 3、4 个连续的 1 GB 空间内。这些构成单个电影中的文件块称为扩展区(extends)。 就像我们反复提到的,历史总是惊人的相似,许多年前,连续分配由于其简单和高性能被实际使用在磁盘文件系统中。后来由于用户不希望在创建文件时指定文件的大小,于是放弃了这种想法。但是随着 CD-ROM 、DVD、蓝光光盘等光学介质的出现,连续分配又流行起来。从而得出结论,技术永远没有过时性,现在看似很老的技术,在未来某个阶段可能又会流行起来。 链表分配 第二种存储文件的方式是为每个文件构造磁盘块链表,每个文件都是磁盘块的链接列表,就像下面所示 每个块的第一个字作为指向下一块的指针,块的其他部分存放数据。如果上面这张图你看的不是很清楚的话,可以看看整个的链表分配方案 与连续分配方案不同,这一方法可以充分利用每个磁盘块。除了最后一个磁盘块外,不会因为磁盘碎片而浪费存储空间。同样,在目录项中,只要存储了第一个文件块,那么其他文件块也能够被找到。 另一方面,在链表的分配方案中,尽管顺序读取非常方便,但是随机访问却很困难(这也是数组和链表数据结构的一大区别)。 还有一个问题是,由于指针会占用一些字节,每个磁盘块实际存储数据的字节数并不再是 2 的整数次幂。虽然这个问题并不会很严重,但是这种方式降低了程序运行效率。许多程序都是以长度为 2 的整数次幂来读写磁盘,由于每个块的前几个字节被指针所使用,所以要读出一个完成的块大小信息,就需要当前块的信息和下一块的信息拼凑而成,因此就引发了查找和拼接的开销。 使用内存表进行链表分配 由于连续分配和链表分配都有其不可忽视的缺点。所以提出了使用内存中的表来解决分配问题。取出每个磁盘块的指针字,把它们放在内存的一个表中,就可以解决上述链表的两个不足之处。下面是一个例子 上图表示了链表形成的磁盘块的内容。这两个图中都有两个文件,文件 A 依次使用了磁盘块地址 4、7、 2、 10、 12,文件 B 使用了6、3、11 和 14。也就是说,文件 A 从地址 4 处开始,顺着链表走就能找到文件 A 的全部磁盘块。同样,从第 6 块开始,顺着链走到最后,也能够找到文件 B 的全部磁盘块。你会发现,这两个链表都以不属于有效磁盘编号的特殊标记(-1)结束。内存中的这种表格称为 文件分配表(File Application Table,FAT)。 使用这种组织方式,整个块都可以存放数据。进而,随机访问也容易很多。虽然仍要顺着链在内存中查找给定的偏移量,但是整个链都存放在内存中,所以不需要任何磁盘引用。与前面的方法相同,不管文件有多大,在目录项中只需记录一个整数(起始块号),按照它就可以找到文件的全部块。 这种方式存在缺点,那就是必须要把整个链表放在内存中。对于 1TB 的磁盘和 1KB 的大小的块,那么这张表需要有 10 亿项。。。每一项对应于这 10 亿个磁盘块中的一块。每项至少 3 个字节,为了提高查找速度,有时需要 4 个字节。根据系统对空间或时间的优化方案,这张表要占用 3GB 或 2.4GB 的内存。FAT 的管理方式不能较好地扩展并应用于大型磁盘中。而这正是最初 MS-DOS 文件比较实用,并仍被各个 Windows 版本所安全支持。 inode 最后一个记录各个文件分别包含哪些磁盘块的方法是给每个文件赋予一个称为 inode(索引节点) 的数据结构,每个文件都与一个 inode 进行关联,inode 由整数进行标识。 下面是一个简单例子的描述。 给出 inode 的长度,就能够找到文件中的所有块。 相对于在内存中使用表的方式而言,这种机制具有很大的优势。即只有在文件打开时,其 inode 才会在内存中。如果每个 inode 需要 n 个字节,最多 k 个文件同时打开,那么 inode 占有总共打开的文件是 kn 字节。仅需预留这么多空间。 这个数组要比我们上面描述的 FAT(文件分配表) 占用的空间小的多。原因是用于保存所有磁盘块的链接列表的表的大小与磁盘本身成正比。如果磁盘有 n 个块,那么这个表也需要 n 项。随着磁盘空间的变大,那么该表也随之线性增长。相反,inode 需要节点中的数组,其大小和可能需要打开的最大文件个数成正比。它与磁盘是 100GB、4000GB 还是 10000GB 无关。 inode 有一个问题是如果每个节点都会有固定大小的磁盘地址,那么文件增长到所能允许的最大容量外会发生什么?一个解决方案是最后一个磁盘地址不指向数据块,而是指向一个包含额外磁盘块地址的地址,如上图所示。一个更高级的解决方案是:有两个或者更多包含磁盘地址的块,或者指向其他存放地址的磁盘块的磁盘块。Windows 的 NTFS 文件系统采用了相似的方法,所不同的仅仅是大的 inode 也可以表示小的文件。 NTFS 的全称是 New Technology File System,是微软公司开发的专用系统文件,NTFS 取代 FAT(文件分配表) 和 HPFS(高性能文件系统) ,并在此基础上进一步改进。例如增强对元数据的支持,使用更高级的数据结构以提升性能、可靠性和磁盘空间利用率等。 3.3 目录的实现 文件只有打开后才能够被读取。在文件打开后,操作系统会使用用户提供的路径名来定位磁盘中的目录。目录项提供了查找文件磁盘块所需要的信息。根据系统的不同,提供的信息也不同,可能提供的信息是整个文件的磁盘地址,或者是第一个块的数量(两个链表方案)或 inode 的数量。不过不管用那种情况,目录系统的主要功能就是 将文件的 ASCII 码的名称映射到定位数据所需的信息上。 与此关系密切的问题是属性应该存放在哪里。每个文件系统包含不同的文件属性,例如文件的所有者和创建时间,需要存储的位置。一种显而易见的方法是直接把文件属性存放在目录中。有一些系统恰好是这么做的,如下。 在这种简单的设计中,目录有一个固定大小的目录项列表,每个文件对应一项,其中包含一个固定长度的文件名,文件属性的结构体以及用以说明磁盘块位置的一个或多个磁盘地址。 对于采用 inode 的系统,会把 inode 存储在属性中而不是目录项中。在这种情况下,目录项会更短:仅仅只有文件名称和 inode 数量。这种方式如下所示 到目前为止,我们已经假设文件具有较短的、固定长度的名字。在 MS-DOS 中,具有 1 - 8 个字符的基本名称和 1 - 3 个字符的可拓展名称。在 UNIX 版本 7 中,文件有 1 - 14 个字符,包括任何拓展。然而,几乎所有的现代操作系统都支持可变长度的扩展名。这是如何实现的呢? 最简单的方式是给予文件名一个长度限制,比如 255 个字符,然后使用上图中的设计,并为每个文件名保留 255 个字符空间。这种处理很简单,但是浪费了大量的目录空间,因为只有很少的文件会有那么长的文件名称。所以,需要一种其他的结构来处理。 一种可选择的方式是放弃所有目录项大小相同的想法。在这种方法中,每个目录项都包含一个固定部分,这个固定部分通常以目录项的长度开始,后面是固定格式的数据,通常包括所有者、创建时间、保护信息和其他属性。这个固定长度的头的后面是一个任意长度的实际文件名,如下图所示 上图是 SPARC 机器使用正序放置。 处理机中的一串字符存放的顺序有正序(big-endian) 和逆序(little-endian) 之分。正序存放的就是高字节在前低字节在后,而逆序存放的就是低字节在前高字节在后。 这个例子中,有三个文件,分别是 project-budget、personnel 和 foo。每个文件名以一个特殊字符(通常是 0 )结束,用矩形中的叉进行表示。为了使每个目录项从字的边界开始,每个文件名被填充成整数个字,如下图所示 这个方法的缺点是当文件被移除后,就会留下一块固定长度的空间,而新添加进来的文件大小不一定和空闲空间大小一致。 这个问题与我们上面探讨的连续磁盘文件的问题是一样的,由于整个目录在内存中,所以只有对目录进行紧凑拼接操作才可节省空间。另一个问题是,一个目录项可能会分布在多个页上,在读取文件名时可能发生缺页中断。 处理可变长度文件名字的另外一种方法是,使目录项自身具有固定长度,而将文件名放在目录末尾的堆栈中。如上图所示的这种方式。这种方法的优点是当目录项被移除后,下一个文件将能够正常匹配移除文件的空间。当然,必须要对堆进行管理,因为在处理文件名的时候也会发生缺页异常。 到目前为止的所有设计中,在需要查找文件名时,所有的方案都是线性的从头到尾对目录进行搜索。对于特别长的目录,线性搜索的效率很低。提高文件检索效率的一种方式是在每个目录上使用哈希表(hash table),也叫做散列表。我们假设表的大小为 n,在输入文件名时,文件名被散列在 0 和 n - 1 之间,例如,它被 n 除,并取余数。或者对构成文件名字的字求和或类似某种方法。 无论采用哪种方式,在添加一个文件时都要对与散列值相对 应的散列表进行检查。如果没有使用过,就会将一个指向目录项的指针指向这里。文件目录项紧跟着哈希表后面。如果已经使用过,就会构造一个链表(这种构造方式是不是和 HashMap 使用的数据结构一样?),链表的表头指针存放在表项中,并通过哈希值将所有的表项相连。 查找文件的过程和添加类似,首先对文件名进行哈希处理,在哈希表中查找是否有这个哈希值,如果有的话,就检查这条链上所有的哈希项,查看文件名是否存在。如果哈希不在链上,那么文件就不在目录中。 使用哈希表的优势是查找非常迅速,缺点是管理起来非常复杂。只有在系统中会有成千上万个目录项存在时,才会考虑使用散列表作为解决方案。 另外一种在大量目录中加快查找指令目录的方法是使用缓存,缓存查找的结果。在开始查找之前,会首先检查文件名是否在缓存中。如果在缓存中,那么文件就能立刻定位。当然,只有在较少的文件下进行多次查找,缓存才会发挥最大功效。 3.4 共享文件 当多个用户在同一个项目中工作时,他们通常需要共享文件。如果这个共享文件同时出现在多个用户目录下,那么他们协同工作起来就很方便。下面的这张图我们在上面提到过,但是有一个更改的地方,就是 C 的一个文件也出现在了 B 的目录下。 如果按照如上图的这种组织方式而言,那么 B 的目录与该共享文件的联系称为 链接(link)。那么文件系统现在就是一个 有向无环图(Directed Acyclic Graph, 简称 DAG),而不是一棵树了。 在图论中,如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个有向无环图,我们不会在此着重探讨关于图论的东西,大家可以自行 google。 将文件系统组织成为有向无环图会使得维护复杂化,但也是必须要付出的代价。 共享文件很方便,但这也会带来一些问题。如果目录中包含磁盘地址,则当链接文件时,必须把 C 目录中的磁盘地址复制到 B 目录中。如果 B 或者 C 随后又向文件中添加内容,则仅在执行追加的用户的目录中显示新写入的数据块。这种变更将会对其他用户不可见,从而破坏了共享的目的。 有两种方案可以解决这种问题。 第一种解决方案,磁盘块不列入目录中,而是会把磁盘块放在与文件本身相关联的小型数据结构中。目录将指向这个小型数据结构。这是 UNIX 中使用的方式(小型数据结构就是 inode)。 在第二种解决方案中,通过让系统建立一个类型为 LINK 的新文件,并把该文件放在 B 的目录下,使得 B 与 C 建立链接。新的文件中只包含了它所链接的文件的路径名。当 B 想要读取文件时,操作系统会检查 B 的目录下存在一个类型为 LINK 的文件,进而找到该链接的文件和路径名,然后再去读文件,这种方式称为 符号链接(symbolic linking)。 上面的每一种方法都有各自的缺点,在第一种方式中,B 链接到共享文件时,inode 记录文件的所有者为 C。建立一个链接并不改变所有关系,如下图所示。 第一开始的情况如图 a 所示,此时 C 的目录的所有者是 C ,当目录 B 链接到共享文件时,并不会改变 C 的所有者关系,只是把计数 + 1,所以此时 系统知道目前有多少个目录指向这个文件。然后 C 尝试删除这个文件,这个时候有个问题,如果 C 把文件移除并清除了 inode 的话,那么 B 会有一个目录项指向无效的节点。如果 inode 以后分配给另一个文件,则 B 的链接指向一个错误的文件。系统通过 inode 可知文件仍在被引用,但是没有办法找到该文件的全部目录项以删除它们。指向目录的指针不能存储在 inode 中,原因是有可能有无数个这样的目录。 所以我们能做的就是删除 C 的目录项,但是将 inode 保留下来,并将计数设置为 1 ,如上图 c 所示。c 表示的是只有 B 有指向该文件的目录项,而该文件的前者是 C 。如果系统进行记账操作的话,那么 C 将继续为该文件付账直到 B 决定删除它,如果是这样的话,只有到计数变为 0 的时刻,才会删除该文件。 对于符号链接,以上问题不会发生,只有真正的文件所有者才有一个指向 inode 的指针。链接到该文件上的用户只有路径名,没有指向 inode 的指针。当文件所有者删除文件时,该文件被销毁。以后若试图通过符号链接访问该文件将会失败,因为系统不能找到该文件。删除符号链接不会影响该文件。 符号链接的问题是需要额外的开销。必须读取包含路径的文件,然后要一个部分接一个部分地扫描路径,直到找到 inode 。这些操作也许需要很多次额外的磁盘访问。此外,每个符号链接都需要额外的 inode ,以及额外的一个磁盘块用于存储路径,虽然如果路径名很短,作为一种优化,系统可以将它存储在 inode 中。符号链接有一个优势,即只要简单地提供一个机器的网络地址以及文件在该机器上驻留的路径,就可以连接全球任何地方机器上的文件。 还有另一个由链接带来的问题,在符号链接和其他方式中都存在。如果允许链接,文件有两个或多个路径。查找一指定目录及其子目录下的全部文件的程序将多次定位到被链接的文件。例如,一个将某一目录及其子目录下的文件转存到磁带上的程序有可能多次复制一个被链接的文件。进而,如果接着把磁带读入另一台机器,除非转出程序具有智能,否则被链接的文件将被两次复制到磁盘上,而不是只是被链接起来。 3.5 日志结构文件系统 技术的改变会给当前的文件系统带来压力。这种情况下,CPU 会变得越来越快,磁盘会变得越来越大并且越来越便宜(但不会越来越快)。内存容量也是以指数级增长。但是磁盘的寻道时间(除了固态盘,因为固态盘没有寻道时间)并没有获得提高。 这些因素结合起来意味着许多系统文件中出现性能瓶颈。为此,Berkeley 设计了一种全新的文件系统,试图缓解这个问题,这个文件系统就是 日志结构文件系统(Log-structured File System, LFS)。 日志结构文件系统由 Rosenblum和 Ousterhout于 90 年代初引入,旨在解决以下问题。 不断增长的系统内存 顺序 I/O 性能胜过随机 I/O 性能 现有低效率的文件系统 文件系统不支持 RAID(虚拟化) 另一方面,当时的文件系统不论是 UNIX 还是 FFS,都有大量的随机读写(在 FFS 中创建一个新文件至少需要 5 次随机写),因此成为整个系统的性能瓶颈。同时因为 Page cache的存在,作者认为随机读不是主要问题:随着越来越大的内存,大部分的读操作都能被 cache,因此 LFS 主要要解决的是减少对硬盘的随机写操作。 在这种设计中,inode 甚至具有与 UNIX 中相同的结构,但是现在它们分散在整个日志中,而不是位于磁盘上的固定位置。所以,inode 很定位。为了能够找到 inode ,维护了一个由 inode 索引的 inode map(inode 映射)。表项 i 指向磁盘中的第 i 个 inode 。这个映射保存在磁盘中,但是也保存在缓存中,因此,使用最频繁的部分大部分时间都在内存中。 日志结构文件系统主要使用四种数据结构:Inode、Inode Map、Segment、Segment Usage Table。 到目前为止,所有写入最初都缓存在内存中,并且追加在日志末尾,所有缓存的写入都定期在单个段中写入磁盘。所以,现在打开文件也就意味着用映射定位文件的索引节点。一旦 inode 被定位后,磁盘块的地址就能够被找到。所有这些块本身都将位于日志中某处的分段中。 真实情况下的磁盘容量是有限的,所以最终日志会占满整个磁盘空间,这种情况下就会出现没有新的磁盘块被写入到日志中。幸运的是,许多现有段可能具有不再需要的块。例如,如果一个文件被覆盖了,那么它的 inode 将被指向新的块,但是旧的磁盘块仍在先前写入的段中占据着空间。 为了处理这个问题,LFS 有一个清理(clean)线程,它会循环扫描日志并对日志进行压缩。首先,通过查看日志中第一部分的信息来查看其中存在哪些索引节点和文件。它会检查当前 inode 的映射来查看 inode 是否在当前块中,是否仍在被使用。如果不是,该信息将被丢弃。如果仍然在使用,那么 inode 和块就会进入内存等待写回到下一个段中。然后原来的段被标记为空闲,以便日志可以用来存放新的数据。用这种方法,清理线程遍历日志,从后面移走旧的段,然后将有效的数据放入内存等待写到下一个段中。由此一来整个磁盘会形成一个大的环形缓冲区,写线程将新的段写在前面,而清理线程则清理后面的段。 3.6 日志文件系统 虽然日志结构系统的设计很优雅,但是由于它们和现有的文件系统不相匹配,因此还没有广泛使用。不过,从日志文件结构系统衍生出来一种新的日志系统,叫做日志文件系统,它会记录系统下一步将要做什么的日志。微软的 NTFS 文件系统、Linux 的 ext3 就使用了此日志。OS X 将日志系统作为可供选项。为了看清它是如何工作的,我们下面讨论一个例子,比如 移除文件 ,这个操作在 UNIX 中需要三个步骤完成: 在目录中删除文件 释放 inode 到空闲 inode 池 将所有磁盘块归还给空闲磁盘池。 在 Windows 中,也存在类似的步骤。不存在系统崩溃时,这些步骤的执行顺序不会带来问题。但是一旦系统崩溃,就会带来问题。假如在第一步完成后系统崩溃。inode 和文件块将不会被任何文件获得,也不会再分配;它们只存在于废物池中的某个地方,并因此减少了可利用的资源。如果崩溃发生在第二步后,那么只有磁盘块会丢失。日志文件系统保留磁盘写入期间对文件系统所做的更改的日志或日志,该日志可用于快速重建可能由于系统崩溃或断电等事件而发生的损坏。 一般文件系统崩溃后必须运行 fsck(文件系统一致性检查)实用程序。 为了让日志能够正确工作,被写入的日志操作必须是 幂等的(idempotent),它意味着只要有必要,它们就可以重复执行很多次,并不会带来破坏。像操作 更新位表并标记 inode k 或者块 n 是空闲的 可以重复执行任意次。同样地,查找一个目录并且删除所有叫 foobar 的项也是幂等的。相反,把从 inode k 新释放的块加入空闲表的末端不是幂等的,因为它们可能已经被释放并存放在那里了。 为了增加可靠性,一个文件系统可以引入数据库中 原子事务(atomic transaction) 的概念。使用这个概念,一组动作可以被界定在开始事务和结束事务操作之间。这样,文件系统就会知道它必须完成所有的动作,要么就一个不做。 3.7 虚拟文件系统 即使在同一台计算机上或者在同一个操作系统下,都会使用很多不同的文件系统。Windows 中的主要文件系统是 NTFS 文件系统,但不是说 Windows 只有 NTFS 操作系统,它还有一些其他的例如旧的 FAT -32 或FAT -16 驱动器或分区,其中包含仍需要的数据,闪存驱动器,旧的 CD-ROM 或 DVD(每个都有自己的独特文件系统)。Windows 通过指定不同的盘符来处理这些不同的文件系统,比如 C:,D: 等。盘符可以显示存在也可以隐式存在,如果你想找指定位置的文件,那么盘符是显示存在;如果当一个进程打开一个文件时,此时盘符是隐式存在,所以 Windows 知道向哪个文件系统传递请求。 相比之下,UNIX 采用了一种不同的方式,即 UNIX 把多种文件系统整合到一个统一的结构中。一个 Linux 系统可以使用 ext2 作为根文件系统,ext3 分区装载在 /usr 下,另一块采用 Reiser FS 文件系统的硬盘装载到 /home下,以及一个 ISO 9660 的 CD - ROM 临时装载到 /mnt 下。从用户的观点来看,只有一个文件系统层级,但是事实上它们是由多个文件系统组合而成,对于用户和进程是不可见的。 UNIX 操作系统使用一种 虚拟文件系统(Virtual File System, VFS) 来尝试将多种文件系统构成一个有序的结构。关键的思想是抽象出所有文件系统都共有的部分,并将这部分代码放在一层,这一层再调用具体文件系统来管理数据。下面是一个 VFS 的系统结构 还是那句经典的话,在计算机世界中,任何解决不了的问题都可以加个代理来解决。所有和文件相关的系统调用在最初的处理上都指向虚拟文件系统。这些来自用户进程的调用,都是标准的 POSIX 系统调用,比如 open、read、write 和 seek 等。VFS 对用户进程有一个 上层 接口,这个接口就是著名的 POSIX 接口。 VFS 也有一个对于实际文件的 下层 接口,就是上图中标记为 VFS 的接口。这个接口包含许多功能调用,这样 VFS 可以使每一个文件系统完成任务。因此,要创建一个可以与 VFS 一起使用的新文件系统,新文件系统的设计者必须确保它提供了 VFS 要求的功能。一个明显的例子是从磁盘读取特定的块,然后将其放入文件系统的缓冲区高速缓存中,然后返回指向该块的指针的函数。因此,VFS 具有两个不同的接口:上一个到用户进程,下一个到具体文件系统。 当系统启动时,根文件系统在 VFS 中注册。另外,当装载其他文件时,不管在启动时还是在操作过程中,它们也必须在 VFS 中注册。当一个文件系统注册时,根文件系统注册到 VFS。另外,在引导时或操作期间挂载其他文件系统时,它们也必须向 VFS 注册。当文件系统注册时,其基本作用是提供 VFS 所需功能的地址列表、调用向量表、或者 VFS 对象。因此一旦文件系统注册到 VFS,它就知道从哪里开始读取数据块。 装载文件系统后就可以使用它了。比如,如果一个文件系统装载到 /usr 并且一个进程调用它: open("/usr/include/unistd.h",O_RDONLY) 当解析路径时, VFS 看到新的文件系统被挂载到 /usr,并且通过搜索已经装载文件系统的超级块来确定它的超块。然后它找到它所转载的文件的根目录,在那里查找路径 include/unistd.h。然后 VFS 创建一个 vnode 并调用实际文件系统,以返回所有的在文件 inode 中的信息。这个信息和其他信息一起复制到 vnode (内存中)。而这些其他信息中最重要的是指向包含调用 vnode 操作的函数表的指针,比如 read、write 和 close 等。 当 vnode 被创建后,为了进程调用,VFS 在文件描述符表中创建一个表项,并将它指向新的 vnode,最后,VFS 向调用者返回文件描述符,所以调用者可以用它去 read、write 或者 close 文件。 当进程用文件描述符进行一个读操作时,VFS 通过进程表和文件描述符确定 vnode 的位置,并跟随指针指向函数表,这样就调用了处理 read 函数,运行在实际系统中的代码并得到所请求的块。VFS 不知道请求时来源于本地硬盘、还是来源于网络中的远程文件系统、CD-ROM 、USB 或者其他介质,所有相关的数据结构如下图所示 从调用者进程号和文件描述符开始,进而是 vnode,读函数指针,然后是对实际文件系统的访问函数定位。 4 文件系统的管理和优化 能够使文件系统工作是一回事,能够使文件系统高效、稳定的工作是另一回事,下面我们就来探讨一下文件系统的管理和优化。 4.1 磁盘空间管理 文件通常存在磁盘中,所以如何管理磁盘空间是一个操作系统的设计者需要考虑的问题。在文件上进行存有两种策略:分配 n 个字节的连续磁盘空间;或者把文件拆分成多个并不一定连续的块。在存储管理系统中,主要有分段管理和 分页管理 两种方式。 正如我们所看到的,按连续字节序列存储文件有一个明显的问题,当文件扩大时,有可能需要在磁盘上移动文件。内存中分段也有同样的问题。不同的是,相对于把文件从磁盘的一个位置移动到另一个位置,内存中段的移动操作要快很多。因此,几乎所有的文件系统都把文件分割成固定大小的块来存储。 块大小 一旦把文件分为固定大小的块来存储,就会出现问题,块的大小是多少?按照磁盘组织方式,扇区、磁道和柱面显然都可以作为分配单位。在分页系统中,分页大小也是主要因素。 拥有大的块尺寸意味着每个文件,甚至 1 字节文件,都要占用一个柱面空间,也就是说小文件浪费了大量的磁盘空间。另一方面,小块意味着大部分文件将会跨越多个块,因此需要多次搜索和旋转延迟才能读取它们,从而降低了性能。因此,如果分配的块太大会浪费空间;分配的块太小会浪费时间。 记录空闲块 一旦指定了块大小,下一个问题就是怎样跟踪空闲块。有两种方法被广泛采用,如下图所示 第一种方法是采用磁盘块链表,链表的每个块中包含极可能多的空闲磁盘块号。对于 1 KB 的块和 32 位的磁盘块号,空闲表中每个块包含有 255 个空闲的块号。考虑 1 TB 的硬盘,拥有大概十亿个磁盘块。为了存储全部地址块号,如果每块可以保存 255 个块号,则需要将近 400 万个块。通常,空闲块用于保存空闲列表,因此存储基本上是空闲的。 另一种空闲空间管理的技术是位图(bitmap),n 个块的磁盘需要 n 位位图。在位图中,空闲块用 1 表示,已分配的块用 0 表示。对于 1 TB 硬盘的例子,需要 10 亿位表示,即需要大约 130 000 个 1 KB 块存储。很明显,和 32 位链表模型相比,位图需要的空间更少,因为每个块使用 1 位。只有当磁盘快满的时候,链表需要的块才会比位图少。 如果空闲块是长期连续的话,那么空闲列表可以改成记录连续分块而不是单个的块。每个块都会使用 8 位、16 位、32 位的计数来与每个块相联,来记录连续空闲块的数量。最好的情况是一个空闲块可以用两个数字来表示:第一个空闲块的地址和空闲块的计数。另一方面,如果磁盘严重碎片化,那么跟踪连续分块要比跟踪单个分块运行效率低,因为不仅要存储地址,还要存储数量。 这种情况说明了一个操作系统设计者经常遇到的一个问题。有许多数据结构和算法可以用来解决问题,但是选择一个最好的方案需要数据的支持,而这些数据是设计者无法预先拥有的。只有在系统部署完毕真正使用使用后才会获得。 现在,回到空闲链表的方法,只有一个指针块保存在内存中。创建文件时,所需要的块从指针块中取出。当它用完时,将从磁盘中读取一个新的指针块。类似地,删除文件时,文件的块将被释放并添加到主存中的指针块中。当块被填满时,写回磁盘。 在某些特定的情况下,这个方法导致了不必要的磁盘 IO,如下图所示 上面内存中的指针块仅有两个空闲块,如果释放了一个含有三个磁盘块的文件,那么该指针块就会溢出,必须将其写入磁盘,那么就会产生如下图的这种情况。 如果现在写入含有三个块的文件,已满的指针不得不再次读入,这将会回到上图 a 中的情况。如果有三个块的文件只是作为临时文件被写入,在释放它时,需要进行另一次磁盘写操作以将完整的指针块写回到磁盘。简而言之,当指针块几乎为空时,一系列短暂的临时文件可能会导致大量磁盘 I/O。 避免大部分磁盘 I/O 的另一种方法是拆分完整的指针块。这样,当释放三个块时,变化不再是从 a - b,而是从 a - c,如下图所示 现在,系统可以处理一系列临时文件,而不需要进行任何磁盘 I/O。如果内存中指针块满了,就写入磁盘,半满的指针块从磁盘中读入。这里的思想是:要保持磁盘上的大多数指针块为满的状态(减少磁盘的使用),但是在内存中保留了一个半满的指针块。这样,就可以既处理文件的创建又同时可以处理文件的删除操作,而不会为空闲表进行磁盘 I/O。 对于位图,会在内存中只保留一个块,只有在该块满了或空了的情形下,才到磁盘上取另一个块。通过在位图的单一块上进行所有的分配操作,磁盘块会紧密的聚集在一起,从而减少了磁盘臂的移动。由于位图是一种固定大小的数据结构,所以如果内核是分页的,就可以把位图放在虚拟内存中,在需要时将位图的页面调入。 4.2 磁盘配额 为了防止一些用户占用太多的磁盘空间,多用户操作通常提供一种磁盘配额(enforcing disk quotas)的机制。系统管理员为每个用户分配最大的文件和块分配,并且操作系统确保用户不会超过其配额。我们下面会谈到这一机制。 在用户打开一个文件时,操作系统会找到文件属性和磁盘地址,并把它们送入内存中的打开文件表。其中一个属性告诉文件所有者是谁。任何有关文件的增加都会记到所有者的配额中。 第二张表包含了每个用户当前打开文件的配额记录,即使是其他人打开该文件也一样。如上图所示,该表的内容是从被打开文件的所有者的磁盘配额文件中提取出来的。当所有文件关闭时,该记录被写回配额文件。 当在打开文件表中建立一新表项时,会产生一个指向所有者配额记录的指针。每次向文件中添加一个块时,文件所有者所用数据块的总数也随之增加,并会同时增加硬限制和软限制的检查。可以超出软限制,但硬限制不可以超出。当已达到硬限制时,再往文件中添加内容将引发错误。同样,对文件数目也存在类似的检查。 什么是硬限制和软限制?硬限制是软限制的上限。软限制是为会话或进程实际执行的限制。这允许管理员(或用户)将硬限制设置为允许它们希望允许的最大使用上限。然后,其他用户和进程可以根据需要使用软限制将其资源使用量自限制到更低的上限。 当一个用户尝试登陆,系统将检查配额文件以查看用户是否超出了文件数量或磁盘块数量的软限制。如果违反了任一限制,则会显示警告,保存的警告计数减 1,如果警告计数为 0 ,表示用户多次忽略该警告,因而将不允许该用户登录。要想再得到登录的许可,就必须与系统管理员协商。 如果用户在退出系统时消除所超过的部分,他们就可以再一次终端会话期间超过其软限制,但无论什么情况下都不会超过硬限制。 4.3 文件系统备份 文件系统的毁坏要比计算机的损坏严重很多。无论是硬件还是软件的故障,只要计算机文件系统被破坏,要恢复起来都是及其困难的,甚至是不可能的。因为文件系统无法抵御破坏,因而我们要在文件系统在被破坏之前做好数据备份,但是备份也不是那么容易,下面我们就来探讨备份的过程。 许多人认为为文件系统做备份是不值得的,并且很浪费时间,直到有一天他们的磁盘坏了,他们才意识到事情的严重性。相对来说,公司在这方面做的就很到位。磁带备份主要要处理好以下两个潜在问题中的一个 从意外的灾难中恢复 这个问题主要是由于外部条件的原因造成的,比如磁盘破裂,水灾火灾等。 从错误的操作中恢复 第二个问题通常是由于用户意外的删除了原本需要还原的文件。这种情况发生的很频繁,使得 Windows 的设计者们针对 删除 命令专门设计了特殊目录,这就是 回收站(recycle bin),也就是说,在删除文件的时候,文件本身并不真正从磁盘上消失,而是被放置到这个特殊目录下,等以后需要的时候可以还原回去。文件备份更主要是指这种情况,能够允许几天之前,几周之前的文件从原来备份的磁盘进行还原。 做文件备份很耗费时间而且也很浪费空间,这会引起下面几个问题。首先,是要备份整个文件还是仅备份一部分呢?一般来说,只是备份特定目录及其下的全部文件,而不是备份整个文件系统。 其次,对上次未修改过的文件再进行备份是一种浪费,因而产生了一种增量转储(incremental dumps) 的思想。最简单的增量转储的形式就是周期性的做全面的备份,而每天只对增量转储完成后发生变化的文件做单个备份。 周期性:比如一周或者一个月 稍微好一点的方式是只备份最近一次转储以来更改过的文件。当然,这种做法极大的缩减了转储时间,但恢复起来却更复杂,因为最近的全面转储先要全部恢复,随后按逆序进行增量转储。为了方便恢复,人们往往使用更复杂的转储模式。 第三,既然待转储的往往是海量数据,那么在将其写入磁带之前对文件进行压缩就很有必要。但是,如果在备份过程中出现了文件损坏的情况,就会导致破坏压缩算法,从而使整个磁带无法读取。所以在备份前是否进行文件压缩需慎重考虑。 第四,对正在使用的文件系统做备份是很难的。如果在转储过程中要添加,删除和修改文件和目录,则转储结果可能不一致。因此,因为转储过程中需要花费数个小时的时间,所以有必要在晚上将系统脱机进行备份,然而这种方式的接受程度并不高。所以,人们修改了转储算法,记下文件系统的瞬时快照,即复制关键的数据结构,然后需要把将来对文件和目录所做的修改复制到块中,而不是到处更新他们。 磁盘转储到备份磁盘上有两种方案:物理转储和逻辑转储。物理转储(physical dump) 是从磁盘的 0 块开始,依次将所有磁盘块按照顺序写入到输出磁盘,并在复制最后一个磁盘时停止。这种程序的万无一失性是其他程序所不具备的。 第二个需要考虑的是坏块的转储。制造大型磁盘而没有瑕疵是不可能的,所以也会存在一些坏块(bad blocks)。有时进行低级格式化后,坏块会被检测出来并进行标记,这种情况的解决办法是用磁盘末尾的一些空闲块所替换。 然而,一些块在格式化后会变坏,在这种情况下操作系统可以检测到它们。通常情况下,它可以通过创建一个由所有坏块组成的文件来解决问题,确保它们不会出现在空闲池中并且永远不会被分配。那么此文件是完全不可读的。如果磁盘控制器将所有的坏块重新映射,物理转储还是能够正常工作的。 Windows 系统有分页文件(paging files) 和 休眠文件(hibernation files) 。它们在文件还原时不发挥作用,同时也不应该在第一时间进行备份。 物理转储和逻辑转储 物理转储的主要优点是简单、极为快速(基本上是以磁盘的速度运行),缺点是全量备份,不能跳过指定目录,也不能增量转储,也不能恢复个人文件的请求。因此句大多数情况下不会使用物理转储,而使用逻辑转储。 逻辑转储(logical dump)从一个或几个指定的目录开始,递归转储自指定日期开始后更改的文件和目录。因此,在逻辑转储中,转储磁盘上有一系列经过仔细识别的目录和文件,这使得根据请求轻松还原特定文件或目录。 既然逻辑转储是最常用的方式,那么下面就让我们研究一下逻辑转储的通用算法。此算法在 UNIX 系统上广为使用,如下图所示 待转储的文件系统,其中方框代表目录,圆圈代表文件。黄色的项目表是自上次转储以来修改过。每个目录和文件都被标上其 inode 号。 此算法会转储位于修改文件或目录路径上的所有目录(也包括未修改的目录),原因有两个。第一是能够在不同电脑的文件系统中恢复转储的文件。通过这种方式,转储和重新存储的程序能够用来在两个电脑之间传输整个文件系统。第二个原因是能够对单个文件进行增量恢复。 逻辑转储算法需要维持一个 inode 为索引的位图(bitmap),每个 inode 包含了几位。随着算法的进行,位图中的这些位会被设置或清除。算法的执行分成四个阶段。第一阶段从起始目录(本例为根目录)开始检查其中所有的目录项。对每一个修改过的文件,该算法将在位图中标记其 inode。算法还会标记并递归检查每一个目录(不管是否修改过)。 在第一阶段结束时,所有修改过的文件和全部目录都在位图中标记了,如下图所示 理论上来说,第二阶段再次递归遍历目录树,并去掉目录树中任何不包含被修改过的文件或目录的标记。本阶段执行的结果如下 注意,inode 编号为 10、11、14、27、29 和 30 的目录已经被去掉了标记,因为它们所包含的内容没有修改。它们也不会转储。相反,inode 编号为 5 和 6 的目录本身尽管没有被修改过也要被转储,因为在新的机器上恢复当日的修改时需要这些信息。为了提高算法效率,可以将这两阶段的目录树遍历合二为一。 现在已经知道了哪些目录和文件必须被转储了,这就是上图 b 中标记的内容,第三阶段算法将以节点号为序,扫描这些 inode 并转储所有标记为需转储的目录,如下图所示 为了进行恢复,每个被转储的目录都用目录的属性(所有者、时间)作为前缀。 最后,在第四阶段,上图中被标记的文件也被转储,同样,由其文件属性作为前缀。至此,转储结束。 从转储磁盘上还原文件系统非常简单。一开始,需要在磁盘上创建空文件系统。然后恢复最近一次的完整转储。由于磁带上最先出现目录,所以首先恢复目录,给出文件系统的框架(skeleton),然后恢复文件系统本身。在完整存储之后是第一次增量存储,然后是第二次重复这一过程,以此类推。 尽管逻辑存储十分简单,但是也会有一些棘手的问题。首先,既然空闲块列表并不是一个文件,那么在所有被转储的文件恢复完毕之后,就需要从零开始重新构造。 另外一个问题是关于链接。如果文件链接了两个或者多个目录,而文件只能还原一次,那么并且所有指向该文件的目录都必须还原。 还有一个问题是,UNIX 文件实际上包含了许多 空洞(holes)。打开文件,写几个字节,然后找到文件中偏移了一定距离的地址,又写入更多的字节,这么做是合法的。但两者之间的这些块并不属于文件本身,从而也不应该在其上进行文件转储和恢复。 最后,无论属于哪一个目录,特殊文件,命名管道以及类似的文件都不应该被转储。 4.4 文件系统的一致性 影响可靠性的一个因素是文件系统的一致性。许多文件系统读取磁盘块、修改磁盘块、再把它们写回磁盘。如果系统在所有块写入之前崩溃,文件系统就会处于一种不一致(inconsistent)的状态。如果某些尚未写回的块是索引节点块,目录块或包含空闲列表的块,则此问题是很严重的。 为了处理文件系统一致性问题,大部分计算机都会有应用程序来检查文件系统的一致性。例如,UNIX 有 fsck;Windows 有 sfc,每当引导系统时(尤其是在崩溃后),都可以运行该程序。 可以进行两种一致性检查:块的一致性检查和文件的一致性检查。为了检查块的一致性,应用程序会建立两张表,每个包含一个计数器的块,最初设置为 0 。第一个表中的计数器跟踪该块在文件中出现的次数,第二张表中的计数器记录每个块在空闲列表、空闲位图中出现的频率。 然后检验程序使用原始设备读取所有的 inode,忽略文件的结构,只返回从零开始的所有磁盘块。从 inode 开始,很容易找到文件中的块数量。每当读取一个块时,该块在第一个表中的计数器 + 1,应用程序会检查空闲块或者位图来找到没有使用的块。空闲列表中块的每次出现都会导致其在第二表中的计数器增加。 如果文件系统一致,则每一个块或者在第一个表计数器为 1,或者在第二个表计数器中为 1,如下图所示 但是当系统崩溃后,这两张表可能如下所示 其中,磁盘块 2 没有出现在任何一张表中,这称为 块丢失(missing block)。尽管块丢失不会造成实际的损害,但它的确浪费了磁盘空间,减少了磁盘容量。块丢失的问题很容易解决,文件系统检验程序把他们加到空闲表中即可。 有可能出现的另外一种情况如下所示 其中,块 4 在空闲表中出现了 2 次。这种解决方法也很简单,只要重新建立空闲表即可。 最糟糕的情况是在两个或者多个文件中出现同一个数据块,如下所示 比如上图的磁盘块 5,如果其中一个文件被删除,块 5 会被添加到空闲表中,导致一个块同时处于使用和空闲的两种状态。如果删除这两个文件,那么在空闲表中这个磁盘块会出现两次。 文件系统检验程序采取的处理方法是,先分配一磁盘块,把块 5 中的内容复制到空闲块中,然后把它插入到其中一个文件中。这样文件的内容未改变,虽然这些内容可以肯定是不对的,但至少保证了文件的一致性。这一错误应该报告给用户,由用户检查受检情况。 除了检查每个磁盘块计数的正确性之外,文件系统还会检查目录系统。这时候会用到一张计数器表,但这时是一个文件(而不是一个块)对应于一个计数器。程序从根目录开始检验,沿着目录树向下查找,检查文件系统的每个目录。对每个目录中的文件,使其计数 + 1。 注意,由于存在硬连接,一个文件可能出现在两个或多个目录中。而遇到符号链接是不计数的,不会对目标文件的计数器 + 1。 在检验程序完成后,会得到一张由 inode 索引的表,说明每个文件和目录的包含关系。检验程序会将这些数字与存储在文件 inode 中的链接数目做对比。如果 inode 节点的链接计数大户目录项个数,这时即使所有文件从目录中删除,这个计数仍然不是 0 ,inode 不会被删除。这种错误不严重,却因为存在不属于任何目录的文件而浪费了磁盘空间。 另一种错误则是潜在的风险。如果同一个文件链接两个目录项,但是 inode 链接计数只为 1,如果删除了任何一个目录项,对应 inode 链接计数变为 0。当 inode 计数为 0 时,文件系统标志 inode 为 未使用,并释放全部的块。这会导致其中一个目录指向一未使用的 inode,而很有可能其块马上就被分配给其他文件。 4.5 文件系统性能 访问磁盘的效率要比内存满的多,是时候又祭出这张图了 从内存读一个 32 位字大概是 10ns,从硬盘上读的速率大概是 100MB/S,对每个 32 位字来说,效率会慢了四倍,另外,还要加上 5 - 10 ms 的寻道时间等其他损耗,如果只访问一个字,内存要比磁盘快百万数量级。所以磁盘优化是很有必要的,下面我们会讨论几种优化方式 高速缓存 最常用的减少磁盘访问次数的技术是使用 块高速缓存(block cache) 或者 缓冲区高速缓存(buffer cache)。高速缓存指的是一系列的块,它们在逻辑上属于磁盘,但实际上基于性能的考虑被保存在内存中。 管理高速缓存有不同的算法,常用的算法是:检查全部的读请求,查看在高速缓存中是否有所需要的块。如果存在,可执行读操作而无须访问磁盘。如果检查块不再高速缓存中,那么首先把它读入高速缓存,再复制到所需的地方。之后,对同一个块的请求都通过高速缓存来完成。 高速缓存的操作如下图所示 由于在高速缓存中有许多块,所以需要某种方法快速确定所需的块是否存在。常用方法是将设备和磁盘地址进行散列操作,然后,在散列表中查找结果。具有相同散列值的块在一个链表中连接在一起(这个数据结构是不是很像 HashMap?),这样就可以沿着冲突链查找其他块。 如果高速缓存已满,此时需要调入新的块,则要把原来的某一块调出高速缓存,如果要调出的块在上次调入后已经被修改过,则需要把它写回磁盘。这种情况与分页非常相似,所有常用的页面置换算法我们之前已经介绍过,如果有不熟悉的小伙伴可以参考 比如 FIFO 算法、第二次机会算法、LRU 算法、时钟算法、老化算法等。它们都适用于高速缓存。 块提前读 第二个明显提高文件系统的性能是,在需要用到块之前,试图提前将其写入高速缓存,从而提高命中率。许多文件都是顺序读取。如果请求文件系统在某个文件中生成块 k,文件系统执行相关操作并且在完成之后,会检查高速缓存,以便确定块 k + 1 是否已经在高速缓存。如果不在,文件系统会为 k + 1 安排一个预读取,因为文件希望在用到该块的时候能够直接从高速缓存中读取。 当然,块提前读取策略只适用于实际顺序读取的文件。对随机访问的文件,提前读丝毫不起作用。甚至还会造成阻碍。 减少磁盘臂运动 高速缓存和块提前读并不是提高文件系统性能的唯一方法。另一种重要的技术是把有可能顺序访问的块放在一起,当然最好是在同一个柱面上,从而减少磁盘臂的移动次数。当写一个输出文件时,文件系统就必须按照要求一次一次地分配磁盘块。如果用位图来记录空闲块,并且整个位图在内存中,那么选择与前一块最近的空闲块是很容易的。如果用空闲表,并且链表的一部分存在磁盘上,要分配紧邻的空闲块就会困难很多。 不过,即使采用空闲表,也可以使用 块簇 技术。即不用块而用连续块簇来跟踪磁盘存储区。如果一个扇区有 512 个字节,有可能系统采用 1 KB 的块(2 个扇区),但却按每 2 块(4 个扇区)一个单位来分配磁盘存储区。这和 2 KB 的磁盘块并不相同,因为在高速缓存中它仍然使用 1 KB 的块,磁盘与内存数据之间传送也是以 1 KB 进行,但在一个空闲的系统上顺序读取这些文件,寻道的次数可以减少一半,从而使文件系统的性能大大改善。若考虑旋转定位则可以得到这类方法的变体。在分配块时,系统尽量把一个文件中的连续块存放在同一个柱面上。 在使用 inode 或任何类似 inode 的系统中,另一个性能瓶颈是,读取一个很短的文件也需要两次磁盘访问:一次是访问 inode,一次是访问块。通常情况下,inode 的放置如下图所示 其中,全部 inode 放在靠近磁盘开始位置,所以 inode 和它所指向的块之间的平均距离是柱面组的一半,这将会需要较长时间的寻道时间。 一个简单的改进方法是,在磁盘中部而不是开始处存放 inode ,此时,在 inode 和第一个块之间的寻道时间减为原来的一半。另一种做法是:将磁盘分成多个柱面组,每个柱面组有自己的 inode,数据块和空闲表,如上图 b 所示。 当然,只有在磁盘中装有磁盘臂的情况下,讨论寻道时间和旋转时间才是有意义的。现在越来越多的电脑使用 固态硬盘(SSD),对于这些硬盘,由于采用了和闪存同样的制造技术,使得随机访问和顺序访问在传输速度上已经较为相近,传统硬盘的许多问题就消失了。但是也引发了新的问题。 磁盘碎片整理 在初始安装操作系统后,文件就会被不断的创建和清除,于是磁盘会产生很多的碎片,在创建一个文件时,它使用的块会散布在整个磁盘上,降低性能。删除文件后,回收磁盘块,可能会造成空穴。 磁盘性能可以通过如下方式恢复:移动文件使它们相互挨着,并把所有的至少是大部分的空闲空间放在一个或多个大的连续区域内。Windows 有一个程序 defrag 就是做这个事儿的。Windows 用户会经常使用它,SSD 除外。 磁盘碎片整理程序会在让文件系统上很好地运行。Linux 文件系统(特别是 ext2 和 ext3)由于其选择磁盘块的方式,在磁盘碎片整理上一般不会像 Windows 一样困难,因此很少需要手动的磁盘碎片整理。而且,固态硬盘并不受磁盘碎片的影响,事实上,在固态硬盘上做磁盘碎片整理反倒是多此一举,不仅没有提高性能,反而磨损了固态硬盘。所以碎片整理只会缩短固态硬盘的寿命。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    C语言与CPP编程 磁盘 文件系统 共享文件

  • 熔断的意义和适用场景,你真的清楚吗?

    曾任职于阿里巴巴,每日优鲜等互联网公司,任技术总监,15年电商互联网经历 关于熔断,网上的文章很多,但是真正讲明白的文章寥寥无几。很多作者互相抄袭,缺乏自己的认知和理解。 分享熔断之前,咱们先说说大家更熟悉的Timeout。Timeout和熔断有关系吗?别急,往下看就清楚啦! 有两个作用:并发不高时timeout可以避免线程阻塞带来的性能问题,但是 现在轮到熔断大显身手了。 熔断是什么? 实际上,熔断的意义上游服务快速失败(Fail Fast) ,快速失败,。避免因为,导致请求线程持续等待,进而导致线程池线程和CPU资源耗尽,进而导致A无响应甚至整条调用链故障。 对于下游服务B:熔断后,请求被A拦截,不再发送到B,B压力得到缓解,避免了仍旧存活的B被压垮,B得到了保护。 熔断状态流转过程: 假设上游服务是A,下游服务是B,A调用B。 熔断生命周期包括三个状态:关闭,半开启,开启。 关闭状态->开启状态,A调用B请求失败次数在设定时间内达到阈值,开启熔断 开启状态->半开启状态,熔断开启后,熔断器根据设定时间间隔定期自动进入半开启状态 出于系统性能问题的考虑,并发高时timeout就不能解决问题啦。这时可以用熔断 只要是服务之间的调用,并且能设计合理的获取返回值的方案(返回值可以是默认值,或者通过一种后备(Fallback)方案获取的值),一般业务场景都可以做熔断处理。下单减库存时,如果库存服务挂了,开启熔断后,我们可以直接从订单服务取库存,订单服务取不到库存,按扣减库存失败处理。这个可以做为后备(Fallback)方案。 有哪些开源实现 Hystrix, 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    架构师社区 服务 熔断 上游服务

  • 国企程序员有多香?这是一个普通程序员在国企的每日工作清单!

    都说在国企工作很舒服,没有互联网大厂的996,工作节奏慢,工作压力小,是个适合养老的地方,真的是这样吗? 一位普通程序员分享了自己在国企的一天工作情况,来看看国企的日子是不是真如传说中那么香。 在清单中可以看出,这位程序员过着955生活,每天工作时间不超过六个小时,不仅有充分的时间运动、散步、休息, 晚上还有时间看看电视、看视频、写日记等,与那些996的苦逼互联网程序员相比,日子确实很香。网友们也纷纷表示羡慕,这是什么神仙生活! 还有人问“咱公司叫啥”,“咱公司还招人吗”?如果还有机会,请带我一个! 对楼主的经历,网友盖章认证“真实”。 有人每天下午5点半准时跑路。 有人的上班时间是早晨八点半到中午十一点半,下午一点半到五点半,感觉自己在混吃等死。 有人说自己在国企的亲戚就这么爽,虽然工资低了点,但上班打游戏没人管,只要在工位坐着就行,下午五点下班,一个月正经干活时间也就20多个小时。 不过也有人觉得楼主的生活还不够惬意,早晨起得太早,工作比较忙,还有内部站会,要是能取消就好了。 楼主下班时间也太晚,应该四点就下班买菜做饭去,更有网友说以前在国企下午三点多就下班了。 有人说自己之前在国企,上午十点左右到公司,中午十一点多去吃饭,午休到下午三点,四点多就下班了……小编算了一下,这位同学每天的平均工作时长也就两个多小时,比楼主舒服多了。 也有人说国企工作虽然清闲,但工资少,有可能楼主月工资只有三千五。 国企工资确实不如互联网高,但也不至于才三千五,有人说自己所在的国企工资8000元,房补餐补油补都是偷着发,工作955从来不加班,过节费劳保从来不断,预算之内的吃喝还可以报销,当然要有编制才行。虽然没有互联网行业动不动就年薪百万,但听起来福利待遇还是很不错的。 如果有程序员不想在互联网公司996,想找个清闲舒服的地方养老,国企确实是个不错的选择。现在许多国企都成立子公司搞IT,以后带编制的国企会越来越吃香。 但有意求职国企的人也要注意,不是所有国企都不错,如今市面上招聘的国企良莠不齐,鱼龙混杂,大家一定要擦亮眼睛,以免被那些工资低、待遇差还要加班的伪国企给骗了,浪费自己的时间和精力。 如果你有兴趣从互联网上岸国企,可以在文章下方留言告诉我们,后续我们还会写一篇关于如何鉴别真假国企的文章,请持续关注!

    架构师社区 国企 工作清单 程序员

  • 随着物联网的发展,企业将寻求低功耗广域网的解决方案

    随着物联网的发展,企业将寻求低功耗广域网的解决方案

    随着物联网的发展,企业面临着巨大的机遇,为了更好的发展,企业不断地探索,将寻求低功耗广域网的解决方案,以实现在格格前景光明的领域中占据一席之地。 机器对机器通信(M2M)和物联网(IOT)的概念已经永久地改变了当今每一项业务的价值链。随着2G网络即将关闭,各组织一直在寻找可以服务于广泛的工业连接网络,而这必然为低功耗广域网(LPWAN)提供了广泛的应用机会。SigFox、LoRa等低功耗广域网比以太网、蜂窝网络、卫星网络、蓝牙和无线网络等现有连接方式更受欢迎,因为它们能够解决传统网络技术的关键限制,如距离、成本和电池寿命。 根据《全球市场观察》预计,到2025年,全球LPWAN市场将超过650亿美元,其主要原因是物联网的日益扩张。随着低成本传感器的部署和实施,企业、消费者、城市和整个国家都有机会通过提供数据来改变经济、市场和成果,从而彻底改变价值创造。 让我们来看看LPWAN技术的一些流行用例。 智慧城市 智慧城市可能是近代最雄心勃勃的概念,在这个概念中,整个城市都被物联网传感器连接在一起。智慧城市融合了先进的绿色技术,创造一个可持续的环境,既能提供高生活水平,又能引领碳中和之路,并提供经济高效的解决方案。这是一个健康、节能的城市,并尽可能多地使用可再生能源,包括生物质和废物。 智慧城市中物联网和M2M的部署涵盖了多种公共服务和基础设施,包括公共交通、智能交通管理(如停车位管理)、交通流量监控、拥堵收费和道路收费、交通信号灯和执法摄像头、环境和公共安全以及公共场所广告。 为了能够连接所有物联网设备,各种无线局域网和广域网(如低功耗蓝牙、ZigBee、Wi-Fi和蜂窝技术)的重要性不言而喻。不过,现在,像Sigfox、LoRa、LTE-M和NB-IoT这样的低功率广域网已经成为物联网的催化剂,并且越来越多地部署在停车场、公用设施、污染监测以及需要无线通信的各种应用中。据《世界新闻报》报道,在智慧城市的数十亿连网设备中,有60%适用于低功耗广域网技术。 汽车制造业 随着全球汽车需求的不断增长,汽车制造商面临着提高运营效率、保证产品质量和准时交货以保持竞争优势的巨大压力,而物联网和其他数字技术极大地弥补了这一压力。相当多的全球汽车行业参与者已经加入了数字化转型潮流,并利用诸如联网和自动驾驶汽车、车辆远程信息处理、车队管理或驾驶员辅助等趋势。 尽管LPWAN对汽车制造商来说仍是一个相对较新的领域,但它是物联网的关键推动者之一,也是一个潜在的转型引擎。LPWAN专为由众多电池供电传感器的高性价比遥测数据通信而设计,旨在为汽车行业的高级云分析带来全新的运营透明度和效率。 智能计量 智能电表是智慧城市的基本特征,通常部署在智能建筑、建筑能源管理、家庭自动化和连网电力解决方案中,这些因素对公用事业行业的数字化转型至关重要。鉴于智能电表的使用特征,它们通常使用LPWAN连接。事实上,据福布斯估计,公用事业是第二大物联网市场,因此很明显,智能电表有望成为扩展LPWAN连接的主要驱动因素之一。 石油天然气和采矿业 采矿、石油天然气行业需要远程管理连网设备。支持物联网的LPWAN可穿戴设备和技术可以通过监控整个价值链的绩效来改变采矿作业并帮助改善安全状况。LoRa技术已经开始部署,用于连接位于危险偏远地区的矿山,这些地区以前被认为是具有挑战性或成本过高,无法有效开采。LoRa网关凭借其独特的穿透能力,部署在建筑物或塔上的速度相对较快且成本较低,可以连接到15公里以外的传感器或部署在地下的仪表。 LPWAN的技术优势和能力正迅速得到全球认可。开发人员和系统集成商需要识别和响应客户对运营、流程和投资回报率(ROI)改进的需求,他们已经意识到物联网解决方案对其持续成功的重要性。 在核心制造商和终端行业的共同支持下,LPWAN技术的采用可能会在未来几年继续增长。 物联网的热潮对众多企业带来了机会,对于我们来说是一把双刃剑,既是机会又是挑战需要我们认真的对待。

    单片机 物联网 机遇

  • 我们期待5G全面服务可望2021年之后明朗

    我们期待5G全面服务可望2021年之后明朗

    随着现代社会的发展,人们对网络的要求越来越高,5G时代一点一点的渗透进我们的生活,给我们带来了不一样的体验,虽然5G在生活中还没有普遍存在,但在有些方面我们已经体会到它带个我们的便捷,我们期待着5G社会的到来。 资策会MIC预估,明年全球信息通讯产业可留意5G、智能音箱、情感辨识、区块链等7大趋势;其中,5G全面服务可望2020年之后明朗。 资策会产业情报研究所(MIC)今天上午举办2019信息通讯国际局势与7大前景记者会。展望明年全球信息通讯产业,MIC预估有5G、物联网、人工智能与区块链等7大趋势。 其中在5G部分,MIC资深产业分析师钟晓君表示,5G商转倒数,频谱释照与基础网络部署进入冲刺,预期明年5G商转可观察5G释照进度、基础网络整备状况与移动服务时机3大重点。 MIC预估明年超过30个国家规划完成5G频谱释照,各国对5G时代基地台建设审查倾向宽松、部分国家鼓励共建共构,电信业者在明年投入5G基地台时,也需投入更多的光纤建设。明年全面性的5G移动服务时机还未到,预期2020年之后可望明朗。 在家用上网部分,MIC资深产业分析师徐子明预估,明年5G家庭上网有机会与Wi-Fi 6(802.11ax)标准结合,深耕家用固定无线接取(FWA)市场。 他指出,5G FWA部分国家电信营运商跟进中,关键是否有足够动力积极推动布建,布建成本相对固网宽带现阶段仍偏高。 观察边缘运算,MIC资深产业分析师施柏荣预期,明年边缘运算可实现平行分散的无服务器环境(Serverless),未来有望响应既多元且复杂的实时情境,明年5G商转可成为发展边缘运算的助力。未来将有更多依照客户端逻辑客制化的混合运算架构(FlexibleArchitecture)。 在特殊应用芯片(ASIC)芯片部分,MIC资深产业分析师叶贞秀预估,明年ASIC芯片需求看涨,台湾IC设计业者可望受惠。包括人工智能发展开启云端与终端客制化芯片的市场需求,包括联发科、凌阳等台厂成立ASIC部门进行扩张。 观察智能音箱市场,MIC产业分析师曾巧灵表示,明年智能音箱市场可望持续成长,以美中两大市场为核心,4大厂包括亚马逊(Amazon)、谷歌(Google)、阿里巴巴、百度四强鼎立。其中Amazon与Google几乎囊括8成以上美国市场。 Amazon与Google未来瞄准美国以外市场,阿里巴巴利用电商优势,整合零售、支付等服务,主打语音购物;百度持续强化语音助理功能,结合智能手机品牌厂商,奠定语音助理用户基础。 观察情感辨识应用,MIC资深产业分析师杨政霖表示,明年情感辨识发展将进入情绪辨识2.0,呈现应用多元与情绪优化情境。明年后应用领域可延伸到影视、零售、医疗、教育、电话客服等领域。2020年后可期待例如家用型机器人、智能音箱等、甚至聊天机器人具备情感辨识功能。 展望区块链,MIC产业分析师高志昕预期,明年区块链发展将持续迈向区块链3.0,从聚焦金融应用扩散到医疗、物流、能源、物联网、农业、食品等产业。明年持续偏向小规模采用与试验性阶段,预估2025年后区块链技术应用可望大规模扩散。 2021年已经开始,我们相信5G时代也已经开始,我们期待它带给我们的变化,也期待着它带给我们不一样的体验。

    单片机 互联网 5G

  • 物联网连接在发展中将会遇到哪些挑战?

    物联网连接在发展中将会遇到哪些挑战?

    随着现在社会的发展,物联网的发展也越来越好,不断地完善,以更好地服务于人类生活,但凡事在发展中都会遇到想象不到的一系列的挑战,那么我们现在来分一下物联网在发展中遇到的挑战有哪些。 连接性是物联网设备的关键。毕竟,连接性使简单的东西变成物联网的一部分。连接性使物联网设备收集的数据能够得到分析、组织和实际使用。 在实验室中开发新的物联网设备时,实现无缝、低延迟的连接非常简单,但这些是实验室条件。在现实世界中,连接性对于新设备的性能至关重要,因此您需要正确处理它。 那么,提供可靠物联网连接的主要挑战是什么?这里有三个主要方面需要考虑。 挑战1、功耗 大多数物联网设备相对较小且简单,依靠电池供电。此外,它们可能部署在无法定期维护或升级的地方。因此,能源效率至关重要。 最大化物联网设备和生态系统的能源效率取决于选择正确的无线接入技术和射频(RF)设计。这样既可以确保首先将功耗降至最低,又可以确保没有不必要的射频功率输出。 挑战2、带宽 众所周知,物联网是建立在数据基础之上。任何物联网设备都需要能够收集和传输数据,并有可能从其他设备或中央控制单元收集数据。但带宽会因您所使用的连接协议类型,以及最终物联网部署的规模而异。(来自物联之家网)您的设备将会是成千上万个向您服务器发送请求和响应信号的设备之一吗?您的设备是要散布在广阔的地理区域,还是仅限于一栋建筑? 然后是您物联网设备将要传输的数据类型。简单的数据,例如,在固定时间测量温度或重量——意味着要传输的数据既小又简单。对特定参数(如位置)的持续跟踪需要更多带宽。而丰富的数据,如音频和视频流,则需要大量带宽。 挑战3、安全 在任何收集、存储、分析或传输数据的环境中,安全性都是一个关键的考虑因素。然而,它在物联网方面具有一些特殊的细微差别。 首先,物联网设备从本质上讲是提供对网络访问权限的端点,因此,它们可能会成为恶意攻击者的目标。此外,许多物联网设备太小、太简单,自身无法集成复杂的安全保护机制。这些设备需要经过适当的验证和授权才能连接到相关网络,并且它们发送和接收的数据必须始终加密。 物联网生态系统的连接性是灵活的,并且形式多种多样。在开发和部署物联网网络时,组织可以选择使用适当的协议和技术。然而,在做出这些决定时,重要的是要考虑如何确保高效的功耗、足够的带宽和强大的安全性。 以上是物联网连接的三大挑战,不过我们相信物联网会找到解决这些挑战的方法。

    单片机 物联网 单片机

  • 日本5G基地台搭上红绿灯、节省成本

    日本5G基地台搭上红绿灯、节省成本

    随着5G社会的到来,我们越来越渴望它能够在我们生活中普遍存在,科学家们也在努力的运用5G为我们创造更好的福祉,来满足我们各项要求,方便我们生活,给我们的社会带来更加便宜的方式。 基于,5G通讯距离较短,因此基地台的密度必须比4G 高,自然也造成建设成本拉高。日本政府将允许NTT Docomo等三大电信业者,在交通信号(traffic signals)上架设5G基地台,希望透过国家高密度的交通信号灯来减少推出超高速网络所需的成本。该计划包含在政府的IT战略蓝图草案中,预计将于2019年6月中旬获得内阁批准。 政府认为交通信号灯计划是一种可以加速5G到来的解决方案,特别是考虑到日本的交通灯号密度高于其他国家。目前,大约200,000个交通信号是由地方政府管理。地方当局将能够利用这些网络进行车辆的自动驾驶以及自然灾害发生时的紧急通讯。交通信号进行5G设备测试,将于2020财政年度在多个城市开始执行,该项目将持续到2021年3月,目标是在2023财政年度结束时,完成全国引进的作业。相关政府机构,包括通讯部、国家警察厅、交通部和地方政府,将设立一个沟通协调会,以协助5G测试进行。 全国5G覆盖,预计需要数十万个基地站。营运商将继续使用现有的4G基地台,但由于5G信号距离较短(例如:28 GHz频段的频率只有几百米),因此该国的四家营运商需要寻找更多的位置。营运商经常为建屋顶上基地站及输电塔而伤透脑筋,因大部分可用空间已经被使用,所以将需要花时间和精力。 日本5G运营给四家电信公司,包括NTT Docomo、KDDI、SoftBank Corp.和Rakuten乐天,计划在2024财政年度的五年内投资5G约1.6兆日元(148亿美元)。 5G基地台与红绿灯搭载一起,在很大程度上能够节省开支。Docomo于2010至2018财政年度期间,在4G基地站投资约2.4兆日元,并在此期间结束时,拥有208,500个站台,相当于每个站台超过1000万日元。5G电台的平均成本应该更低,因为只需升级软件就可以使现有设施与新服务兼容,而且搭载红绿灯可以削减建设新电台所需的费用。 中央政府计划与营运商讨论这个想法,希望让他们分担与地方政府使用交通信号的成本。但有关如何划分开支的详情尚未确定。由于,传感器将被安装在灯上以建造「可信任mesh网状网络」,也就是即使当紧急情况切断互联网的连接,也可以继续传输讯息的本地网络。而营运商、警察和地方政府都有自己的私人mesh网状网络。 在当地政府能够使用配备基地站的灯来为居民提供服务。例如,在紧急情况下,可以透过信号扫描居民的My Number识别卡号将讯息传达给他们的家人,以确认居民的安全。 日本运用5G不仅节约了资源还方便了人们,更重要的是保证了人们的利益,我相信不远的将来我们运用5G会寻找到更有益处的方案。

    单片机 互联网 5G

  • 互联网的下个阶段 – 物联网(IoT),可能对Wi-Fi有破坏性影响。

    互联网的下个阶段 – 物联网(IoT),可能对Wi-Fi有破坏性影响。

    现在无论你是上班族还是学生党,无论你涉及什么样的工作领域,WiFi在我们生活中占据着重大的地位,我们无时无刻没有在依靠着它,毫不夸张的说,WiFi在我们现在带生活中是推动我们发展的重要动力,不过随着互联网的发展,物联网将逐步破坏WiFi对我们生活的影响力度。 一些新创及网络服务公司,开始发展偏离IEEE 802.11基础网络标准的应用层服务。此层提供许多新型服务,例如网状网络(Mesh Network,一组路由器协同运作,提升无线网络的涵盖率)及服务开通策略(Provisioning Tactics,无线装置链接网络的方式)。这些都是为了响应物联网的需求。 这不免令人想起用在小型、低功率的数字无线电网状网络标准Zigbee的历史。它也有一些应用层服务,专门为特定种类的装置而设计。不过却造成顾客的困扰,因其无法判断哪些设备可搭配使用。Wi-Fi恐怕正朝这方向前进。 如果考虑网状Wi-FI系统,像Eero、Google、Securifi都提供宣称可涵盖整个家庭的产品,不过其网状路由器必须全来自于同一公司才能得到保证。有些公司还理解到可在网状路由器里植入安全相关产品,使得网络存取优先权得以实现。但是到底这样的应用层与Wi-Fi标准核心之界线何在? 这里有个案例,就是Comcast公司。在2017年,它的Wi-Fi产品修改成架构在分级保护控制等服务之上。服务软件来自于新创公司Cirrent,使服务开通或新增网络组件变得比较容易。Cirrent软件不只用在Comcast的路由器,也用在Bose、Electrolux的一些消费电子装置上。 装有Cirrent软件的产品一拿到使用Comcast路由器的家庭,就能自动连上Wi-Fi网络。但这是特定厂商的片段解决方案,如果家中路由器没有Cirrent软件,即使有Electrolux洗衣机可能也会遇到连网问题。Cirrent的应用软件确实能解决常见的Wi-Fi问题,但是用户也被特定芯片商、路由器制造商、服务提供商绑住了。 “如果把这类应用看成是架在Wi-Fi标准之上的商业手段,那还有道理,”新创公司Plume的执行官Fahri Diner指出。Plume的软件是借助调整家中路由器的容量来改善覆盖率。除了Comcast之外,Plume也跟三星签约,将其产品转变成内含Plume软件的网状路由器。 Diner跟许多主要的Wi-Fi芯片商合作以确保兼容性,也开放了一些必要的程序代码。不过如果不是每家厂商都使用Plume软件的话,其动态网络优化的功能便无法在不兼容的路由器上执行。 解决厂商各自为政的唯一解答在认证Wi-Fi设备的Wi-Fi联盟身上。Cypress半导体的物联网营销副总Brian Bedrosian认为:简化的服务开通及邻近网络感知能力(即使没有Wi-Fi存取点,装置间仍能互相通信),应该成为Wi-Fi认证规格的一部分。 “相较之下,Wi-Fi有可能像Zigbee的规格那样,变成一场灾难,”Bedrosian指出。希望Wi-Fi联盟能阻止走向这样的结局。 我们还是希望WiFi联盟将不断的发展和壮大,只有不断地顺应现代社会的发展要求,还会不会走向灾难。

    单片机 Wi-Fi 物联网

  • 亿级用户基于微服务的互联网系统稳定性~

    互联网系统为大量的C端用户提供服务,如果隔三差五的出问题宕机,会严重影响用户体验,甚至导致用户流失。所以稳定性对互联网系统非常重要!接下来,我根据自己的实际经验来聊聊基于微服务的互联网系统的稳定性。 下面我们从雪崩、隔离、服务降级、突发流量、缓存、数据冗余、熔断、限流、CDN、数据库、CI、网络等方面,聊聊如何保证基于微服务的系统稳定性。 雪崩效应产生原因,如何避免? 服务化后,服务变多,调用链路变长,如果一个调用链上某个服务节点出问题,很可能引发整个调用链路崩溃,也就是所谓的雪崩效应。 举个例子,详细理解一下雪崩。如上图,现在有A,B,C三个服务,A调B,B调C。假如C发生故障,B方法1调用C方法1的请求不能及时返回,B的线程会发生阻塞等待。B会在一定时间后因为线程阻塞耗尽线程池所有线程,这时B就会无法响应A的请求。A调用B的请求不能及时返回,A的线程池线程资源也会逐渐被耗尽,最终A也无法对外提供服务。这样就引发了连锁故障,发生了雪崩。纵向:C故障引发B故障,B故障引发A故障,最终发生连锁故障。横向:方法1出问题,导致线程阻塞,进而线程池线程资源耗尽,最终服务内所有方法都无法访问,这就是“线程池污染” 为了避免雪崩效应,我们可以从两个方面考虑: 在服务间加熔断。解决服务间纵向连锁故障问题。比如在A服务加熔断,当B故障时,开启熔断,A调用B的请求不再发送到B,直接快速返回。这样就避免了线程等待的问题。当然快速返回什么,fallback方案是什么,也需要根据具体场景,比如返回默认值或者调用其他备用服务接口。你的场景异步可以采用消息队列,这样也以机器人脚本等频繁调用服务(userID限流和IP限流) 数据冗余 下单减库存时,如果库存服务挂了,我们可以直接从订单服务取库存。可以结合熔断一起使用,作为熔断的Fallback(后备)方案。 服务降级 可能很多人都听过服务降级,但是又不知道降级是怎么回事。实际上,上面说的熔断,限流,数据冗余,都属于服务降级的范畴。还有手动降级的例子,比如大促期间我们会关掉第三方物流接口,页面上也关掉物流查询功能,避免拖垮自己的服务。这种降级的例子很多。不管什么降级方式,目的都是让系统可用性更高,容错能力更强,更稳定。 可以在初始化数据时,差异化各个key的缓存失效时间,失效时间 = 一个较大的固定值 + 较小的随机值 库存和订单的缓存(Redis)和数据库需要单独部署!数据隔离后,秒杀订单和日常订单不在相同的数据库,之后的订单查询怎么展示?可以采用相应的数据同步策略。比如,在创建秒杀订单后发消息到消息队列,日常订单服务收到消息后将订单写入日常订单库。注意,要考虑数据的一致性,可以使用从业务上把秒杀和日常的售卖区分开来,把秒杀做为营销活动,要参与秒杀的商品需要提前报名参加活动,这样我们就能提前知道哪些商家哪些商品要参与秒杀,可以根据提报的商品提前生成商品详情静态页面并上传到CDN预热,提报的商品库存也需要提前预热,可以将商品库存在活动开始前预热到Redis,避免秒杀开始后大量访问穿透到数据库。服务化后,一次请求会跨多个服务,追踪问题也会变麻烦。这时就需要能够追踪整个调用链路的工具,协助我们排查问题。常见的开源全链路监控工具有(pinpoint,skywaking,cat等),以Pinpoint为例简单介绍一下: 上图是一个请求的调用栈,我们可以清晰看到一次请求调用了哪些服务和方法以及各个环节的耗时,以及发生在哪个节点。这样通过APM系统我们就能轻松定位线上性能问题和错误了! CI测试&性能测试 CI测试,持续集成测试,在我们每次提交代码到发布分支前自动构建项目并执行所有测试用例,如果有测试用例执行失败,拒绝将代码合并到发布分支,本次集成失败。CI测试可以保证上线质量,适用于用例不会经常变化的稳定业务。 性能测试,为了保证上线性能,所有用户侧功能需要进行性能测试。上线前要保证性能测试通过。而且要定期做全链路压测,有性能问题可以及时发现。 监控 包括CDN 除了提高用户访问速度之外,页面静态化之后存放到CDN,用CDN扛流量,可以大幅减少系统(源站)的访问压力。对系统稳定性非常有好处。 避免单点问题 除了服务要多点部署外,网关,数据库,缓存也要避免单点问题,至少要有一个Backup,而且要可以自动发现上线节点和自动摘除下线和故障节点。 网络带宽 避免带宽成为瓶颈,促销和秒杀开始前提前申请带宽。不光要考虑外网带宽,还要考虑内网带宽,有些旧服务器网口是千兆网口,访问量高时很可能会打满。 安全 机器人脚本防刷,在网关层对下单等接口按userID限流。

    架构师社区 互联网 系统稳定性 微服务

  • 一文探讨堆外内存的监控与回收

    引子 记得那是一个风和日丽的周末,太阳红彤彤,花儿五颜六色,96 年的普哥微信找到我,描述了一个诡异的线上问题:线上程序使用了 NIO FileChannel 的 堆内内存作为缓冲区,读写文件,逻辑可以说相当简单,但根据监控却发现堆外内存飙升,导致了 OutOfMemeory 的异常。 由这个线上问题,引出了这篇文章的主题,主要包括:FileChannel 源码分析,堆外内存监控,堆外内存回收。 问题分析&源码分析 根据异常日志的定位,发现的确使用的是 HeapByteBuffer 来进行读写,但却导致堆外内存飙升,随即翻了 FileChannel 的源码,来一探究竟: FileChannel 使用的是 IOUtil 来进行读写(只分析读的逻辑,写的逻辑行为和读其实一致,不进行重复分析) Util.getTemporaryDirectBuffer(var1.remaining()); 这个 Util 封装了更为底层的一些 IO 逻辑 public class ReadByHeapByteBufferTest { public static void main(String[] args) throws IOException, InterruptedException { File data = new File("/tmp/data.txt"); FileChannel fileChannel = new RandomAccessFile(data, "rw").getChannel(); ByteBuffer buffer = ByteBuffer.allocate(4 * 1024 * 1024); for (int i = 0; i < 1000; i++) { Thread.sleep(1000); new Thread(new Runnable() { @Override public void run() { try { fileChannel.read(buffer); buffer.clear(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } } 运行一段时间后,我们观察下堆外内存的使用情况 如上图左所示,堆外内存的确开始疯涨了,符合我们的预期,堆外缓存和线程绑定,当线程非常多时,即使只使用了 4M 的堆内内存,也可能会造成极大的堆外内存膨胀,在中间发生了一次断崖,推测是线程执行完毕 or GC,导致了内存的释放。 知晓了这一点,相信大家今后使用堆内内存时可能就会更加注意了,我总结了两个注意点: 使用 HeapByteBuffer 还需要经过一次 DirectByteBuffer 的拷贝,在追求极致性能的场景下是可以通过直接复用堆外内存来避免的。 多线程下使用 HeapByteBuffer 进行文件读写,要注意 问题深究 public class WriteByDirectByteBufferTest { public static void main(String[] args) throws IOException, InterruptedException { ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 1024); System.in.read(); buffer = null; new CountDownLatch(1).await(); } } 结论:变量虽然置为了 null,但内存依旧持续占用。 CASE 2:分配 1G DirectByteBuffer,等待用户输入后,赋值为 null,手动触发 GC,之后阻塞持续观察堆外内存变化 public class WriteByDirectByteBufferTest { public static void main(String[] args) throws IOException, InterruptedException { ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 1024); System.in.read(); ((DirectBuffer) buffer).cleaner().clean(); new CountDownLatch(1).await(); } } 结论:手动回收可以立刻释放堆外内存,不需要等待到 GC 的发生。 对于 MappedByteBuffer 这个有点神秘的类,它的回收机制大概和 DirectByteBuffer 类似,体现在右边的 Mapped 之中,我们就不重复 CASE1 和 CASE2 的测试了,直接给出结论,在 GC 发生或者操作系统主动清理时 MappedByteBuffer 会被回收。但也不是不进行测试,我们会对 MappedByteBuffer 进行更有意思的研究。 CASE 4:手动回收 MappedByteBuffer。

    架构师社区 回收 源码 堆外内存

  • 爱奇艺员工拿到北京户口后辞职,被判赔偿公司十万!

    许多互联网大厂有北京落户的名额,这也是大厂招揽人才的重要福利之一。但许多人拿到户口以后却不按合同履行义务,而是选择辞职走人,也因此引发了一系列纠纷。 爱奇艺员工彭某就因为落户后离职被公司起诉,最终被判赔付10万元。彭某于2018年7月入职爱奇艺,双方约定彭某将不间断地在爱奇艺工作5年,爱奇艺为彭某办理北京落户。2019年12月彭某落户成功,2020年2月底,彭某因为个人原因提出辞职,并于3月27日离职。爱奇艺认为他违背诚信,要求他赔偿16.6万,彭某辩称自己是因为受到公司不公正待遇才辞职,与北京户口无关。 法院判决彭某应赔付爱奇艺10万元,彭某和爱奇艺均不服,提起上诉,二审判决维持原判。据报道,彭某离职前在爱奇艺的税前月工资将近三万元。 网友们表示,像彭某这样的情况并不少见,落户完赔个违约金,相当于买个户口。 十万买个北京户口不仅不吃亏,甚至还血赚了,彭某这是钻法律的空子,还上诉什么? 许多网友认为彭某的做法不道德,签合同之前就知道条件,拿到户口就走,这不是过河拆桥吗?为了自己的一己私利把别人的路给堵上了。 网友说,很多有利于劳动者的政策就是被这样的人玩坏的,最后相关部门发现有漏洞,直接一刀切,苦的还是广大劳动者。 有人觉得爱奇艺也有问题,合同本身不够完善,以后再遇到类似情况,就在合同里写明:办理户口后,因员工自己原因离职的,工作不满多少年就要赔偿公司每年多少钱。 也可以工作年限满五年再办理入京手续。 或者把违约代价定得足够大,比如100万,这样对方想违约时才会有所顾虑。 也有人说,大家不要只看到彭某拿了户口,看不到他工作中的难处,如果发展空间大,不加班不压榨,待遇又好,谁会离职呢? 爱奇艺有没有因为他不敢轻易离职而把他当奴隶一起欺压过?现实中吃准了某些员工不敢离职而肆意压榨的企业太多了。 和其他员工相比,彭某可能工资更低,工作更多,入职前承诺的福利估计除了户口都没给落实。 还有爱奇艺内部员工透露,彭某在爱奇艺遇到了一个人品有问题的领导,不然也不至于离职。虽然我们不得知具体内情,但从这寥寥数语中也能猜想彭某的日子不好过。 当然,出现这种新闻的根本原因还是户口制度,有人问,十万元买个北京户口大家都说值,难道北京人一出生就比外地人身价高10万块钱? 还有人呼吁,早日放开户口让人口自由流动吧!如此就再也不会有这种情况发生了。 彭某这样的情况在北京并不少见,想方设法拿到北京户口,然后辞职走人,用比较低廉的违约金来换取一个北京户口,这是许多人选择的捷径。 所谓捷径,自然是性价比较高的道路,但夜路走多了难免会遇到鬼。彭某算是比较幸运,才赔偿十万就能结束纠纷,相比他在爱奇艺挣到的工资,这不是一个太大的数字。其他人未必有他这么幸运,有人需要赔偿更多钱,有人根本没法辞职,还有人被影响到后续找工作发展。 同为打工人,我们愿意相信其中有些人不是过河拆桥的白眼狼,而是真的在公司被压榨被欺负,忍无可忍才选择了离开。但无论如何这样的人和事多了,能提供户口的企事业单位会越来越收紧条件,甚至像网友所说,有关部门一刀切,后来人也就无法再拿到户口指标。 对单位而言,给户口不是终极目的,留住人才才是,不能给了户口就拼命压榨员工。对人才而言,拿到户口也不是终极目的,努力工作实现价值,在北京立足生存下去才是。如果工作本身有价值,工资待遇也合适,谁会选择离职呢? 只能希望今后这样的事越来越少。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

    架构师社区 爱奇艺

  • 单片机的I2C和SPI通信的含义

    单片机的I2C和SPI通信的含义

    I2C和SPI是两种不同的通信协议。 当我们听到这个协议时,它似乎是不可实现的。事实上,协议只是人们定义的一个标准。我们只需要按照这个标准去做。例如,如果公司说我们早上9点上班,我们9点上班,或者我们扣工资,这就是协议。 最常用的I2C通信芯片是EEPROM芯片,如ATMEL AT24CXX系列,除此之外还有该芯片的其他一些功能。使用SPI通信的芯片有外部闪存芯片,以及一些用于其他功能的芯片。 I2C通信需要两个引脚:SDA SCL。SCL是时钟引脚,SDA是数据引脚。 (这是一个EEPROM芯片。) (这是时钟芯片。) SPI通信需要3引脚或4引脚:CS SCK MOSI MISO。SPI通讯芯片销名字不一定是名字,可能有其他的名字,但意思是一样的,比如莫西人销手段“主机机器的输出输入”,SPI接口芯片可能SDI写的,就像SPI设备作为从机,所以它的SDI,这意味着“从机数据输入插口。 SPI通信过程如下:将CS引脚下拉,然后由SCK输出时钟,然后在MOSI引脚上输出数据,在MISO上获得数据。 这是一个SPI FLASH芯片,DO是MISO, DI是MOSI, CLK是SCK,功能相同,只是名字不同。 这是一个带有SPI接口的ADC芯片,Dout是MISO, DCLOCK是SCK,这个芯片有3个SPI引脚。 大多数MCU有I2C端口和SPI端口,可能有几个I2C端口和SPI端口。但是,没有I2C端口和SPI端口的单片机,也可以通过普通引脚来模拟它们的定时通信。 另外,如果你是一个初学者,一定要学会使用通用的pin模拟,以更深入地理解他们通信的本质。 整个通信过程实际上就是对引脚电平进行控制和检测的过程。也就是说,第一天学习控制单片机的引脚电平和检测单片机的引脚电平。因此,I2C通信和SPI通信并不困难。 让我告诉你一个简单的沟通过程。例如,我们将下面的通信名称称为KJLWT。这个名字看起来很有趣,但实际上是中文“technology old naughty boy”的首字母缩写。主要是让大家明白,这个名字是用来吓唬人的。 我们用两条线通信,一条时钟线和一条数据线。时钟线实际上是用来产生脉冲波形的,更直接的是引脚高低信号,如下图所示: (这是时钟信号) 例如,我们规定当时钟引脚高时,读取数据引脚的电平,并且需要8个连续的时钟来读取一个字节。在数据方面,如何给出数据呢?就像那样简单,数据馈送端,当一个低电平被检测到,表示数据在数据引脚上被发送的位。例如,数字0x88以二进制形式写入为10001000。让我们来看看传输这些数据的过程:从机器检测时钟针,检测到一个下降沿(即从高到低水平),数据发送的bit7体现数据大头针,比如bit7是1,1000年,1000年,数据销高水平,高水平的的主机时钟针,针测试数据,记录,从机器再次发现时钟的下降沿销后,和数据bit6反映了数据的别针,由于1000 1000 bit6是0,所以机器的数据确定,然后时钟引脚为高电平,主机检测数据引脚的高电平和低电平,然后记录位6…这样做八次,一个字节就可以从从机传送到主机。是容易的吗? 时钟的速度,也就是数据传输的速度,例如,如果脉冲周期是1秒,也就是1Hz,那么它需要8秒来传输一个字节;如果脉冲周期为1毫秒,即1 kHz,则输出一个字节只需要8毫秒。所以你知道交流的速度意味着什么,对吧? I2C通信,SPI通信,在我刚才展示的例子之上再多一点协议。对于特定的协议,您可以查看任何I2C和SPI通信接口芯片,并查看序列图。我们要做的就是利用单片机的引脚来进行计时。

    单片机 单片机 SPI I2C

  • 如何设置单片机STM32的引脚

    如何设置单片机STM32的引脚

    单片机 单片机 STM32 引脚

  • 如何用kiCad制作电路原理图

    如何用kiCad制作电路原理图

    3.绘制电路原理图 在本节中,我们将学习如何使用KiCad制作电路原理图。 3.1使用Eeschema 1. Windows操作系统运行KiCad。打开KiCad. Exe,在Linux操作系统的终端输入KiCad打开KiCad。打开后,您将看到KiCad主屏幕。在主界面中,您可以访问八个独立的子软件工具。它们是:Eeschema,原理图库编辑器,PCBNew, PCB足迹编辑器,GerbView, Bitmap2Component, PCB计算器和PL编辑器。参考“工作流程图”(KiCad入门的第2章),了解如何使用这些主要工具。 2.建立新工程步骤:File(文件) → New Project → New Project,给新工程取个名字:tutorial1(名字是自己定义的),工程文件会自动带.pro后缀。kicad会弹出窗口问你把文件放到什么地方,你最好新建一个文件夹,然后点击确定,工程文件就保存了,以后的所有文件也默认保存到这个目录里面。 3.现在就可以开始画原理图了,点击图标Eeschema ,这个图标位于左边的第一个。 4.在顶部工具栏,点击“Page Settings”图标 ,设置Page Size为A4,设置Title为Tutorial1,你可以看到,这里还有很多信息,如有需要,你可以修改它们,点击OK完成,这些信息位于原理图的右下角。把鼠标放到原理图的右下角,然后滑动鼠标的滚轮,就可以放大和缩小原理图,点击File → Save Schematic Project保存工程。 5.现在就可以放置我们的第一个元器件了,在右边的工具栏中,点击“Place component”图标 ,或者用快捷键,直接单击键盘上的字母a。 提示:按住shift+?键,可以浏览所有的快捷键使用方法。 6.接着前面的步骤,在原理图中点击一下,就可以打开“Choose Component”窗口,我们将放一个电阻到原理图。在Fliter栏里面,输入字母R,大小写都可以,然后你就可以看见所有以R开头的元件都列出来了。这些库元件位于本地,都是一些通用的元器件。 7.接着上一步骤,选择电阻R,单击OK,或者在R上双击,就可以看到一个电阻附着在鼠标上,然后你可以单击鼠标,把电阻放到原理图上去。 8.用放大镜图标,可以放大元器件,也可以滑动鼠标滚轮来放大和缩小,把鼠标放到电阻上边,然后按下鼠标滚轮,可以随意拖动电阻在原理图中的位置。 9.把鼠标放到电阻上边,然后按键盘上的R键,可以旋转电阻。 注意:不用单击元器件旋转它。(我的理解是,你不必把电阻附着在鼠标上,也可以用R来旋转它。) 10.在元器件上单击右键选择Edit Component → Value,或者把鼠标悬停在元器件上以后按V键,可以修改电阻的阻值。如果你打击了E键,将会出现更多的可以修改的值。在单击右键出现的菜单中,你可以了解到更多的快捷键用法和代表的意义。 11.接着上面的步骤,将会出现一个窗口,你一把R改为1K,代表电阻的阻值,然后点击OK,完成。 注意:不要修改R?,这里的问号,在画好原理图后,我们可以给他们统一自动修改。上面步骤完成以后,电阻中间的R应该变成了1K,如下图所示: 12.我们再放一个电阻,在原理图空白处单击鼠标,然后元器件选择窗口就会再次弹出来。 13.和之前不同的是,在窗口中多了一个“history”(历史),可以在这里选择电阻。如下图所示: 14.如果想要删除元件,在该元件上单击右键,然后选择Delete Component,也可以把鼠标悬浮到要删除的元件上边后按键盘上的DEL键删除元件。 注意:通过菜单Preferences → Hotkeys → Edit hotkeys你可以编辑任何一个快捷键,修改后立即生效。 15.如果你想复制一个元件,你可以把鼠标悬停到元件上以后,按下快捷键C,然后把复制出来的元件放到任何你想要放置的地方。 16.把鼠标悬停到第二个电阻上边,然后单击鼠标右键,选择“Drag Component”(拖动元件),选好位置后再点击鼠标左键放下。你可以把鼠标悬停到电阻上边以后按下按键G来实现同样的功能。按键R用来旋转元器件。按X键和Y键可以在X方向或者Y方向颠倒元器件。 17.把鼠标悬停到第二个电阻上边,然后按键V,把电阻值修改为100。按下Ctrl+Z键可以撤销之前的动作。 18.改变网格(grid)大小。你可以注意到现在在原理图上的网格间距还很大。单击右键,选择 Grid select(网格选择)菜单,可以很容易的修改网格的间距大小。通常情况下,我们强烈建议使用50mils网格间距。 19.按照之前的添加元件步骤,添加元器件PIC12C508A-I/SN。和之前不同的使是,这个元器不在device库里边,而位于microchip_pic12mcu库里边。默认情况下,元器件选择窗口中没有这个库,所以我们必须要先添加库。菜单栏选择Preferences → Component Libraries,然后点击“Add”(添加)按钮。找到microchip_pic12mcu库以后添加,然后找到PIC12C508A-I/SN器件并放到原理图中。 20.把鼠标悬停到元件PIC12C508A-I/SN上边,然后按X键或者Y键,可以观察元件的变化。按第二下X键或者第二下Y键,将返回按X键或者Y键之前的状态。 21.同上边的步骤,添加元件LED,该元件位于device库。 22.把原理图中的所有元器件摆放规整一下,结果如下图所示: 23.现在我们创建一个元件,起名为MYCONN3。关于如何制作元器件库,你可以看《Make Schematic Components in KiCad》。 24.按A键,在元器件选择窗口中选择我们刚刚制作好的MYCONN3元件,放到原理图中。 25.元件MYCONN3的标识符J?,如果你想改变它的位置,你可以在J?上边单击右键,然后选择Move Field,或者使用快捷键M。最好是先放大以后再操作比较好。如下图所示,摆放MYCONN3的J?。 26.现在是时候放电源和地的标志了,单击右边工具栏中的“Place a power port button”图标 ,或者使用快捷键P。在Power库中,找到VCC,点击OK。 27.在1K电阻的上边,放一个VCC,然后在单片机的VDD上边放一个VCC,然后在MYCONN3的上边也放一个VCC。 28.同上,把GND放到原理图中,最后的效果如下图所示: 29.下面,我们将使用右边工具栏的“Place wire”图标 ,把所有的元器件连接起来。 注意:不要选错图标,尤其是?Place a bus,两个图标很相似。 30.接着上边的步骤,在单片机的PIN7上的小圆圈上点击鼠标,然后在LED的PIN2的小圆圈上点击鼠标,这样,两个引脚就连在一起了。在操作之前,你最好先放大原理图。 注意:在连接好线以后,如果你想改变元器件在原理图中的位置,可以把鼠标悬停到该元器件以后按快捷键G,然后就可以移动了。它与快捷键M的区别是,G是连着线移动的,M是只移动元器件,线不动。 31.如下图所示,用线连接所有的元器件。双击鼠标可以结束一条线。 32.我们接下来来用网络标号来连接电子元器件之间的引脚。在右侧的工具栏中,点击“Place net name”图标 ,或则使用快捷键L。 33.在单片机PIN6的线中间点击一下,然后给这个网络标号起名为INPUT。 34.然后以同样的方式,给100欧电阻的右侧引脚上放置一个相同名称的网络标号,同样起名为INPUT。因为两个网络标号的名字相同,所以这两个网络标号把单片机的PIN6引脚和100欧电阻连接起来了。对于一个复杂的设计,尤其是当用线连接看起来非常杂乱的场合,用网络标号就会使得原理图看起来很清爽。 35.网络标号还可以用于说明某根线的用途。给单片机的PIN7脚上放一个网络标号,给它取名为uCtoLED。电阻和LED的中间放一个网络标号,取名为LEDtoR。给MYCONN3和连接它的电阻那根线之间放一个网络标号,起名为INPUTtoR。 36.你不需要给VCC和GND用网络标号标注,因为它们自己已经很清楚了。 37.最后的结果如下图所示: 38.现在让我们来处理没有连线的引脚。在自检的时候,所有未连接的引脚或线都会产生提醒。为避免这种情况的发生,我们可以给这些引脚或者线上边放上标志。 39.在右侧的工具栏里边,点击Place no connect flag图标 ,分别在单片机的PIN2,3,4,5上边点击一下,就会看到引脚上边有了X图标。如下图所示: 40.有些元器件的电源引脚是不可见的。你可以通过点击左侧工具栏中的Show hidden pins图标 让电源引脚显示出来。如果隐藏的电源引脚名称是VCC或者GND的话,这些引脚就会自动的连接,用不着我们操心。 41.现在,我们必须要放置Power Flag在KiCAD的原理图,来提示电源来自某个地方。点击快捷键A,在POWER库中找到PWR_FLAG,在原理图中放两个这标志。然后分别把它们和VCC GND连接起来,如下图所示: 注意:这个操作将避免一个经典的KICAD提醒:Warning Pin power_in not driven (Net xx) 42.有时候,我们需要给原理图的某些地方添加注释。给原理图添加注释,使用右侧工具栏的Place graphic text (comment)图标 。 43.现在,所有的元器件都有了自己的独有标识。实际上,我们的元器件的名称都还是R?或者J?,标记这些标识符,使用Annotate schematic图标 。 44.在弹出的原理图注释窗口中,选择Use the entire schematic?,然后点击?Annotation按钮,在弹出的确认信息窗口中点击OK。注意一下,所有的?都变成了数字。所有的标识符都是唯一的。在我们的这个例子中,它们的标识符分别是:R1, R2, IC1, D1 和 J1。 45.现在我们将检测一下原理图中有没有错误。点击Perform Electric Rules Check图标 ,点击Run按钮,如果有错误或者提醒的话,就会在窗口中输出,例如:某根线没有连接。正常情况,必须是0个错误,0个提醒。如果有错误或者提醒的话,在原理图中就会有小的绿色箭头指向出错的地方。选择Create ERC file report,然后再点击Run按钮,就可以得到更为详细的错误和提醒的信息。 46.原理图已经画完了。现在我们给每个元器件添加封装,然后再生成一个网络表。在顶部的工具栏中,点击Netlist generation图标 ,点击Generate按钮,然后再点击Save(保存)按钮,以默认的名称保存就可以。 47.网络表生成后,点击顶部工具栏的Run Cvpcb图标 ,如果这时候弹出来一个提示窗口提示你丢失了某个文件,不要管它,点击OK关掉。(注意:点击完Run Cvpcb图标之后,可能会等待较长时间,电脑才会反应过来,因为要加载很多封装,这时候,等着,不要瞎点,否则容易点死软件) 48.Cvpcb允许你通过使用KICAD中的封装链接到原理图中的元器件。中间区域显示原理图中用到的所有元器件。我们选择D1,在右侧区域我们找到?LEDs:LED-5MM,然后双击它,这样,就把这个封装给了D1了。 49.在右侧的区域,只会显示对应元器件可能需要的封装,Kicad试图给你推荐比较合适的封装。点击图标 ,和可以禁止或者允许这些过滤器的功能。 50.IC1对应的封装选择Housings_DIP:DIP-8_W7.62mm,J1对应的封装选择Connect:Banana_Jack_3Pin,R1和R2对应的封装选择?Discret:R1。 51.如果你想知道你选择的封装长什么样子,有两种办法可以看到。你可以单击View selected footprint图标 预览当前的封装。另外,你可以点击Display footprint list documentation图标,然后,你可以看到包含了所有可用封装的PDF文件。你可以把这个文件打印出来,确保用到的封装尺寸正确。 52.现在,你可以更新网络表了,这次的网络表,元器件和封装就关联起来了。点击File → Save Edits,或者点击图标 保存更新网络表。如果你在已有的封装库中找不到需要的封装,你将需要自己做封装,这个在以后的文章中会写到。 53.你现在就可以管了Cvpcb,然后回到原理图编辑器。在菜单栏选择File → Save Schematic Project保存文件,然后关闭原理图编辑器。 54.切换到Kicad工程文件管理器。 55.网络表文件描述了所有的元器件和它们引脚直接的连接关系。网络表文件实际上就是一个文本文件,你可以检查和编辑它,还可以把它当做脚本。 注意:库文件(.lib)实际上也是文本文件,也可以编辑或者脚本化。 56. 创建物料清单(BOM)。打开schematic编辑器并单击顶部的Bill of Materials图标 BOM图标,首次使用,默认无插件。要添加插件,单击Add Plugin按钮并选择*。要选择的XSL文件。在本例中,我们选择bom2cv .xsl。 注:*。xsl文件位于…\ Kicad \ Bin \ Scripting \ Plugins文件夹。 KiCad会自动生成如下命令: Xsltproc-o "% o" "C: Program Files\ Kicad \ Bin \ Scripting \ Plugins \ Bom2CSV。XSL %我” 让我们为这个命令添加一个后缀: Xsltproc-o "% o.csv" "C: Program Files\ Kicad \ Bin \ Scripting \ Plugins \ Bom2CSv。xsl %我” 单击“帮助”按钮获取更多信息。 57. 现在单击Generate生成BOM表,单击Close关闭。BOM表的文件名在project文件夹中,与您的项目名相同。您可以使用Excel等办公软件打开。

    单片机 电路原理图 kiCad

发布文章