操作系统开发:启用内存分页机制

news2024/12/23 2:58:10

目前我们已进入保护模式,但依然会受到限制,虽然地址空间达到了4GB,但此空间是包括操作系统共享的4GB空间,我们把段基址+段内偏移地址称为线性地址,线性地址是唯一的,只属于某一个进程。在我们机器上即使只有512MB的内存,每个进程自己的内存空间也是4GB,这是指的虚拟内存空间。一直以来我们都是在内存分段机制下工作的,该模式下如果系统里面的应用程序过多,或者内存碎片过多无法容纳新的进程,则可能会出现进程需要等待,或无法直接运行的局面,而内存分页机制,理论上只要4KB内存就可以让程序运行下去。

注释: 该系列笔记是在学习《操作系统真相还原》时通过阅读后简化并适当描述整理的学习笔记,首先,致敬作者郑刚博士,在读本书时能深刻的感觉到作者写书时一丝不苟的态度,书很厚写的,讲解细致幽默,很能让人愿意继续读下去,同时也不得不佩服作者计算机底层功力的深厚,转载本文请一并附带郑刚版权信息。

目前我们已进入保护模式,但依然会受到限制,虽然地址空间达到了4GB,但此空间是包括操作系统共享的4GB空间,我们把段基址+段内偏移地址称为线性地址,线性地址是唯一的,只属于某一个进程。

在我们机器上即使只有512MB的内存,每个进程自己的内存空间也是4GB,这是指的虚拟内存空间。

为什么要分页,分段它不香吗?

一直以来我们都是在内存分段机制下工作的,该模式下如果系统里面的应用程序过多,或者内存碎片过多无法容纳新的进程,则可能会出现进程需要等待,或无法直接运行的局面,而内存分页机制,理论上只要4KB内存就可以让程序运行下去。

  • 在分段模式下

  • CPU认为线性地址等于物理地址,而线性地址是由编译器编译出来的,它本身是连续的,所以要想运行程序,物理地址也必须要连续才行,但在多数情况下物理地址并不是连续的。

  • 在分页模式下

  • 线性地址可以连续而物理地址是分散的,我们通过某种映射关系来解除线性地址与物理地址的一一对应,然后通过映射机制将他们重新关联起来,把线性地址关联到任意的物理地址上面,理论上只要4KB内存就可以运行程序了。

对于分页机制来说,CPU在硬件层面提供了支持,在CPU实现中,这种映射关系是通过页表来实现的,由于地址转换的实时性较高,所以查找页表的功能也是被CPU硬件支持的。

什么是一级页表,它的作用是啥?

在保护模式中段寄存器中的内容是段选择子,选择子的最终目的就是为了找到段基址,其内存访问的核心机制依然是,段基址:段内偏移地址,这两个地址相加后才是绝对地址(线性地址),此地址在分段机制下被CPU认为是物理地址可以直接被送上总线,该计算过程也是由段部件自动完成。

分页机制依然要建立在分段机制的基础之上,段部件依然需要工作,而分页只能在分段机制之后进行。

CPU在不打开分页机制的情况下,是按照分段方式工作的,将段地址和段内偏移地址经过段部件处理后所输出的线性地址,CPU就认为是物理地址,
而如果打开了分页,段部件输出的线性地址会变成虚拟地址,虚拟地址不等同于物理地址。

而CPU必须要拿到物理地址才行,此虚拟地址对应的物理地址需要在页表中查找,该工作由页部件自动完成。

  • 分页机制的思想:
  • 通过映射,可以使连续的线性地址与任意物理内存地址相关联,逻辑上连续的线性地址其对应的物理地址可以不连续。
  • 分页作用,将线性地址转换成物理地址,用大小相等的页(页部件)代替大小不相等的段(段部件)。

通常情况下,经过段部件输出的线性地址也可以叫做虚拟地址,所谓的转换是指,从线性地址空间(段)到虚拟地址空间(页)再到物理地址空间(实际),如下图:

前面说过,分页机制建立在分段机制之上,即使在分页机制下的进程也要先经过逻辑上的分段,代码段和数据段在逻辑上被拆分成为以页为单位的小内存块,此时的虚拟地址不能存放任何数据。

接着操作系统开始为这些虚拟内存页分配真实的物理内存页,它查找物理内存中的可用页,然后在页表中登记这些物理页地址,此时就完成了虚拟页到物理页的映射,每个进程都以为自己独享4GB地址空间。

用于存储这种映射关系的表,就是页表(PT),页表中每一行(1个单元格)称为页表项(PTE),其大小是4字节,页表项的作用是存储内存物理地址,当访问一个线性地址时,实际上就是在访问页表项中所记录的物理内存地址。

一个页可被分为高低位吗?

CPU采用一个页的大小是4KB,32位地址表示4GB空间,可将32为地址分成高低两部分,低地址是内存块大小,高地址是内存块数量,故内存块数*内存块大小=4GB,页是地址空间的计量单位,只要是4KB的地址空间都可以称为一页,所以线性地址空间也要对应物理地址空间的一页。

一页大小是4KB(页大小是4KB所以页表项中的物理地址都是4K的整数倍),这样一来,4GB地址空间被划分成4GB/4KB=1MB个页,也就是4GB空间中可容纳1048576个页,页表中自然也要有1048576个页表项,这就是一级页表。

任意一个地址最终都会落到某个实际的物理页上,32位地址空间共有1MB个物理页.

首先定位到某个具体物理页,然后给出物理页内的偏移量就可以访问到任意1字节的内存,虚拟地址的高20位用来定位具体的物理页,低12位则用来在该物理页内寻址。

一级分页中的地址转换过程是怎样的?

页表是位于内存中的,在打开分页前需要将页表物理地址加载到CR3寄存器内,只要提供页表物理地址,便能够访问到页表中任意一个页表项地址。

一个页表项对应一个页,所以,用线性地址的高20位作为页表项的索引,每个页表项要占用 4 字节大小,所以这高20位的索引乘以4后才是该页表项相对于页表物理地址的字节偏移量。用 cr3 寄存器中的页表物理地址加上此偏移量便是该页表项的物理地址,从该页表项中得到映射的物理页地址,然后用线性地址的低 12 位与该物理页地址相加,所得的地址之和便是最终要访问的物理地址。CPU 中集成了专门用来干这项工作的硬件模块,我们把该模块称为页部件。

  • 总结一下页部件的工作
  • 用线性地址的高 20 位在页表中索引页表项,用线性地址的低 12 位与页表项中的物理地址相加,所求的和便是最终线性地址对应的物理地址。

什么是二级页表,它的作用是啥?

一级页表与二级页表原理一致,但在现代操作系统中一般都采用二级页表结构,两种页表结构区别如下:

  • 1.一级页表中最多可容纳 1M(1048576)个页表项,每个页表项是 4 字节,如果页表项全满的话,便是 4MB 大小。
  • 2.一级页表中所有页表项必须要提前建好,原因是操作系统要占用 4GB 虚拟地址空间的高 1GB,用户进程要占用低 3GB。
  • 3.每个进程都有自己的页表,进程一多,光是页表占用的空间就很可观了。
  • 4.不要一次性地将全部页表项建好,需要时动态创建页表项。

无论是几级页表,标准页的尺寸都是 4KB,所以 4GB 线性地址空间最多有 1M 个标准页。一级页表是将这 1M 个标准页放置到一张页表中,二级页表是将这 1M 个标准页平均放置 1K 个页表中,每个页表中包含有 1K 个页表项。页表项是 4 字节大小,页表包含 1K 个页表项,故页表大小为4KB,这恰恰是一个标准页的大小。

页目录表来存储这些页表,每个页表的物理地址在页目录表中都以页目录项(Page Directory Entry, PDE)的形式存储,页目录项大小同页表项一样,都用来描述一个物理页的物理地址,其大小都是 4 字节,而且最多有 1024 个页表,所以页目录表也是 4KB 大小,同样也是标准页的大小。

页目录表中共 1024 个页目录项,一个页目录项中记录一个页表物理页地址,页大小都是 0x1000,即 4096,每个页表中有 1024 个页表项,每个页表项中是一个物理页地址,最终数据写在这页表项中指定的物理页中。

二级页表是如何工作的?

二级页表地址转换原理是将 32 位虚拟地址拆分成高 10 位、中间 10 位、低 12 位三部分,

  • 高 10 位作为页表的索引,用于在页目录表(PT)中定位一个页目录项(PDE),页目录项中有页表物理地址,也就是定位到了某个页表。
  • 中间 10 位作为物理页的索引,用于在页表内定位到某个页表项(PTE),页表项中有分配的物理页地址,也就是定位到了某个物理页。
  • 低 12 位作为页内偏移量用于在已经定位到的物理页内寻址。

访问任何页表内的数据都要通过物理地址,由于页目录项 PDE 和页表项 PTE 都是 4 字节大小,给出了 PDE 和 PTE 索引后,还需要乘以 4,再加上页表物理地址,这才是最终要访问的绝对物理地址。

  • 具体寻址流程如下所示:
  • 1.用虚拟地址的高 10 位乘以 4,作为页目录表内的偏移地址,加上页目录表的物理地址(CR3内的基地址),所得的和,便是页目录项的物理地址。读取该页目录项,从中获取到页表的物理地址。
  • 2.用虚拟地址的中间 10 位乘以 4,作为页表内的偏移地址,加上在第 1 步中得到的页表物理地址,所得的和,便是页表项的物理地址。读取该页表项,从中获取到分配的物理页地址。
  • 3.虚拟地址的高 10 位和中间 10 位分别是 PDE 和 PTE 的索引值,所以它们需要乘以 4,而低 12 位作为页内偏移直接与第2步物理页地址相加,即可得到实际内存物理地址。

在建立页表时,会在页目录项(PDE)及页表项(PTE)中写入合适的值,每个任务都有自己的页表,每个任务都活在自己的虚拟地址空间中,另外,任务在切换时,页表也需要跟着切换。

页目录项(PDE)及页表项(PTE)具体结构解析?

页目录项(PDE)及页表项(PTE)结构对比图:

如上图,页目录项跟页表项其大小都是4字节,只有后20位(12-31)是物理地址,其余的12位(0-11)用于增加其他一些属性,如下是属性具体功能:

  • P位(Present)-存在位:

  • 为 1 表示该页存在于物理内存中,若为 0 表示该表不在物理内存中。

  • RW位(Read/Write)-读写位:

  • 若为 1 表示可读可写,若为 0 表示可读不可写。

  • US位(User/Supervisor)- 普通用户/超级用户位:

  • 为 1 表示处于User级,任意级别特权的程序都可以访问该页。若为0处于Supervisor级,特权级别为3的程序不允许访问该页,该页只允许特权级别为0、1、2的程序可以访问。

  • PWT位(Page-level Write-Through)页级通写位、页级写透位:

  • 为1表示此项采用通写方式,表示该页不仅是普通内存,还是高速缓存。

  • PCD位(Page-level Cache Disable)页级高速缓存禁止位:

  • 为 1 表示该页启用高速缓存,为 0 表示禁止将该页缓存。

  • A位(Accessed)访问位:

  • 为 1 表示该页被 CPU 访问过,所以该位是由 CPU 设置的。

  • D位(Dirty)脏页位:

  • CPU 对一个页面执行写操作时,就会设置对应页表项的 D 位为 1 此项仅针对页表项有效,并不会修改页目录项中的 D 位

  • PAT位(Page Attribute Table)页属性表位:

  • 能够在页面一级的粒度上设置内存属性

  • G位(Global)全局位:

  • 为了提高获取物理地址的速度,将虚拟地址与物理地址转换结果存储在 TLB,TLB 是用来缓存地址转换结果的高速缓存,此 G 位用来指定该页是否为全局页,为 1 表示是全局页,为 0 表示不是全局页。

  • AVL位(Available)操作系统可用位

  • 表示软件可用,CPU不理会该位的值。

启用内存分页时需要做2件事

第一步:准备好页目录表及页表,将页表地址写入控制寄存器CR3

页表需要存储在内存的物理地址内,需要时通常会被载入到CR3寄存器内,CR3寄存器又称为页目录基址寄存器(PDBR),该寄存器的低12位(0-11)全是0,低12位除第3位PWT和第4位PCD位以外(用于设置高速缓存相关的特性,在此将其置为0),其余位都无用,高20位(12-31)用于填充页目录表物理地址,故只需要把页目录表物理地址的高20位写入CR3寄存器即可。

第二步:寄存器CR0的PG位置1

启动分页机制的开关是将控制寄存器 cr0 的 PG 位置 1,PG 位是 cr0 寄存器的最后一位(31位),将PG位置1后便进入了内存分页运行机制,段部件输出的线性地址将变为虚拟地址,在将PG位置1之前,系统都是在内存分段机制下工作,段部件输出的线性地址便直接是物理地址。

CR0 第 0 位是 PE位,用来进入保护模式的开关(拓展)

设计一个页表

设计页表前的基础知识

分页第一步是准备好一个页表,设计页表其实就是设计内存布局。

为了计算机安全,用户进程必须运行在低特权级,当用户进程需要访问硬件相关的资源时,需要向操作系统申请,由操作系统去做,之后将结果返回给用户进程,通常申请资源需要调用系统提供给我们的API接口,进程可以有无限多个,而操作系统只有一个,所以必须将操作系统中的API资源共享给所有用户进程使用。

如何实现操作系统接口共享?

我们可以把4GB虚拟地址空间分成两部分,将4GB内存的高3GB-4GB以上的地址空间划分给操作系统,用户进程则在0GB-3GB低位地址空间内。

为了实现共享操作系统,让所有用户进程3GB-4GB的虚拟地址空间都指向同一个操作系统,也就是所有进程的虚拟地址3GB-4GB本质上都是指向的同一片物理页地址,这片物理页上是操作系统的实体代码。

所以只要保证,所有用户进程,虚拟地址空间3GB-4GB对应到操作系统中,页表项中,所记录的物理页地址是相同的即可实现共享。

打开分页机制,我们的程序将会在虚拟地址空间中运行,分页机制得有页目录表,页目录表中的是页目录项,其中记录的是页表的物理地址及相关属性,所以还得有页表,页目录表的位置,我们就放在物理地址0x100000处,让页表紧挨着页目录表,页目录本身占4KB,所以第一个页表的物理地址是0x101000,其内存布局如上图。

代码中我们将充分利用低端1MB内存,我们将 mbr、loader、操作系统内核都放置在这 1MB 空间内,当然这 1MB 是指物理内存的 0~0xffff。

内核在物理内存 1MB 之内,将内核地址映射到虚拟地址 3GB 之上,就是将虚拟地址 0xc0000000之上的 1MB 地址映射到物理内存 1MB 之内。

内存分页测试(代码测试)

编译汇编代码,并让Bochs虚拟机运行,设置vb断点,让程序可以在jmp跳转处停下。

C运行到入口点,单步N运行程序

程序运行到mov cx, 0x0004时,还没有加载GDT

运行到lgdt ds:0x0b03时,第一次加载GDT

运行到0008:0000000000000b8b (unk. ctxt): mov eax, cr0第二次加载GDT,此时还没有开启分页呢。

运行到0008:0000000000000b96 (unk. ctxt): lgdt ds:0x00000b03分页机制正式开启。

在分页后,GDT 的基址会变成 3GB 之上的虚拟地址,显存段基址也变成了 3GB 这上的虚拟地址.

开启分页之后,可以在物理内存0x100000处后看到页目录表,还可以在虚拟机中利用info tab命令看到页表中虚拟地址到物理地址的映射关系。

快表 TLB

分页机制的目的是实现虚拟地址到物理地址的转换,最终是想得到虚拟地址所对应的物理地址,但如果每次都经过中间的查表过程那么效率上会大打折扣。

如果给出一个虚拟地址后能直接得到相应的页框物理地址,免去中间的查表过程,用虚拟地址的低12位在该物理页框中寻址,即可极大的提高地址转换速度。

实现方法是将近来常用的地址和指令加载到TLB快表中,快表是一个专门用来存放虚拟地址页框与物理地址页框的映射关系表。

有了 TLB,处理器在寻址之前会用虚拟地址的高20位作为索引来查找 TLB 中的相关条目,如果命中则返回虚拟地址所映射的物理页框地址,否则会查询内存中的页表,获得页框物理地址后再更新 TLB。

但TLB是高速缓存容量较小,因此 TLB 中的数据只是当前任务的部分页表,而且只有 P 位为 1 的页表项才有资格在 TLB 中,如果 TLB 被装满了,需要将很少使用的条目换出,所以 TLB 必须实时更新。

TLB 并不自动更新,处理器也不负责 TLB 的有效性,它把 TLB 的维护工作交给操作系统开发人员,由开发人员手动控制

尽管 TLB 对开发人员不可见,但依然有两种方法可以间接更新 TLB 一个是针对 TLB 中所有条目的方法—重新加载 CR3,比如将 CR3 寄存器的数据读出来后再写入 CR3,这会使整个 TLB 失效

另一个方法是针对 TLB 中某个条目的更新。处理器提供了指令 invlpg(invalidate page),它用于在 TLB 中刷新某个虚拟地址对应的条目,处理器是用虚拟地址来检索 TLB 的,因此很自然地,指令 invlpg 的操作数也是虚拟地址

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

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

相关文章

开发微服务电商项目演示(四)

一&#xff0c;网关服务限流熔断降级第1步&#xff1a;启动sentinel-dashboard控制台和Nacos注册中心服务第2步&#xff1a;在网关服务中引入sentinel依赖<!-- sentinel --> <dependency><groupId>com.alibaba.cloud</groupId><artifactId>sprin…

快速排序基本原理

快速排序基本原理1.快速排序1.1 基本原理1.2 快速排序执行步骤1.2.1 分区包含步骤1.2.1 分区步骤1.3 快速排序大O记法表示2. 将[0,5,2,1,6,3]进行快速排序 【实战】2.1 第一次分区步骤2.2 第二次分区步骤2.3 第三次分区步骤2.4 第四次分区步骤3.快速排序代码实现1.快速排序 1.…

一文盘点,ZBC的应用场景与通缩场景

进入到2023年&#xff0c;Zebec生态正在向新的叙事方向发展。一方面我们看到&#xff0c;流支付生态Zebec在此前正式走向DAO&#xff0c;并上线了DAO治理投票平台Zebec Node&#xff0c;任何持有ZBC的用户都可以参与投票。另一方面我们看到生态正在朝着最初的愿景迸发&#xff…

3.28 haas506 2.0开发教程-example-蓝牙多设备扫描(仅支持M320,HD1)

haas506 2.0开发教程-example-蓝牙多设备扫描案例说明蓝牙信息克隆1.手机蓝牙改名信息克隆代码测试案例说明 开发板扫描蓝牙设备&#xff0c;获取并打印蓝牙设备mac地址。mac地址每个设备不同&#xff0c;且不能更改。本案例仅适用于M320开发板和HD1-RTU。案例使用手机与iBeac…

LeetCode(剑指offer) Day1

1.用两个栈实现一个队列。队列的声明如下&#xff0c;请实现它的两个函数 appendTail 和 deleteHead &#xff0c;分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素&#xff0c;deleteHead 操作返回 -1 ) 解题过程记录&#xff1a;本题就是用两个栈&…

如何激励你的内容团队产出更好的创意

对于一个品牌而言&#xff0c;如何创造吸引受众并对受众有价值内容是十分关键的。随着市场数字化的推进&#xff0c;优质的创意和内容输出对一个品牌在市场中有着深远的影响。对于很多内容策划和创作者来说&#xff0c;不断地产出高质量有创意的内容是一件非常有挑战性的事情。…

【零基础入门前端系列】—超链接和文本格式化标签(四)

【零基础入门前端系列】—超链接和文本格式化标签&#xff08;四&#xff09; 一、超链接 HTML使用标签 <a>来设置超文本链接。超链接可以是一个字&#xff0c;一个词&#xff0c;或者一组词&#xff0c;也可以是一幅图像&#xff0c;您可以点击这些内容来跳转到新的文…

别再问我供应商质量工程师(SQE)是干什么的了,这是最好的解释。

供应商质量工程师&#xff08;SQE&#xff09;是指一种负责监督供应商质量的职业。SQE的工作主要包括以下几项内容&#xff1a; 核查供应商质量&#xff1a;通过对供应商的产品、服务和生产流程的评估&#xff0c;来确保供应商的质量符合公司的标准和要求。开展质量审核&#…

【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决串口唤醒和回调无法一起使用的问题)

【STM32笔记】HAL库低功耗停止模式的串口唤醒&#xff08;解决串口唤醒时钟问题&#xff09; 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置&#xff08;ADC唤醒无法使用、低功耗模式无法烧录解决方案&#xf…

家政服务小程序实战教程10-分类展示

小程序一般底部菜单栏会有一个分类的功能&#xff0c;点击分类&#xff0c;以侧边栏导航的形式列出所有类目&#xff0c;点击某个类目可以做数据筛选&#xff0c;我们本篇就实现一下该功能 01 优化数据源 在我们家政服务小程序里&#xff0c;我们已经建立了类型和服务的数据源…

企业财务管理升级,智慧税务和数据可视化打造新标准

一、引言在发展社会主义市场经济的过程中&#xff0c;税收承担着组织财政收入、调控经济、调节社会分配的职能。中国每年财政收入的90%以上来自税收&#xff0c;其地位和作用越来越重要&#xff0c;可称之为国家经济的“晴雨表”&#xff0c;有效进行税务管理、充分挖掘税务大数…

面试碰壁15次,作为一个已经28岁的测试工程师,路究竟该怎么走....

3年测试经验原来什么都不是&#xff0c;只是给你的简历上画了一笔&#xff0c;一直觉得经验多&#xff0c;无论在哪都能找到满意的工作&#xff0c;但是现实却是给我打了一个大巴掌&#xff01;事后也不会给糖的那种... 先说一下自己的个人情况&#xff0c;普通二本计算机专业…

深度学习知识补充

候选位置(proposal) RCNN 什么时ROI&#xff1f; 在图像处理领域&#xff0c;感兴趣区域(region of interest &#xff0c; ROI) 是从图像中选择的一个图像区域&#xff0c;这个区域是你的图像分析所关注的重点。圈定该区域以便进行进一步处理。使用ROI圈定你想读的目标&…

dfs(十)矩阵最长递增路径 (注意dfs前后状态的更新)

描述 给定一个 n 行 m 列矩阵 matrix &#xff0c;矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径&#xff0c;使这条路径上的元素是递增的。并输出这条最长路径的长度。 这个路径必须满足以下条件&#xff1a; 1. 对于每个单元格&#xff0c;你可以往上&#…

全解析 ESM 模块语法,出去还是进来都由你说了算

模块语法是ES6的一个重要特性&#xff0c;它的出现让JavaScript的模块化编程成为了可能。 在JavaScript中可以直接使用import和export关键字来导入和导出模块&#xff0c;但是这种语法并不是ES6的标准&#xff0c;而是ESM&#xff08;ECMAScript Module&#xff09;模块语法的…

【CICD】Jenkins 构建部署前端项目

出于对 CICD 的研究与学习&#xff0c;在初步学习了解并安装 jenkins 后&#xff0c;记录一下对于使用 jenkins 部署前端项目的过程。 1.目标 希望能够实现的是&#xff1a;在本地使用 git 工具将项目代码推送到远程仓库&#xff08;本篇使用 gitee 演示&#xff09;&#xf…

[C][KEIL5][IAR] 全局取消结构体对齐

文章目录一、 正常编译&#xff1a;二、 -fpack-struct 全局取消结构体对齐三、 结论&#xff1a;结构体字节不进行对齐的用途&#xff08;1&#xff09;减小内存占用的空间&#xff08;2&#xff09;直接将结构体作为通信协议&#xff08;在低带宽下通讯&#xff09;&#xff…

【Java】代码块的细节你搞懂了吗(基础知识七)

希望像唠嗑一样&#xff0c;one step one futher。 目录 &#xff08;1&#xff09;代码块的应用场景 &#xff08;2&#xff09;代码块的细节 1.static 代码块只加载一次 2.当调用类的静态成员时&#xff0c;类会加载 3. 使用类的静态成员时&#xff0c;static代码块会被执…

大数据第一轮复习笔记

linux: 添加用户 useradd 删除用户 userdel useradd -d指定组 添加组 groupadd 删除组 groupdel 创建目录 mkdir -p 删除目录 rm -rf 创建目录 touch cat -n 查看文件(显示行号)

Axure 9 收录不同效果的制作过程

效果类别 一、默认选中实现单选效果 1、默认选中 点击组件&#xff0c;右键选择selected字样&#xff1b; 2、实现单选效果 点击所有组件&#xff0c;右键选择selected group&#xff0c;填好命名&#xff0c;并设置选中时的组件样式&#xff1b;选择其中一个组件&#xf…