能力说明:
精通JVM运行机制,包括类生命、内存模型、垃圾回收及JVM常见参数;能够熟练使用Runnable接口创建线程和使用ExecutorService并发执行任务、识别潜在的死锁线程问题;能够使用Synchronized关键字和atomic包控制线程的执行顺序,使用并行Fork/Join框架;能过开发使用原始版本函数式接口的代码。
能力说明:
熟练掌握Linux常用命令、文件及用户管理、文本处理、Vim工具使用等,熟练掌握企业IP规划、子网划分、Linux的路由、网卡、以及其他企业级网络配置技术,可进行Web服务器(Nginx),以及数据库(My SQL)的搭建、配置、应用,可根据需求编写Shell脚本,通过常用工具进行linux服务器自动化运维。
能力说明:
掌握Java开发环境下所需的MySQL高级技巧,包括索引策略、innodb和myisam存储引擎,熟悉MySQL锁机制,能熟练配置MySQL主从复制,熟练掌握日常SQL诊断和性能分析工具和策略。可对云数据库进行备份恢复与监控、安全策略的设置,并可对云数据库进行性能优化。掌握主要NOSQL数据库的应用技术。
附录“关于 A20 Gate”附录A“关于 A20 Gate”【参考“关于 A20 Gate” http://hengch.blog.163.com/blog/static/107800672009013104623747/ 】【参考“百度文库 激活 A20 地址线详解” http://wenku.baidu.com/view/d6efe68fcc22bcd126ff0c00.html】Intel 早期的 8086 CPU 提供了 20 根地址线,可寻址空间范围即 0~2^20(00000H~FFFFFH)的 1MB 内存空间。但 8086 的数据处理位宽位 16 位,无法直接寻址 1MB 内存空间,所以 8086 提供了段地址加偏移地址的地址转换机制。PC 机的寻址结构是 segment:offset,segment 和 offset 都是 16 位的寄存器,最大值是 0ffffh,换算成物理地址的计算方法是把 segment 左移 4 位,再加上 offset,所以 segment:offset 所能表达的寻址空间最大应为 0ffff0h + 0ffffh = 10ffefh(前面的 0ffffh 是 segment=0ffffh 并向左移动 4 位的结果,后面的 0ffffh 是可能的最大 offset),这个计算出的 10ffefh 是多大呢?大约是 1088KB,就是说,segment:offset 的地址表示能力,超过了 20 位地址线的物理寻址能力。所以当寻址到超过 1MB 的内存时,会发生“回卷”(不会发生异常)。但下一代的基于 Intel 80286 CPU 的 PC AT 计算机系统提供了 24 根地址线,这样 CPU 的寻址范围变为 2^24=16M,同时也提供了保护模式,可以访问到 1MB 以上的内存了,此时如果遇到“寻址超过 1MB”的情况,系统不会再“回卷”了,这就造成了向下不兼容。为了保持完全的向下兼容性,IBM 决定在 PC AT 计算机系统上加个硬件逻辑,来模仿以上的回绕特征,于是出现了 A20 Gate。他们的方法就是把 A20 地址线控制和键盘控制器的一个输出进行 AND 操作,这样来控制 A20 地址线的打开(使能)和关闭(屏蔽\禁止)。一开始时 A20 地址线控制是被屏蔽的(总为 0),直到系统软件通过一定的 IO 操作去打开它(参看 bootasm.S)。很显然,在实模式下要访问高端内存区,这个开关必须打开,在保护模式下,由于使用 32 位地址线,如果 A20 恒等于 0,那么系统只能访问奇数兆的内存,即只能访问 0--1M、2-3M、4-5M......,这样无法有效访问所有可用内存。所以在保护模式下,这个开关也必须打开。在保护模式下,为了使能所有地址位的寻址能力,需要打开 A20 地址线控制,即需要通过向键盘控制器 8042 发送一个命令来完成。键盘控制器 8042 将会将它的的某个输出引脚的输出置高电平,作为 A20 地址线控制的输入。一旦设置成功之后,内存将不会再被绕回(memory wrapping),这样我们就可以寻址整个 286 的 16M 内存,或者是寻址 80386 级别机器的所有 4G 内存了。键盘控制器 8042 的逻辑结构图如下所示。从软件的角度来看,如何控制 8042 呢?早期的 PC 机,控制键盘有一个单独的单片机 8042,现如今这个芯片已经给集成到了其它大片子中,但其功能和使用方法还是一样,当 PC 机刚刚出现 A20 Gate 的时候,估计为节省硬件设计成本,工程师使用这个 8042 键盘控制器来控制 A20 Gate,但 A20 Gate 与键盘管理没有一点关系。下面先从软件的角度简单介绍一下 8042 这个芯片。 图 13 键盘控制器 8042 的逻辑结构图8042 键盘控制器的 IO 端口是 0x60 ~ 0x6f,实际上 IBM PC/AT 使用的只有 0x60 和 0x64 两个端口(0x61、0x62 和 0x63 用于与 XT 兼容目的)。8042 通过这些端口给键盘控制器或键盘发送命令或读取状态。输出端口 P2 用于特定目的。位 0(P20 引脚)用于实现 CPU 复位操作,位 1(P21 引脚)用户控制 A20 信号线的开启与否。系统向输入缓冲(端口 0x64)写入一个字节,即发送一个键盘控制器命令。可以带一个参数。参数是通过 0x60 端口发送的。 命令的返回值也从端口 0x60 去读。8042 有 4 个寄存器:1 个 8-bit 长的 Input buffer;Write-Only;1 个 8-bit 长的 Output buffer; Read-Only;1 个 8-bit 长的 Status Register;Read-Only;1 个 8-bit 长的 Control Register;Read/Write。有两个端口地址:60h 和 64h,有关对它们的读写操作描述如下:读 60h 端口,读 output buffer写 60h 端口,写 input buffer读 64h 端口,读 Status Register操作 Control Register,首先要向 64h 端口写一个命令(20h 为读命令,60h 为写命令),然后根据命令从 60h 端口读出 Control Register 的数据或者向 60h 端口写入 Control Register 的数据(64h 端口还可以接受许多其它的命令)。Status Register 的定义(要用 bit 0 和 bit 1):bitmeaning0output register (60h) 中有数据1input register (60h/64h) 有数据2系统标志(上电复位后被置为0)3data in input register is command (1) or data (0)41=keyboard enabled, 0=keyboard disabled (via switch)51=transmit timeout (data transmit not complete)61=receive timeout (data transmit not complete)71=even parity rec'd, 0=odd parity rec'd (should be odd)除了这些资源外,8042 还有 3 个内部端口:Input Port、Outport Port 和 Test Port,这三个端口的操作都是通过向 64h 发送命令,然后在 60h 进行读写的方式完成,其中本文要操作的 A20 Gate 被定义在 Output Port 的 bit 1 上,所以有必要对 Outport Port 的操作及端口定义做一个说明。读 Output Port:向 64h 发送 0d0h 命令,然后从 60h 读取 Output Port 的内容写 Output Port:向 64h 发送 0d1h 命令,然后向 60h 写入 Output Port 的数据禁止键盘操作命令:向 64h 发送 0adh打开键盘操作命令:向 64h 发送 0aeh有了这些命令和知识,就可以实现操作 A20 Gate 来从实模式切换到保护模式了。 理论上讲,我们只要操作 8042 芯片的输出端口(64h)的 bit 1,就可以控制 A20 Gate,但实际上,当你准备向 8042 的输入缓冲区里写数据时,可能里面还有其它数据没有处理,所以,我们要首先禁止键盘操作,同时等待数据缓冲区中没有数据以后,才能真正地去操作 8042 打开或者关闭 A20 Gate。打开 A20 Gate 的具体步骤大致如下(参考 bootasm.S):等待 8042 Input buffer 为空;发送 Write 8042 Output Port (P2)命令到 8042 Input buffer;等待 8042 Input buffer 为空;将 8042 Output Port(P2)得到字节的第 2 位置 1,然后写入 8042 Input buffer;
实验内容和步骤首先要阅读MIPSsim模拟器的使用方法《MIPSsim使用手册》。然后了解MIPSsim的指令系统。1. 启动MIPSsim(用鼠标双击MIPSsim.exe)。2. 点击“配置”→“流水方式”,使模拟器工作在非流水方式下。3. 参照1.4节的使用说明,熟悉MIPSsim模拟器的操作和使用方法。 可以先载入一个样例程序(在本模拟器所在的文件夹下的“样例程序”文件夹中),然后 分别以单步执行一条指令、执行多条指令、连续执行、设置断点等的方式运行程序,观察程 序的执行情况,观察CPU中寄存器和存储器的内容的变化。4. 选择“文件”→“载入程序”选项,加载样例程序alltest.asm,然后查看“代码”窗口, 查看程序所在的位置(起始地址为0x00000100)。5. 查看“寄存器”窗口PC寄存器的值:[PC]=0x。6. 执行load和store指令。步骤如下: (1)单步执行一条指令(F7)。 (2)下一条指令地址为0x,是一条(有,无)符号载入(字节,半字,字)指令。 (3)单步执行1条指令(F7)。 (4)查看R1的值,[R1]=0x____________________。 (5)下一条指令地址为0x,是一条(有,无)符号载入(字,半字,字)指令。 (6)单步执行1条指令。 (7)查看R1的值,[R1]=0x____________________。 (8)下一条指令地址为0x,是一条(有,无)符号载入(字,半字,字)指令。 (9)单步执行1条指令。 (10)查看R1的值,[R1]=0x____________________。 (11)单步执行1条指令。 (12)下一条指令地址为0x,是一条保存(字,半字,字)指令。 (13)单步执行1条指令。 (14)查看内存BUFFER处字的值,值为0x____________________。7. 执行算术运算类指令。步骤如下: (1)双击“寄存器”窗口里的R1,将其值修改为2。 (2)双击“寄存器”窗口里的R2,将其值修改为3。 (3)单步执行1条指令。 (4)下一条指令地址为0x,是一条加法指令。 (5)单步执行1条指令。 (6)查看R3的值,[R3]=0x____________________。 (7)下一条指令地址为0x,是一条乘法指令。 (8)单步执行1条指令。 (9)查看LO、HI的值,[LO]=0x__________________,[HI]=0x。8. 执行逻辑运算类指令。步骤如下: (1)双击“寄存器”窗口里的R1,将其值修改为0xFFFF0000。 (2)双击“寄存器”窗口里的R2,将其值修改为0xFF00FF00。 (3)单步执行1条指令。 (4)下一条指令地址为0x,是一条逻辑与运算指令,第二个操作数寻址方式是(寄存器直接寻址,立即数寻址)。 (5)单步执行1条指令。 (6)查看R3的值,[R3]=0x____________________。 (7)下一条指令地址为:0x,是一条逻辑或指令,第二个操作数寻址方式是(寄存器直接寻址,立即数寻址)。 (8)单步执行1条指令。 (9)查看R3的值,[R3]=0x_____________________。9. 执行控制转移类指令。步骤如下: (1)双击“寄存器”窗口里的R1,将其值修改为2。 (2)双击“寄存器”窗口里的R2,将其值修改为2。 (3)单步执行1条指令。 (4)下一条指令地址为0x,是一条BEQ指令,其测试条件 是:,目标地址为0x。 (5)单步执行1条指令。 (6)查看PC的值,[PC]=0x,表明分支(成功,失败)。 (7)下一条指令地址是一条BGEZ指令,其测试条件是,目标地址为 0x。 (8)单步执行1条指令。 (9)查看PC的值,[PC]=0x,表明分支(成功,失败)。 (10)下一条指令地址是一条BGEZAL指令,其测试条件是,目标地址为 0x。 (11)单步执行1条指令。 (12)查看PC的值,[PC]=0x,表明分支(成功,失败);查看R31的值,[R31]=0x____________________。 (13)单步执行1条指令。 (14)查看R1的值,[R1]=0x____________________。 (15)下一条指令地址为0x,是一条JALR指令,保存目标地址的寄存器为R_,保存返回地址的目标寄存器为R。 (16)单步执行1条指令。 (17)查看PC和R3的值,[PC]=0x,[R3]=0x____________________。
启动模拟器双击MIPSsim.exe,即可启动该模拟器。模拟器启动时,自动将自己初始化为默认状态。所设置的默认值为:所有通用寄存器和浮点寄存器为全0;内存清零;流水寄存器为全0;清空时钟图、断点、统计数据;内存大小为4096字节;载入起始地址为0;浮点加法、乘法、除法部件的个数均为1;浮点加法、乘法、除法运算延迟分别为6、7、10个时钟周期;采用流水方式;不采用定向机制;不采用延迟槽;采用符号地址;采用绝对周期计数。当模拟器工作在非流水方式下(配置菜单中的“流水方式”前没有“√”号)时,下面叙述中有关流水段的内容都没有意义,应该忽略之。
MIPSsim的窗口在流水方式下,模拟器主界面中共有7个子窗口,它们是:代码窗口、寄存器窗口、流水线窗口、时钟周期图窗口、内存窗口、统计窗口和断点窗口。每一个窗口都可以被收起(变成小图标)、展开、拖动位置和放大/缩小。当要看窗口的全部内容时,可以将其放大到最大。在非流水方式下,只有代码窗口、寄存器窗口、内存窗口和断点窗口。1.代码窗口代码窗口给出内存中代码的列表,每条指令占一行,按地址顺序排列。每行有5列(当全部显示时):地址、断点标记、指令的机器码、流水段标记和符号指令。如图1所示。图1代码窗口图中不同抹色的行代表相应的指令所处的执行段。黄色代表IF段,绿色代表ID段,红色代表EX段,青色代表MEM段,棕色代表WB段。该窗口中各列的含义如下:地址:以16进制的形式给出。内存是按字节寻址的,每条指令占4个字节。当采用符号地址时,会在相应的位置给出汇编程序中出现的标号。断点标记:如果在该指令处设有断点,则显示相应的标记。断点标记的形式为B.X(X为段名),表示该断点是设置在该指令的“X”段。例如,若某行的断点标记为“B.EX”,则表示在该指令的EX段设置了断点。当模拟器工作在非流水方式下时,断点的标记为B。机器码:该行所对应的指令的十六进制机器码。若该行无指令,则仅仅显示4字节数据;流水段标记:表示当该指令正在执行时,它在当前周期该指令所处的流水段。当模拟器工作在非流水方式下时,它没有意义。符号指令:机器代码所对应的符号指令。在该窗口中选中某行(用鼠标左键单击),然后再点击鼠标右键,就会弹出菜单:设置断点,清除断点,它们分别用于在所选指令处设置断点和清除断点。设置断点选择(点击)要设断点的指令点击右键“设置断点”,弹出“设置断点”小对话框,在“段”的下拉框中选择断点所在的流水段(在非流水方式下,不存在该下拉框),单击“确定”即可。清除断点选择(点击)指令点击右键“清除断点”,则设置在该指令处的断点被删除。2.寄存器窗口寄存器窗口显示MIPSsim模拟器中的寄存器的内容。共有4组寄存器:通用寄存器、浮点寄存器、特殊寄存器和流水寄存器,分为4栏来显示。每一栏下分别有各自的数据格式选项,如图2所示。图2寄存器窗口(1)通用寄存器MIPS64有32个64位通用寄存器:R0,R1,…,R31。它们被简称为GPRs(General-PurposeRegisters),有时也被称为整数寄存器。R0的值永远是0。通过数据格式选项,可以选择显示的格式是十进制还是十六进制。(2)浮点寄存器共有32个64位浮点数寄存器:F0,F1,…,F31。它们被简称为FPRs(Floating-PointRegisters)。它们既可以用来存放32个单精度浮点数(32位),也可以用来存放32个双精度浮点数(64位)。存储单精度浮点数(32位)时,只用到FPR的一半,其另一半没用。(3)特殊寄存器特殊寄存器有4个:PC:程序计数器(32位);LO:乘法寄存器的低位;HI:乘法寄存器的高位;FCSR:浮点状态寄存器。(4)流水寄存器IF/ID.IR:流水段IF与ID之间的指令寄存器;IF/ID.NPC:流水段IF与ID之间的下一指令程序计数器;ID/EX.A:流水段ID与EX之间的第一操作数寄存器;ID/EX.B:流水段ID与EX之间的第二操作数寄存器;ID/EX.Imm:流水段ID与EX之间的立即数寄存器;ID/EX.IR:存放从IF/ID.IR传过来的指令;EX/MEM.ALUo:流水段EX与MEM之间的ALU计算结果寄存器;EX/MEM.IR:存放从ID/EX.IR传过来的指令;MEM/WB.LMD:流水段MEM与WB之间的数据寄存器,用于存放从存储器读出的数据;MEM/WB.ALUo:存放从EX/MEM.ALUo传过来的计算结果;MEM/WB.IR:存放从EX/MEM.IR传过来的指令。除了流水寄存器外,其他寄存器都可以修改。只要双击某寄存器所在的行,系统就会弹出一个小对话框。该对话框显示了该寄存器原来的值。在新值框中填入新的值,然后点击“保存”,系统就会将新值写入该寄存器。3.流水线窗口流水线窗口显示流水线在当前配置下的组成以及该流水线的各段在当前周期正在处理的指令。如图3所示。非流水方式下,没有该窗口。图3流水线窗口 在该窗口中,每一个矩形方块代表一个流水段,它们用不同的颜色填充。在该窗口的左侧是IF到WB段,其右边为浮点部件。浮点部件分有浮点加法部件(fadd)、浮点乘法部件fmul)和浮点除法部件(fdiv)三种。在菜单“配置”“常规配置”中修改浮点部件个数,可看到该窗口中对应类型的浮点部件个数会发生相应的变化。 在运行过程中,各段的矩形方块中会显示该段正在处理的指令及其地址(16进制)。当双击某矩形方块时,会弹出窗口显示该段出口处的流水寄存器的内容(16进制)。4.时钟周期图窗口该窗口用于显示程序执行的时间关系,画出各条指令执行时所用的时钟周期。非流水方式下,没有该窗口。以窗口左上为原点,横轴正方向指向右方,表示模拟器先后经过的各个周期(列),纵轴正方向指向下方,表示模拟器中先后执行的各条指令(行)。如图4所示。图4时钟周期图窗口 横坐标有相对周期计数和绝对周期计数两种不同的表示形式。在默认的绝对周期计数下,按0、1、2、… 依次递增的顺序计数。而在相对周期计数下,当前周期记为第0个周期,而其余周期(在左边)则按其相对于当前周期的位置,分别记为-1,-2,-3,…等。在由指令轴和周期轴组成的二维空间下,坐标(n, i)对应的矩形区域表示指令i在第n+1周期时所经过的流水段(假设采用绝对周期计数)。双击某行时,会弹出一个小窗口,显示该指令在各流水段所进行的处理。该窗口中还显示定向的情况。这是用箭头来表示的。若在第m周期和第m+1周期间产生从指令i1到指令i2的定向,则在坐标(m, i1)和(m+1, i2)表示的矩形区域之间会有一个箭头。5. 内存窗口 该窗口显示模拟器内存中的内容,左侧一栏为十六进制地址,右侧为数据,如图5所示。可以直接通过双击来修改其内容。这时会弹出一个“内存修改”对话框,如图6所示。对话框的上部区域为数据类型与格式选择区,通过勾选其中的一项,就可以指定所采用的数据类型与格式。图6 “内存修改”对话框 在该“内存修改”对话框中,地址框最开始显示的是被双击的单元的地址,用户可以直接修改该地址。在新值框中输入新值,然后点击按钮“修改”,模拟器就会把新值写入内存中相应的单元。新值的格式必须与所选的数据类型和格式一致。 “前地址”与“后地址”按钮分别将当前地址减少和增加一个数据长度(字节数),并显示当前地址所指定单元的内容。“前地址”和“后地址”用于连续修改一片的内存数据。“显示”按钮用于显示当前地址所指单元的内容。在修改地址后,点击该按钮就可以显示内存单元的内容。6. 统计窗口该窗口显示模拟器统计的各项数据。如下所示。(非流水方式下,没有该窗口) 汇总: 执行周期总数:0 ID段执行了0条指令 硬件配置: 内存容量:4096 B 加法器个数:1 执行时间(周期数):6 乘法器个数:1 执行时间(周期数)7 除法器个数:1 执行时间(周期数)10 定向机制:不采用 停顿(周期数): RAW停顿:0 占周期总数的百分比:0% 其中: load停顿:0 占所有RAW停顿的百分比:0% 分支/跳转停顿:0 占所有RAW停顿的百分比:0% 浮点停顿:0 占所有RAW停顿的百分比:0% WAW停顿:0 占周期总数的百分比:0% 结构停顿:0 占周期总数的百分比:0% 控制停顿:0 占周期总数的百分比:0% 自陷停顿:0 占周期总数的百分比:0% 停顿周期总数:0 占周期总数的百分比:0% 分支指令: 指令条数:0 占指令总数的百分比:0% 其中: 分支成功:0 占分支指令数的百分比:0% 分支失败:0 占分支指令数的百分比:0% load/store指令: 指令条数:0 占指令总数的百分比:0% 其中: load:0 占load/store指令数的百分比:0% store:0 占load/store指令数的百分比:0% 浮点指令: 指令条数:0 占指令总数的百分比:0%其中: 加法:0 占浮点指令数的百分比:0% 乘法:0 占浮点指令数的百分比:0% 除法:0 占浮点指令数的百分比:0% 自陷指令: 指令条数:0 占指令总数的百分比:0%7. 断点窗口断点一般是指指定的一条指令,当程序执行到该指令时,会中断执行,暂停在该指令上。在本模拟器中,断点可以设定在某条指令的某一个流水段上(如果是在流水方式下)。当该指令执行到相应的流水段时,会中断执行。断点窗口列出当前已经设置的所有断点,每行一个。每行由3部分构成:地址(16进制),流水段名称,符号指令。如图7所示。(在非流水方式下,“段”没有意义) 图7 断点窗口该窗口上方有四个按钮:添加、删除、全部删除、修改。添加单击“添加”,会弹出小对话框“设置断点”,在“地址”框中输入断点的十六进制地址,在“段”的下拉框中选择在哪个流水段中断(非流水方式下,不需要该操作,下同),单击“确定”即可。删除选中某个断点(单击断点列表中相应的一项),单击“删除”,则该断点被清除。 全部删除单击“全部删除”,所有断点都将被清除。修改选中某个断点,单击“修改”,会弹出小对话框“设置断点”,在“地址”框中输入断点的地址,在“段”的下拉框中选择在哪个流水段中断,单击“确定”即可将原断点修改为新设断点。
MIPSsim的菜单1. 文件菜单文件菜单如下所示:(1) CPU复位将模拟器中CPU的状态复位为默认值。(2) 全部复位将整个模拟器的状态复位为默认值。模拟器启动时,也是将状态设置为默认值。(3) 载入程序将模拟器要执行的程序载入模拟器的内存。这个程序可以是汇编程序(.s文件),也可以是汇编后的代码(.bin文件)。点击该菜单后,系统将弹出“载入”对话框,选择要载入的文件,然后点击“打开”。如果是.s文件,系统会对该文件进行汇编。若汇编过程无错误,则将产生的二进制代码载入至模拟器的内存;若有错误,则报告错误信息。如果是.bin文件,则直接将该文件的内容载入到模拟器内存。被载入程序在内存中连续存放,其起始地址默认为0。该起始地址可以从“代码”菜单中的“载入起始地址”来查看和修改。修改时,如果输入的地址不是4的整数倍,模拟器会自动将其归整为4的整数倍。(4)退出退出模拟器。2. 执行菜单该菜单提供了对模拟器执行程序进行控制的功能。在下面的执行方式中,除了单步执行,当遇到断点或者用户手动中止(用“中止”菜单项)时,模拟器将立即暂停执行。在流水方式下,执行菜单如下所示:在非流水方式下,执行菜单如下所示:(1)单步执行一个周期执行一个时钟周期,然后暂停。其快捷键为F7。该菜单仅出现在流水方式下。(2)撤销上一个周期模拟器回退一个时钟周期,即恢复到执行该周期之前的状态。其快捷键为F8。该菜单仅出现在流水方式下。(3)执行多个周期执行多个时钟周期,然后暂停。点击该菜单后,系统会弹出一个小对话框,由用户指定要执行的周期的个数。该菜单仅出现在流水方式下。(4)连续执行从当前状态开始连续执行程序,直到程序结束或遇到断点或用户手动中止。(5)执行到…点击该菜单项后,系统会弹出一个“设定终点”小对话框,由用户指定此次执行的终点,即在哪条指令的哪个流水段暂停。点击“确定”后,模拟器即开始执行程序,直到到达终点位置或遇到断点或用户手动中止。所输入的终点地址将被归整为4的整数倍。(6) 中止点击该菜单项后,模拟器会立即暂停执行。当模拟器执行程序出现长期不结束的状况时,可用该菜单强行使模拟器停止执行。(7)单步执行一条指令执行一条指令,然后暂停。该指令的地址由当前的PC给出。其快捷键为F7。该菜单仅出现在非流水方式下。(8)撤销上一条指令模拟器回退一条指令,即恢复到执行该指令之前的状态。其快捷键为F8。该菜单仅出现在非流水方式下。(9) 执行多条指令执行多条指令,然后暂停。点击该菜单项后,系统会弹出一个小对话框,由用户指定要执行的指令的条数。3. 内存菜单该菜单下有3项:显示,修改,符号表。(1)显示该菜单项用于设置显示内存的值的数据类型与格式。点击该菜单项后,系统会弹出“内存显示”对话框,如图8所示。在选定所要的数据类型与格式后,单击“确定”按钮,内存窗口中的数据就会按指定的数据类型与格式显示。图8 内存显示窗口(2) 修改 该菜单项用于对内存的值进行修改。点击该菜单项后,系统会弹出“内存修改”对话框,该对话框与图6的“内存修改”相同。请参见相关的论述。(3) 符号表该菜单项用于显示符号表。符号表中列出了当前被执行的程序中各符号的地址(内存地址)。4. 代码菜单(1) 载入起始地址该菜单项用于显示和修改代码载入内存的起始地址。如果要修改,只要在弹出的“载入起始地址”对话框中输入新的地址,然后点击“确认”即可。(2) 设置断点点击该菜单项,会弹出“设置断点”小对话框,输入断点地址,并在“段”的下拉框中选择断点所在的流水段(在非流水方式下,不存在该下拉框),单击“确定”即可。(3) 取消断点在代码窗口中选择某条指令,然后点击该菜单项,则设置在该指令处的断点被删除。(4) 清除所有断点清除所有断点。5. 配置菜单配置菜单用于修改模拟器的配置。需要注意的是:修改配置将会使模拟器复位。配置菜单如下所示:(1) 常规配置该菜单项用于查看和设置以下参数:(其默认值见“一、启动程序”)内存容量;浮点加法运算部件个数浮点乘法运算部件个数浮点除法运算部件个数浮点加法运算延迟周期数浮点乘法运算延迟周期数浮点除法运算延迟周期数如果要修改这些参数,只要直接修改,然后点击“确定”按钮即可。(2) 流水方式“流水方式”开关。当该项被勾选(即其前面有个“√”号)时,模拟器按流水方式工作,能模拟流水线的工作过程。否则(即没有被勾选)就是按串行方式执行程序。该项的勾选和去选(即去掉其前面的“√”号)都是通过点击该项来实现的。下同。当从流水方式切换为非流水方式(或相反)时,系统将强行使模拟器的状态复位。(3) 符号地址“符号地址”开关。当该项被勾选时,代码窗口中的代码将显示原汇编程序中所采用的标号。否则(即没有被勾选)就不显示。(4) 绝对周期计数“绝对周期计数”开关。当该项被勾选时,时钟周期图窗口中的横坐标将显示为绝对周期,即时钟周期按0、1、2、… 顺序递增的顺序计数。否则(即没有被勾选)就按相对周期计数,即把当前周期记为第0个周期,而其余周期(在左边)则按其相对于当前周期的位置,分别记为-1,-2,-3,…等。在非流水方式下,该菜单项不起作用(变灰)。(5) 定向“定向”开关,用于指定是否采用定向技术。当该项被勾选时,模拟器在执行程序时将采用定向技术。否则就不采用。在非流水方式下,该菜单项不起作用(变灰)。(6) 延迟槽“延迟槽”开关,用于指定是否采用延迟分支技术。当该项被勾选时,模拟器在执行程序时将采用一个延迟槽。否则就不采用。在非流水方式下,该菜单项不起作用(变灰)。(7) 载入配置用于将一个配置文件(.cfg)载入模拟器,模拟器将按该文件进行配置并复位。点击该菜单项,系统将弹出“打开”对话框,让用户指定所要载入的配置文件。(8) 保存配置用于将模拟器当前的配置保存到一个配置文件(.cfg)中,以便以后重用。点击该菜单项,系统将弹出“保存文件”对话框,让用户指定配置文件的名称和位置。6. 窗口菜单(1) 平铺将当前已打开的子窗口平铺在主窗口中。(2) 层叠将当前已打开的子窗口层叠在主窗口中。(3) 打开所有将所有子窗口都打开。(4) 收起所有将所有子窗口最小化。(5) 选择子窗口在该菜单中还列出了所有的子窗口的名称,选择其中的任一个,将使该窗口被选中,并被提到最上层。7. 帮助菜单该菜单下有两项:“帮助”和“关于MIPSsim”。前者提供用户手册,后者给出关于MIPSsim的版权信息和设计开发者信息。
MIPSsim的菜单1. 文件菜单文件菜单如下所示:(1) CPU复位将模拟器中CPU的状态复位为默认值。(2) 全部复位将整个模拟器的状态复位为默认值。模拟器启动时,也是将状态设置为默认值。(3) 载入程序将模拟器要执行的程序载入模拟器的内存。这个程序可以是汇编程序(.s文件),也可以是汇编后的代码(.bin文件)。点击该菜单后,系统将弹出“载入”对话框,选择要载入的文件,然后点击“打开”。如果是.s文件,系统会对该文件进行汇编。若汇编过程无错误,则将产生的二进制代码载入至模拟器的内存;若有错误,则报告错误信息。如果是.bin文件,则直接将该文件的内容载入到模拟器内存。被载入程序在内存中连续存放,其起始地址默认为0。该起始地址可以从“代码”菜单中的“载入起始地址”来查看和修改。修改时,如果输入的地址不是4的整数倍,模拟器会自动将其归整为4的整数倍。(4)退出退出模拟器。2. 执行菜单该菜单提供了对模拟器执行程序进行控制的功能。在下面的执行方式中,除了单步执行,当遇到断点或者用户手动中止(用“中止”菜单项)时,模拟器将立即暂停执行。在流水方式下,执行菜单如下所示:在非流水方式下,执行菜单如下所示:(1)单步执行一个周期执行一个时钟周期,然后暂停。其快捷键为F7。该菜单仅出现在流水方式下。(2)撤销上一个周期模拟器回退一个时钟周期,即恢复到执行该周期之前的状态。其快捷键为F8。该菜单仅出现在流水方式下。(3)执行多个周期执行多个时钟周期,然后暂停。点击该菜单后,系统会弹出一个小对话框,由用户指定要执行的周期的个数。该菜单仅出现在流水方式下。(4)连续执行从当前状态开始连续执行程序,直到程序结束或遇到断点或用户手动中止。(5)执行到…点击该菜单项后,系统会弹出一个“设定终点”小对话框,由用户指定此次执行的终点,即在哪条指令的哪个流水段暂停。点击“确定”后,模拟器即开始执行程序,直到到达终点位置或遇到断点或用户手动中止。所输入的终点地址将被归整为4的整数倍。(6) 中止点击该菜单项后,模拟器会立即暂停执行。当模拟器执行程序出现长期不结束的状况时,可用该菜单强行使模拟器停止执行。(7)单步执行一条指令执行一条指令,然后暂停。该指令的地址由当前的PC给出。其快捷键为F7。该菜单仅出现在非流水方式下。(8)撤销上一条指令模拟器回退一条指令,即恢复到执行该指令之前的状态。其快捷键为F8。该菜单仅出现在非流水方式下。(9) 执行多条指令执行多条指令,然后暂停。点击该菜单项后,系统会弹出一个小对话框,由用户指定要执行的指令的条数。3. 内存菜单该菜单下有3项:显示,修改,符号表。(1)显示该菜单项用于设置显示内存的值的数据类型与格式。点击该菜单项后,系统会弹出“内存显示”对话框,如图8所示。在选定所要的数据类型与格式后,单击“确定”按钮,内存窗口中的数据就会按指定的数据类型与格式显示。图8 内存显示窗口(2) 修改 该菜单项用于对内存的值进行修改。点击该菜单项后,系统会弹出“内存修改”对话框,该对话框与图6的“内存修改”相同。请参见相关的论述。(3) 符号表该菜单项用于显示符号表。符号表中列出了当前被执行的程序中各符号的地址(内存地址)。4. 代码菜单(1) 载入起始地址该菜单项用于显示和修改代码载入内存的起始地址。如果要修改,只要在弹出的“载入起始地址”对话框中输入新的地址,然后点击“确认”即可。(2) 设置断点点击该菜单项,会弹出“设置断点”小对话框,输入断点地址,并在“段”的下拉框中选择断点所在的流水段(在非流水方式下,不存在该下拉框),单击“确定”即可。(3) 取消断点在代码窗口中选择某条指令,然后点击该菜单项,则设置在该指令处的断点被删除。(4) 清除所有断点清除所有断点。5. 配置菜单配置菜单用于修改模拟器的配置。需要注意的是:修改配置将会使模拟器复位。配置菜单如下所示:(1) 常规配置该菜单项用于查看和设置以下参数:(其默认值见“一、启动程序”)内存容量;浮点加法运算部件个数浮点乘法运算部件个数浮点除法运算部件个数浮点加法运算延迟周期数浮点乘法运算延迟周期数浮点除法运算延迟周期数如果要修改这些参数,只要直接修改,然后点击“确定”按钮即可。(2) 流水方式“流水方式”开关。当该项被勾选(即其前面有个“√”号)时,模拟器按流水方式工作,能模拟流水线的工作过程。否则(即没有被勾选)就是按串行方式执行程序。该项的勾选和去选(即去掉其前面的“√”号)都是通过点击该项来实现的。下同。当从流水方式切换为非流水方式(或相反)时,系统将强行使模拟器的状态复位。(3) 符号地址“符号地址”开关。当该项被勾选时,代码窗口中的代码将显示原汇编程序中所采用的标号。否则(即没有被勾选)就不显示。(4) 绝对周期计数“绝对周期计数”开关。当该项被勾选时,时钟周期图窗口中的横坐标将显示为绝对周期,即时钟周期按0、1、2、… 顺序递增的顺序计数。否则(即没有被勾选)就按相对周期计数,即把当前周期记为第0个周期,而其余周期(在左边)则按其相对于当前周期的位置,分别记为-1,-2,-3,…等。在非流水方式下,该菜单项不起作用(变灰)。(5) 定向“定向”开关,用于指定是否采用定向技术。当该项被勾选时,模拟器在执行程序时将采用定向技术。否则就不采用。在非流水方式下,该菜单项不起作用(变灰)。(6) 延迟槽“延迟槽”开关,用于指定是否采用延迟分支技术。当该项被勾选时,模拟器在执行程序时将采用一个延迟槽。否则就不采用。在非流水方式下,该菜单项不起作用(变灰)。(7) 载入配置用于将一个配置文件(.cfg)载入模拟器,模拟器将按该文件进行配置并复位。点击该菜单项,系统将弹出“打开”对话框,让用户指定所要载入的配置文件。(8) 保存配置用于将模拟器当前的配置保存到一个配置文件(.cfg)中,以便以后重用。点击该菜单项,系统将弹出“保存文件”对话框,让用户指定配置文件的名称和位置。6. 窗口菜单(1) 平铺将当前已打开的子窗口平铺在主窗口中。(2) 层叠将当前已打开的子窗口层叠在主窗口中。(3) 打开所有将所有子窗口都打开。(4) 收起所有将所有子窗口最小化。(5) 选择子窗口在该菜单中还列出了所有的子窗口的名称,选择其中的任一个,将使该窗口被选中,并被提到最上层。7. 帮助菜单该菜单下有两项:“帮助”和“关于MIPSsim”。前者提供用户手册,后者给出关于MIPSsim的版权信息和设计开发者信息。
2.创建流水线接下来以一个 Java Spring Boot 的代码库为例,为您讲解如何进行构建并部署到阿里云 ECS 服务器。在本机浏览器中,复制如下云效Flow地址,粘贴并访问,并使用您自己的阿里云账号登录。https://flow.aliyun.com2. 在流水线Flow页面,单击右上角的新建流水线。3. 在选择流水线模板对话框中,选择Java>Java · 构建、部署到阿里云ECS/自有主机>创建。
一、触发流水线运行在运行配置对话框中,填入体验主机ECS的公网地址(在阿里云体验实验室的云产品资源列表复制即可),然后单击运行。如下图所示,大概经过1-2分钟,流水线即可自动完成构建和部署。成功部署的流水线如下图所示,单击活动校验下方的访问站点或者手机扫描二维码,即可看到成功部署的人生模拟器游戏啦。二、体验成功,玩一把小游戏吧
一、创建自动部署流水线单击云效左侧的九宫格,选择流水线,前往云效流水线Flow。在我的流水线页面,单击创建流水线。在选择流水线模板对话框,单击Node.js,选择Node.js·ECS部署【人生重开模拟器】小游戏模板,单击创建。二、配置流水线的代码源前面我们将代码上传到了云效Codeup,所以在添加流水线源面板,选择Codeup,单击添加服务连接。说明:如果您使用的账号已有服务连接,即可直接使用,不需要重新创建服务链接。在新建服务连接对话框中,选择您要授权的账号和使用范围,单击创建。代码仓库选择lifeRestart,默认分支选择master,然后单击添加。三、配置要部署的制品及目标主机在流程配置页面,单击主机部署。在编辑面板,制品选择默认构建的制品。在编辑面板,主机组选择新建主机组。在选择主机类型对话框中,单击免费体验主机。按照界面指引,前往阿里云体验实验室,单击立即体验,然后单击创建资源,1分钟左右,即可创建免费的ECS主机。将创建出的ECS的AK ID等信息复制填入,单击保存。填入成功后,如下图所示,即为导入主机成功。在编辑面板中下滑,您可以看到默认的部署脚本和部署策略。无需改动,我们直接单击保存并运行。
2. 从机器启动到操作系统运行的过程一:BIOS 启动过程当计算机加电后,一般不直接执行操作系统,而是执行系统初始化软件完成基本 IO 初始化和引导加载功能。简单地说,系统初始化软件就是在操作系统内核运行之前运行的一段小软件。通过这段小软件,我们可以初始化硬件设备、建立系统的内存空间映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。最终引导加载程序把操作系统内核映像加载到 RAM 中,并将系统控制权传递给它。对于绝大多数计算机系统而言,操作系统和应用软件是存放在磁盘(硬盘/软盘)、光盘、EPROM、ROM、Flash 等可在掉电后继续保存数据的存储介质上。计算机启动后,CPU 一开始会到一个特定的地址开始执行指令,这个特定的地址存放了系统初始化软件,负责完成计算机基本的 IO 初始化,这是系统加电后运行的第一段软件代码。对于 Intel 80386 的体系结构而言,PC 机中的系统初始化软件由 BIOS (Basic Input Output System,即基本输入/输出系统,其本质是一个固化在主板 Flash/CMOS 上的软件)和位于软盘/硬盘引导扇区中的 OS Boot Loader(在 ucore 中的 bootasm.S 和 bootmain.c)一起组成。BIOS 实际上是被固化在计算机 ROM(只读存储器)芯片上的一个特殊的软件,为上层软件提供最底层的、最直接的硬件控制与支持。更形象地说,BIOS 就是 PC 计算机硬件与上层软件程序之间的一个"桥梁",负责访问和控制硬件。以 Intel 80386 为例,计算机加电后,CPU 从物理地址 0xFFFFFFF0(由初始化的 CS:EIP 确定,此时 CS 和 IP 的值分别是 0xF000 和 0xFFF0))开始执行。在 0xFFFFFFF0 这里只是存放了一条跳转指令,通过跳转指令跳到 BIOS 例行程序起始点。BIOS 做完计算机硬件自检和初始化后,会选择一个启动设备(例如软盘、硬盘、光盘等),并且读取该设备的第一扇区(即主引导扇区或启动扇区)到内存一个特定的地址 0x7c00 处,然后 CPU 控制权会转移到那个地址继续执行。至此 BIOS 的初始化工作做完了,进一步的工作交给了 ucore 的 bootloader。补充信息Intel 的 CPU 具有很好的向后兼容性。在 16 位的 8086 CPU 时代,内存限制在 1MB 范围内,且 BIOS 的代码固化在 EPROM 中。在基于 Intel 的 8086 CPU 的 PC 机中的 EPROM 被编址在 1 M B 内存地址空间的最高 64KB 中。PC 加电后,CS 寄存器初始化为 0xF000,IP 寄存器初始化为 0xFFF0,所以 CPU 要执行的第一条指令的地址为 CS:IP=0xF000:0XFFF0(Segment:Offset 表示)=0xFFFF0(Linear 表示)。这个地址位于被固化 EPROM 中,指令是一个长跳转指令JMP F000:E05B。这样就开启了 BIOS 的执行过程。到了 32 位的 80386 CPU 时代,内存空间扩大到了 4G,多了段机制和页机制,但 Intel 依然很好地保证了 80386 向后兼容 8086。地址空间的变化导致无法直接采用 8086 的启动约定。如果把 BIOS 启动固件编址在 0xF000 起始的 64KB 内存地址空间内,就会把整个物理内存地址空间隔离成不连续的两段,一段是 0xF000 以前的地址,一段是 1MB 以后的地址,这很不协调。为此,intel 采用了一个折中的方案:默认将执行 BIOS ROM 编址在 32 位内存地址空间的最高端,即位于 4GB 地址的最后一个 64KB 内。在 PC 系统开机复位时,CPU 进入实模式,并将 CS 寄存器设置成 0xF000,将它的 shadow register 的 Base 值初始化设置为 0xFFFF0000,EIP 寄存器初始化设置为 0x0000FFF0。所以机器执行的第一条指令的物理地址是 0xFFFFFFF0。80386 的 BIOS 代码也要和以前 8086 的 BIOS 代码兼容,故地址 0xFFFFFFF0 处的指令还是一条长跳转指令`jmp F000:E05B`。注意,这个长跳转指令会触发更新 CS 寄存器和它的 shadow register,即执行`jmp F000 : E05B`后,CS 将被更新成 0xF000。表面上看 CS 其实没有变化,但 CS 的 shadow register 被更新为另外一个值了,它的 Base 域被更新成 0x000F0000,此时形成的物理地址为 Base+EIP=0x000FE05B,这就是 CPU 执行的第二条指令的地址。此时这条指令的地址已经是 1M 以内了,且此地址不再位于 BIOS ROM 中,而是位于 RAM 空间中。由于 Intel 设计了一种映射机制,将内存高端的 BIOS ROM 映射到 1MB 以内的 RAM 空间里,并且可以使这一段被映射的 RAM 空间具有与 ROM 类似的只读属性。所以 PC 机启动时将开启这种映射机制,让 4GB 地址空间的最高一个 64KB 的内容等同于 1MB 地址空间的最高一个 64K 的内容,从而使得执行了长跳转指令后,其实是回到了早期的 8086 CPU 初始化控制流,保证了向下兼容。二:bootloader 启动过程BIOS 将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行 bootloader。bootloader 完成的工作包括:切换到保护模式,启用分段机制读磁盘中 ELF 执行文件格式的 ucore 操作系统到内存显示字符串信息把控制权交给 ucore 操作系统对应其工作的实现文件在 lab1 中的 boot 目录下的三个文件 asm.h、bootasm.S 和 bootmain.c。下面从原理上介绍完成上述工作的计算机系统硬件和软件背景知识。1. 保护模式和分段机制为何要了解 Intel 80386 的保护模式和分段机制?首先,我们知道 Intel 80386 只有在进入保护模式后,才能充分发挥其强大的功能,提供更好的保护机制和更大的寻址空间,否则仅仅是一个快速的 8086 而已。没有一定的保护机制,任何一个应用软件都可以任意访问所有的计算机资源,这样也就无从谈起操作系统设计了。且 Intel 80386 的分段机制一直存在,无法屏蔽或避免。其次,在我们的 bootloader 设计中,涉及到了从实模式到保护模式的处理,我们的操作系统功能(比如分页机制)是建立在 Intel 80386 的保护模式上来设计的。如果我们不了解保护模式和分段机制,则我们面向 Intel 80386 体系结构的操作系统设计实际上是建立在一个空中楼阁之上。【注意】虽然大家学习过 X86 汇编,对 X86 硬件架构有一定了解,但对 X86 保护模式和 X86 系统编程可能了解不够。为了能够清楚了解各个实验中汇编代码的含义,我们建议大家阅读如下参考资料:可先回顾一下 lab0-manual 中的“了解处理器硬件”一节的内容。《Intel 80386 Reference Programmers Manual-i386》:第四、六、九、十章。在后续实验中,还可以进一步阅读第五、七、八等章节。(1) 实模式在 bootloader 接手 BIOS 的工作后,当前的 PC 系统处于实模式(16 位模式)运行状态,在这种状态下软件可访问的物理内存空间不能超过 1MB,且无法发挥 Intel 80386 以上级别的 32 位 CPU 的 4GB 内存管理能力。实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,操作系统和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址。这样,用户程序的一个指针如果指向了操作系统区域或其他用户程序区域,并修改了内容,那么其后果就很可能是灾难性的。通过修改 A20 地址线可以完成从实模式到保护模式的转换。有关 A20 的进一步信息可参考附录“关于 A20 Gate”。(2) 保护模式只有在保护模式下,80386 的全部 32 根地址线有效,可寻址高达 4G 字节的线性地址空间和物理地址空间,可访问 64TB(有 2^14 个段,每个段最大空间为 2^32 字节)的逻辑地址空间,可采用分段存储管理机制和分页存储管理机制。这不仅为存储共享和保护提供了硬件支持,而且为实现虚拟存储提供了硬件支持。通过提供 4 个特权级和完善的特权检查机制,既能实现资源共享又能保证代码数据的安全及任务的隔离。【补充】保护模式下,有两个段表:GDT(Global Descriptor Table)和 LDT(Local Descriptor Table),每一张段表可以包含 8192 (2^13)个描述符[1],因而最多可以同时存在 2 * 2^13 = 2^14 个段。虽然保护模式下可以有这么多段,逻辑地址空间看起来很大,但实际上段并不能扩展物理地址空间,很大程度上各个段的地址空间是相互重叠的。目前所谓的 64TB(2^(14+32)=2^46)逻辑地址空间是一个理论值,没有实际意义。在 32 位保护模式下,真正的物理空间仍然只有 2^32 字节那么大。注:在 ucore lab 中只用到了 GDT,没有用 LDT。Reference: [1] 3.5.1 Segment Descriptor Tables, Intel® 64 and IA-32 Architectures Software Developer’s Manual(3) 分段存储管理机制只有在保护模式下才能使用分段存储管理机制。分段机制将内存划分成以起始地址和长度限制这两个二维参数表示的内存块,这些内存块就称之为段(Segment)。编译器把源程序编译成执行程序时用到的代码段、数据段、堆和栈等概念在这里可以与段联系起来,二者在含义上是一致的。分段机涉及 4 个关键内容:逻辑地址、段描述符(描述段的属性)、段描述符表(包含多个段描述符的“数组”)、段选择子(段寄存器,用于定位段描述符表中表项的索引)。转换逻辑地址(Logical Address,应用程序员看到的地址)到物理地址(Physical Address, 实际的物理内存地址)分以下两步:[1] 分段地址转换:CPU 把逻辑地址(由段选择子 selector 和段偏移 offset 组成)中的段选择子的内容作为段描述符表的索引,找到表中对应的段描述符,然后把段描述符中保存的段基址加上段偏移值,形成线性地址(Linear Address)。如果不启动分页存储管理机制,则线性地址等于物理地址。 [2] 分页地址转换,这一步中把线性地址转换为物理地址。(注意:这一步是可选的,由操作系统决定是否需要。在后续试验中会涉及。上述转换过程对于应用程序员来说是不可见的。线性地址空间由一维的线性地址构成,线性地址空间和物理地址空间对等。线性地址 32 位长,线性地址空间容量为 4G 字节。分段地址转换的基本过程如下图所示。图 1 分段地址转换基本过程分段存储管理机制需要在启动保护模式的前提下建立。从上图可以看出,为了使得分段存储管理机制正常运行,需要建立好段描述符和段描述符表(参看 bootasm.S,mmu.h,pmm.c)。段描述符在分段存储管理机制的保护模式下,每个段由如下三个参数进行定义:段基地址(Base Address)、段界限(Limit)和段属性(Attributes)。在 ucore 中的 kern/mm/mmu.h 中的 struct segdesc 数据结构中有具体的定义。段基地址:规定线性地址空间中段的起始地址。在 80386 保护模式下,段基地址长 32 位。因为基地址长度与寻址地址的长度相同,所以任何一个段都可以从 32 位线性地址空间中的任何一个字节开始,而不象实方式下规定的边界必须被 16 整除。段界限:规定段的大小。在 80386 保护模式下,段界限用 20 位表示,而且段界限可以是以字节为单位或以 4K 字节为单位。段属性:确定段的各种性质。 - 段属性中的粒度位(Granularity),用符号 G 标记。G=0 表示段界限以字节位位单位,20 位的界限可表示的范围是 1 字节至 1M 字节,增量为 1 字节;G=1 表示段界限以 4K 字节为单位,于是 20 位的界限可表示的范围是 4K 字节至 4G 字节,增量为 4K 字节。 - 类型(TYPE):用于区别不同类型的描述符。可表示所描述的段是代码段还是数据段,所描述的段是否可读/写/执行,段的扩展方向等。 - 描述符特权级(Descriptor Privilege Level)(DPL):用来实现保护机制。 - 段存在位(Segment-Present bit):如果这一位为 0,则此描述符为非法的,不能被用来实现地址转换。如果一个非法描述符被加载进一个段寄存器,处理器会立即产生异常。图 5-4 显示了当存在位为 0 时,描述符的格式。操作系统可以任意的使用被标识为可用(AVAILABLE)的位。 - 已访问位(Accessed bit):当处理器访问该段(当一个指向该段描述符的选择子被加载进一个段寄存器)时,将自动设置访问位。操作系统可清除该位。上述参数通过段描述符来表示,段描述符的结构如下图所示:图 2 段描述符结构全局描述符表 全局描述符表的是一个保存多个段描述符的“数组”,其起始地址保存在全局描述符表寄存器 GDTR 中。GDTR 长 48 位,其中高 32 位为基地址,低 16 位为段界限。由于 GDT 不能有 GDT 本身之内的描述符进行描述定义,所以处理器采用 GDTR 为 GDT 这一特殊的系统段。注意,全局描述符表中第一个段描述符设定为空段描述符。GDTR 中的段界限以字节为单位。对于含有 N 个描述符的描述符表的段界限通常可设为 8*N-1。在 ucore 中的 boot/bootasm.S 中的 gdt 地址处和 kern/mm/pmm.c 中的全局变量数组 gdt[]分别有基于汇编语言和 C 语言的全局描述符表的具体实现。选择子线性地址部分的选择子是用来选择哪个描述符表和在该表中索引一个描述符的。选择子可以做为指针变量的一部分,从而对应用程序员是可见的,但是一般是由连接加载器来设置的。选择子的格式如下图所示:图 3 段选择子结构索引(Index):在描述符表中从 8192 个描述符中选择一个描述符。处理器自动将这个索引值乘以 8(描述符的长度),再加上描述符表的基址来索引描述符表,从而选出一个合适的描述符。表指示位(Table Indicator,TI):选择应该访问哪一个描述符表。0 代表应该访问全局描述符表(GDT),1 代表应该访问局部描述符表(LDT)。请求特权级(Requested Privilege Level,RPL):保护机制,在后续试验中会进一步讲解。全局描述符表的第一项是不能被 CPU 使用,所以当一个段选择子的索引(Index)部分和表指示位(Table Indicator)都为 0 的时(即段选择子指向全局描述符表的第一项时),可以当做一个空的选择子(见 mmu.h 中的 SEG_NULL)。当一个段寄存器被加载一个空选择子时,处理器并不会产生一个异常。但是,当用一个空选择子去访问内存时,则会产生异常。(4) 保护模式下的特权级在保护模式下,特权级总共有 4 个,编号从 0(最高特权)到 3(最低特权)。有 3 种主要的资源受到保护:内存,I/O 端口以及执行特殊机器指令的能力。在任一时刻,x86 CPU 都是在一个特定的特权级下运行的,从而决定了代码可以做什么,不可以做什么。这些特权级经常被称为为保护环(protection ring),最内的环(ring 0)对应于最高特权 0,最外面的环(ring 3)一般给应用程序使用,对应最低特权 3。在 ucore 中,CPU 只用到其中的 2 个特权级:0(内核态)和 3(用户态)。有大约 15 条机器指令被 CPU 限制只能在内核态执行,这些机器指令如果被用户模式的程序所使用,就会颠覆保护模式的保护机制并引起混乱,所以它们被保留给操作系统内核使用。如果企图在 ring 0 以外运行这些指令,就会导致一个一般保护异常(general-protection exception)。对内存和 I/O 端口的访问也受类似的特权级限制。数据段选择子的整个内容可由程序直接加载到各个段寄存器(如 SS 或 DS 等)当中。这些内容里包含了请求特权级(Requested Privilege Level,简称 RPL)字段。然而,代码段寄存器(CS)的内容不能由装载指令(如 MOV)直接设置,而只能被那些会改变程序执行顺序的指令(如 JMP、INT、CALL)间接地设置。而且 CS 拥有一个由 CPU 维护的当前特权级字段(Current Privilege Level,简称 CPL)。二者结构如下图所示:图 4 DS 和 CS 的结构图代码段寄存器中的 CPL 字段(2 位)的值总是等于 CPU 的当前特权级,所以只要看一眼 CS 中的 CPL,你就可以知道此刻的特权级了。CPU 会在两个关键点上保护内存:当一个段选择符被加载时,以及,当通过线性地址访问一个内存页时。因此,保护也反映在内存地址转换的过程之中,既包括分段又包括分页。当一个数据段选择符被加载时,就会发生下述的检测过程:图 5 内存访问特权级检查过程因为越高的数值代表越低的特权,上图中的 MAX()用于选择 CPL 和 RPL 中特权最低的一个,并与描述符特权级(Descriptor Privilege Level,简称 DPL)比较。如果 DPL 的值大于等于它,那么这个访问可正常进行了。RPL 背后的设计思想是:允许内核代码加载特权较低的段。比如,你可以使用 RPL=3 的段描述符来确保给定的操作所使用的段可以在用户模式中访问。但堆栈段寄存器是个例外,它要求 CPL,RPL 和 DPL 这 3 个值必须完全一致,才可以被加载。下面再总结一下 CPL、RPL 和 DPL:CPL:当前特权级(Current Privilege Level) 保存在 CS 段寄存器(选择子)的最低两位,CPL 就是当前活动代码段的特权级,并且它定义了当前所执行程序的特权级别)DPL:描述符特权(Descriptor Privilege Level) 存储在段描述符中的权限位,用于描述对应段所属的特权等级,也就是段本身能被访问的真正特权级。RPL:请求特权级 RPL(Request Privilege Level) RPL 保存在选择子的最低两位。RPL 说明的是进程对段访问的请求权限,意思是当前进程想要的请求权限。RPL 的值可自由设置,并不一定要求 RPL>=CPL,但是当 RPL<CPL 时,实际起作用的就是 CPL 了,因为访问时的特权级保护检查要判断:max(RPL,CPL)<=DPL 是否成立。所以 RPL 可以看成是每次访问时的附加限制,RPL=0 时附加限制最小,RPL=3 时附加限制最大。2. 地址空间分段机制涉及 5 个关键内容:逻辑地址(Logical Address,应用程序员看到的地址,在操作系统原理上称为虚拟地址,以后提到虚拟地址就是指逻辑地址)、物理地址(Physical Address, 实际的物理内存地址)、段描述符表(包含多个段描述符的“数组”)、段描述符(描述段的属性,及段描述符表这个“数组”中的“数组元素”)、段选择子(即段寄存器中的值,用于定位段描述符表中段描述符表项的索引)(1) 逻辑地址空间从应用程序的角度看,逻辑地址空间就是应用程序员编程所用到的地址空间,比如下面的程序片段: int val=100; int * point=&val;其中指针变量 point 中存储的即是一个逻辑地址。在基于 80386 的计算机系统中,逻辑地址有一个 16 位的段寄存器(也称段选择子,段选择子)和一个 32 位的偏移量构成。(2) 物理地址空间从操作系统的角度看,CPU、内存硬件(通常说的“内存条”)和各种外设是它主要管理的硬件资源而内存硬件和外设分布在物理地址空间中。物理地址空间就是一个“大数组”,CPU 通过索引(物理地址)来访问这个“大数组”中的内容。物理地址是指 CPU 提交到内存总线上用于访问计算机内存和外设的最终地址。物理地址空间的大小取决于 CPU 实现的物理地址位数,在基于 80386 的计算机系统中,CPU 的物理地址空间为 4GB,如果计算机系统实际上有 1GB 物理内存(即我们通常说的内存条),而其他硬件设备的 IO 寄存器映射到起始物理地址为 3GB 的 256MB 大小的地址空间,则该计算机系统的物理地址空间如下所示:+------------------+ <- 0xFFFFFFFF (4GB) | 无效空间 | | | +------------------+ <- addr:3G+256M | 256MB | | IO外设地址空间 | | | +------------------+ <- 0xC0000000(3GB) | | /\/\/\/\/\/\/\/\/\/\ /\/\/\/\/\/\/\/\/\/\ | 无效空间 | +------------------+ <- 0x40000000(1GB) | | | 实际有效内存 | | | +------------------+ <- 0x00100000 (1MB) | BIOS ROM | +------------------+ <- 0x000F0000 (960KB) | 16-bit devices, | | expansion ROMs | +------------------+ <- 0x000C0000 (768KB) | VGA Display | +------------------+ <- 0x000A0000 (640KB) | | | Low Memory | | | +------------------+ <- 0x00000000图 6 X86 计算机系统的物理地址空间(3) 线性地址空间一台计算机只有一个物理地址空间,但在操作系统的管理下,每个程序都认为自己独占整个计算机的物理地址空间。为了让多个程序能够有效地相互隔离和使用物理地址空间,引入线性地址空间(也称虚拟地址空间)的概念。线性地址空间的大小取决于 CPU 实现的线性地址位数,在基于 80386 的计算机系统中,CPU 的线性地址空间为 4GB。线性地址空间会被映射到某一部分或整个物理地址空间,并通过索引(线性地址)来访问其中的内容。线性地址又称虚拟地址,是进行逻辑地址转换后形成的地址索引,用于寻址线性地址空间。但 CPU 未启动分页机制时,线性地址等于物理地址;当 CPU 启动分页机制时,线性地址还需经过分页地址转换形成物理地址后,CPU 才能访问内存硬件和外设。三种地址的关系如下所示:启动分段机制,未启动分页机制:逻辑地址--> (分段地址转换) -->线性地址==物理地址启动分段和分页机制:逻辑地址--> (分段地址转换) -->线性地址-->分页地址转换) -->物理地址在操作系统的管理下,采用灵活的内存管理机制,在只有一个物理地址空间的情况下,可以存在多个线性地址空间。一个典型的线性地址空间3. 硬盘访问概述bootloader 让 CPU 进入保护模式后,下一步的工作就是从硬盘上加载并运行 OS。考虑到实现的简单性,bootloader 的访问硬盘都是 LBA 模式的 PIO(Program IO)方式,即所有的 IO 操作是通过 CPU 访问硬盘的 IO 地址寄存器完成。一般主板有 2 个 IDE 通道,每个通道可以接 2 个 IDE 硬盘。访问第一个硬盘的扇区可设置 IO 地址寄存器 0x1f0-0x1f7 实现的,具体参数见下表。一般第一个 IDE 通道通过访问 IO 地址 0x1f0-0x1f7 来实现,第二个 IDE 通道通过访问 0x170-0x17f 实现。每个通道的主从盘的选择通过第 6 个 IO 偏移地址寄存器来设置。表一 磁盘 IO 地址和对应功能第6位:为1=LBA模式;0 = CHS模式 第7位和第5位必须为1IO地址功能0x1f0读数据,当0x1f7不为忙状态时,可以读。0x1f2要读写的扇区数,每次读写前,你需要表明你要读写几个扇区。最小是1个扇区0x1f3如果是LBA模式,就是LBA参数的0-7位0x1f4如果是LBA模式,就是LBA参数的8-15位0x1f5如果是LBA模式,就是LBA参数的16-23位0x1f6第0~3位:如果是LBA模式就是24-27位 第4位:为0主盘;为1从盘0x1f7状态和命令寄存器。操作时先给命令,再读取,如果不是忙状态就从0x1f0端口读数据当前 硬盘数据是储存到硬盘扇区中,一个扇区大小为 512 字节。读一个扇区的流程(可参看 boot/bootmain.c 中的 readsect 函数实现)大致如下:等待磁盘准备好发出读取扇区的命令等待磁盘准备好把磁盘扇区数据读到指定内存4. ELF 文件格式概述ELF(Executable and linking format)文件格式是 Linux 系统下的一种常用目标文件(object file)格式,有三种主要类型:用于执行的可执行文件(executable file),用于提供程序的进程映像,加载的内存执行。 这也是本实验的 OS 文件类型。用于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。这里只分析与本实验相关的 ELF 可执行文件类型。ELF header 在文件开始处描述了整个文件的组织。ELF 的文件头包含整个执行文件的控制结构,其定义在 elf.h 中:struct elfhdr { uint magic; // must equal ELF_MAGIC uchar elf[12]; ushort type; ushort machine; uint version; uint entry; // 程序入口的虚拟地址 uint phoff; // program header 表的位置偏移 uint shoff; uint flags; ushort ehsize; ushort phentsize; ushort phnum; //program header表中的入口数目 ushort shentsize; ushort shnum; ushort shstrndx; };program header 描述与程序执行直接相关的目标文件结构信息,用来在文件中定位各个段的映像,同时包含其他一些用来为程序创建进程映像所必需的信息。可执行文件的程序头部是一个 program header 结构的数组, 每个结构描述了一个段或者系统准备程序执行所必需的其它信息。目标文件的 “段” 包含一个或者多个 “节区”(section) ,也就是“段内容(Segment Contents)” 。程序头部仅对于可执行文件和共享目标文件有意义。可执行目标文件在 ELF 头部的 e_phentsize 和 e_phnum 成员中给出其自身程序头部的大小。程序头部的数据结构如下表所示:struct proghdr { uint type; // 段类型 uint offset; // 段相对文件头的偏移值 uint va; // 段的第一个字节将被放到内存中的虚拟地址 uint pa; uint filesz; uint memsz; // 段在内存映像中占用的字节数 uint flags; uint align; };根据 elfhdr 和 proghdr 的结构描述,bootloader 就可以完成对 ELF 格式的 ucore 操作系统的加载过程(参见 boot/bootmain.c 中的 bootmain 函数)。[补充材料]Link addr& Load addrLink Address 是指编译器指定代码和数据所需要放置的内存地址,由链接器配置。Load Address 是指程序被实际加载到内存的位置(由程序加载器 ld 配置)。一般由可执行文件结构信息和加载器可保证这两个地址相同。Link Addr 和 LoadAddr 不同会导致:直接跳转位置错误直接内存访问(只读数据区或 bss 等直接地址访问)错误堆和栈等的使用不受影响,但是可能会覆盖程序、数据区域 注意:也存在 Link 地址和 Load 地址不一样的情况(例如:动态链接库)。三:操作系统启动过程当 bootloader 通过读取硬盘扇区把 ucore 在系统加载到内存后,就转跳到 ucore 操作系统在内存中的入口位置(kern/init.c 中的 kern_init 函数的起始地址),这样 ucore 就接管了整个控制权。当前的 ucore 功能很简单,只完成基本的内存管理和外设中断管理。ucore 主要完成的工作包括:初始化终端;显示字符串;显示堆栈中的多层函数调用关系;切换到保护模式,启用分段机制;初始化中断控制器,设置中断描述符表,初始化时钟中断,使能整个系统的中断机制;执行 while(1)死循环。以后的实验中会大量涉及各个函数直接的调用关系,以及由于中断处理导致的异步现象,可能对大家实现操作系统和改正其中的错误有很大影响。而理解好函数调用关系的建立机制和中断处理机制,对后续实验会有很大帮助。下面就练习 5 涉及的函数栈调用关系和练习 6 中的中断机制的建立进行阐述。1. 函数堆栈栈是一个很重要的编程概念(编译课和程序设计课都讲过相关内容),与编译器和编程语言有紧密的联系。理解调用栈最重要的两点是:栈的结构,EBP 寄存器的作用。一个函数调用动作可分解为:零到多个 PUSH 指令(用于参数入栈),一个 CALL 指令。CALL 指令内部其实还暗含了一个将返回地址(即 CALL 指令下一条指令的地址)压栈的动作(由硬件完成)。几乎所有本地编译器都会在每个函数体之前插入类似如下的汇编指令:pushl %ebp movl %esp , %ebp这样在程序执行到一个函数的实际指令前,已经有以下数据顺序入栈:参数、返回地址、ebp 寄存器。由此得到类似如下的栈结构(参数入栈顺序跟调用方式有关,这里以 C 语言默认的 CDECL 为例):+| 栈底方向 | 高位地址 | ... | | ... | | 参数3 | | 参数2 | | 参数1 | | 返回地址 | | 上一层[ebp] | <-------- [ebp] | 局部变量 | 低位地址图 7 函数调用栈结构这两条汇编指令的含义是:首先将 ebp 寄存器入栈,然后将栈顶指针 esp 赋值给 ebp。“mov ebp esp”这条指令表面上看是用 esp 覆盖 ebp 原来的值,其实不然。因为给 ebp 赋值之前,原 ebp 值已经被压栈(位于栈顶),而新的 ebp 又恰恰指向栈顶。此时 ebp 寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原 ebp 入栈后的栈顶),从该地址为基准,向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取函数局部变量值,而该地址处又存储着上一层函数调用时的 ebp 值。一般而言,ss:[ebp+4]处为返回地址,ss:[ebp+8]处为第一个参数值(最后一个入栈的参数值,此处假设其占用 4 字节内存),ss:[ebp-4]处为第一个局部变量,ss:[ebp]处为上一层 ebp 值。由于 ebp 中的地址处总是“上一层函数调用时的 ebp 值”,而在每一层函数调用中,都能通过当时的 ebp 值“向上(栈底方向)”能获取返回地址、参数值,“向下(栈顶方向)”能获取函数局部变量值。如此形成递归,直至到达栈底。这就是函数调用栈。提示:练习 5 的正确实现取决于对这一小节的正确理解和掌握。2. 中断与异常操作系统需要对计算机系统中的各种外设进行管理,这就需要 CPU 和外设能够相互通信才行。一般外设的速度远慢于 CPU 的速度。如果让操作系统通过 CPU“主动关心”外设的事件,即采用通常的轮询(polling)机制,则太浪费 CPU 资源了。所以需要操作系统和 CPU 能够一起提供某种机制,让外设在需要操作系统处理外设相关事件的时候,能够“主动通知”操作系统,即打断操作系统和应用的正常执行,让操作系统完成外设的相关处理,然后在恢复操作系统和应用的正常执行。在操作系统中,这种机制称为中断机制。中断机制给操作系统提供了处理意外情况的能力,同时它也是实现进程/线程抢占式调度的一个重要基石。但中断的引入导致了对操作系统的理解更加困难。在操作系统中,有三种特殊的中断事件。由 CPU 外部设备引起的外部事件如 I/O 中断、时钟中断、控制台中断等是异步产生的(即产生的时刻不确定),与 CPU 的执行无关,我们称之为异步中断(asynchronous interrupt)也称外部中断,简称中断(interrupt)。而把在 CPU 执行指令期间检测到不正常的或非法的条件(如除零错、地址访问越界)所引起的内部事件称作同步中断(synchronous interrupt),也称内部中断,简称异常(exception)。把在程序中使用请求系统服务的系统调用而引发的事件,称作陷入中断(trap interrupt),也称软中断(soft interrupt),系统调用(system call)简称 trap。在后续试验中会进一步讲解系统调用。本实验只描述保护模式下的处理过程。当 CPU 收到中断(通过 8259A 完成,有关 8259A 的信息请看附录 A)或者异常的事件时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理例程中,在完成对这个事件的处理后再跳回到刚才被打断的程序或任务中。中断向量和中断服务例程的对应关系主要是由 IDT(中断描述符表)负责。操作系统在 IDT 中设置好各种中断向量对应的中断描述符,留待 CPU 在产生中断后查询对应中断服务例程的起始地址。而 IDT 本身的起始地址保存在 idtr 寄存器中。(1) 中断描述符表(Interrupt Descriptor Table)中断描述符表把每个中断或异常编号和一个指向中断服务例程的描述符联系起来。同 GDT 一样,IDT 是一个 8 字节的描述符数组,但 IDT 的第一项可以包含一个描述符。CPU 把中断(异常)号乘以 8 做为 IDT 的索引。IDT 可以位于内存的任意位置,CPU 通过 IDT 寄存器(IDTR)的内容来寻址 IDT 的起始地址。指令 LIDT 和 SIDT 用来操作 IDTR。两条指令都有一个显示的操作数:一个 6 字节表示的内存地址。指令的含义如下:LIDT(Load IDT Register)指令:使用一个包含线性地址基址和界限的内存操作数来加载 IDT。操作系统创建 IDT 时需要执行它来设定 IDT 的起始地址。这条指令只能在特权级 0 执行。(可参见 libs/x86.h 中的 lidt 函数实现,其实就是一条汇编指令)SIDT(Store IDT Register)指令:拷贝 IDTR 的基址和界限部分到一个内存地址。这条指令可以在任意特权级执行。IDT 和 IDTR 寄存器的结构和关系如下图所示:图 8 IDT 和 IDTR 寄存器的结构和关系图在保护模式下,最多会存在 256 个 Interrupt/Exception Vectors。范围[0,31]内的 32 个向量被异常 Exception 和 NMI 使用,但当前并非所有这 32 个向量都已经被使用,有几个当前没有被使用的,请不要擅自使用它们,它们被保留,以备将来可能增加新的 Exception。范围[32,255]内的向量被保留给用户定义的 Interrupts。Intel 没有定义,也没有保留这些 Interrupts。用户可以将它们用作外部 I/O 设备中断(8259A IRQ),或者系统调用(System Call 、Software Interrupts)等。(2) IDT gate descriptorsInterrupts/Exceptions 应该使用 Interrupt Gate 和 Trap Gate,它们之间的唯一区别就是:当调用 Interrupt Gate 时,Interrupt 会被 CPU 自动禁止;而调用 Trap Gate 时,CPU 则不会去禁止或打开中断,而是保留它原来的样子。【补充】所谓“自动禁止”,指的是 CPU 跳转到 interrupt gate 里的地址时,在将 EFLAGS 保存到栈上之后,清除 EFLAGS 里的 IF 位,以避免重复触发中断。在中断处理例程里,操作系统可以将 EFLAGS 里的 IF 设上,从而允许嵌套中断。但是必须在此之前做好处理嵌套中断的必要准备,如保存必要的寄存器等。二在 ucore 中访问 Trap Gate 的目的是为了实现系统调用。用户进程在正常执行中是不能禁止中断的,而当它发出系统调用后,将通过 Trap Gate 完成了从用户态(ring 3)的用户进程进了核心态(ring 0)的 OS kernel。如果在到达 OS kernel 后禁止 EFLAGS 里的 IF 位,第一没意义(因为不会出现嵌套系统调用的情况),第二还会导致某些中断得不到及时响应,所以调用 Trap Gate 时,CPU 则不会去禁止中断。总之,interrupt gate 和 trap gate 之间没有优先级之分,仅仅是 CPU 在处理中断时有不同的方法,供操作系统在实现时根据需要进行选择。在 IDT 中,可以包含如下 3 种类型的 Descriptor:Task-gate descriptor (这里没有使用)Interrupt-gate descriptor (中断方式用到)Trap-gate descriptor(系统调用用到)下图图显示了 80386 的任务门描述符、中断门描述符、陷阱门描述符的格式:图 9 X86 的各种门的格式可参见 kern/mm/mmu.h 中的 struct gatedesc 数据结构对中断描述符的具体定义。(3) 中断处理中硬件负责完成的工作中断服务例程包括具体负责处理中断(异常)的代码是操作系统的重要组成部分。需要注意区别的是,有两个过程由硬件完成:硬件中断处理过程 1(起始):从 CPU 收到中断事件后,打断当前程序或任务的执行,根据某种机制跳转到中断服务例程去执行的过程。其具体流程如下: - CPU 在执行完当前程序的每一条指令后,都会去确认在执行刚才的指令过程中中断控制器(如:8259A)是否发送中断请求过来,如果有那么 CPU 就会在相应的时钟脉冲到来时从总线上读取中断请求对应的中断向量; - CPU 根据得到的中断向量(以此为索引)到 IDT 中找到该向量对应的中断描述符,中断描述符里保存着中断服务例程的段选择子; - CPU 使用 IDT 查到的中断服务例程的段选择子从 GDT 中取得相应的段描述符,段描述符里保存了中断服务例程的段基址和属性信息,此时 CPU 就得到了中断服务例程的起始地址,并跳转到该地址; - CPU 会根据 CPL 和中断服务例程的段描述符的 DPL 信息确认是否发生了特权级的转换。比如当前程序正运行在用户态,而中断程序是运行在内核态的,则意味着发生了特权级的转换,这时 CPU 会从当前程序的 TSS 信息(该信息在内存中的起始地址存在 TR 寄存器中)里取得该程序的内核栈地址,即包括内核态的 ss 和 esp 的值,并立即将系统当前使用的栈切换成新的内核栈。这个栈就是即将运行的中断服务程序要使用的栈。紧接着就将当前程序使用的用户态的 ss 和 esp 压到新的内核栈中保存起来; - CPU 需要开始保存当前被打断的程序的现场(即一些寄存器的值),以便于将来恢复被打断的程序继续执行。这需要利用内核栈来保存相关现场信息,即依次压入当前被打断程序使用的 eflags,cs,eip,errorCode(如果是有错误码的异常)信息; - CPU 利用中断服务例程的段描述符将其第一条指令的地址加载到 cs 和 eip 寄存器中,开始执行中断服务例程。这意味着先前的程序被暂停执行,中断服务程序正式开始工作。硬件中断处理过程 2(结束):每个中断服务例程在有中断处理工作完成后需要通过 iret(或 iretd)指令恢复被打断的程序的执行。CPU 执行 IRET 指令的具体过程如下: - 程序执行这条 iret 指令时,首先会从内核栈里弹出先前保存的被打断的程序的现场信息,即 eflags,cs,eip 重新开始执行; - 如果存在特权级转换(从内核态转换到用户态),则还需要从内核栈中弹出用户态栈的 ss 和 esp,这样也意味着栈也被切换回原先使用的用户态的栈了; - 如果此次处理的是带有错误码(errorCode)的异常,CPU 在恢复先前程序的现场时,并不会弹出 errorCode。这一步需要通过软件完成,即要求相关的中断服务例程在调用 iret 返回之前添加出栈代码主动弹出 errorCode。下图显示了从中断向量到 GDT 中相应中断服务程序起始位置的定位方式: 图 10 中断向量与中断服务例程起始地址的关系(4) 中断产生后的堆栈栈变化下图显示了给出相同特权级和不同特权级情况下中断产生后的堆栈栈变化示意图: 图 11 相同特权级和不同特权级情况下中断产生后的堆栈栈变化示意图(5) 中断处理的特权级转换中断处理得特权级转换是通过门描述符(gate descriptor)和相关指令来完成的。一个门描述符就是一个系统类型的段描述符,一共有 4 个子类型:调用门描述符(call-gate descriptor),中断门描述符(interrupt-gate descriptor),陷阱门描述符(trap-gate descriptor)和任务门描述符(task-gate descriptor)。与中断处理相关的是中断门描述符和陷阱门描述符。这些门描述符被存储在中断描述符表(Interrupt Descriptor Table,简称 IDT)当中。CPU 把中断向量作为 IDT 表项的索引,用来指出当中断发生时使用哪一个门描述符来处理中断。中断门描述符和陷阱门描述符几乎是一样的。中断发生时实施特权检查的过程如下图所示:图 12 中断发生时实施特权检查的过程门中的 DPL 和段选择符一起控制着访问,同时,段选择符结合偏移量(Offset)指出了中断处理例程的入口点。内核一般在门描述符中填入内核代码段的段选择子。产生中断后,CPU 一定不会将运行控制从高特权环转向低特权环,特权级必须要么保持不变(当操作系统内核自己被中断的时候),或被提升(当用户态程序被中断的时候)。无论哪一种情况,作为结果的 CPL 必须等于目的代码段的 DPL。如果 CPL 发生了改变,一个堆栈切换操作(通过 TSS 完成)就会发生。如果中断是被用户态程序中的指令所触发的(比如软件执行 INT n 生产的中断),还会增加一个额外的检查:门的 DPL 必须具有与 CPL 相同或更低的特权。这就防止了用户代码随意触发中断。如果这些检查失败,会产生一个一般保护异常(general-protection exception)。3. lab1 中对中断的处理实现(1) 外设基本初始化设置Lab1 实现了中断初始化和对键盘、串口、时钟外设进行中断处理。串口的初始化函数 serial_init(位于/kern/driver/console.c)中涉及中断初始化工作的很简单:...... // 使能串口1接收字符后产生中断 outb(COM1 + COM_IER, COM_IER_RDI); ...... // 通过中断控制器使能串口1中断 pic_enable(IRQ_COM1);键盘的初始化函数 kbd_init(位于 kern/driver/console.c 中)完成了对键盘的中断初始化工作,具体操作更加简单:...... // 通过中断控制器使能键盘输入中断 pic_enable(IRQ_KBD);时钟是一种有着特殊作用的外设,其作用并不仅仅是计时。在后续章节中将讲到,正是由于有了规律的时钟中断,才使得无论当前 CPU 运行在哪里,操作系统都可以在预先确定的时间点上获得 CPU 控制权。这样当一个应用程序运行了一定时间后,操作系统会通过时钟中断获得 CPU 控制权,并可把 CPU 资源让给更需要 CPU 的其他应用程序。时钟的初始化函数 clock_init(位于 kern/driver/clock.c 中)完成了对时钟控制器 8253 的初始化:...... //设置时钟每秒中断100次 outb(IO_TIMER1, TIMER_DIV(100) % 256); outb(IO_TIMER1, TIMER_DIV(100) / 256); // 通过中断控制器使能时钟中断 pic_enable(IRQ_TIMER);(2) 中断初始化设置操作系统如果要正确处理各种不同的中断事件,就需要安排应该由哪个中断服务例程负责处理特定的中断事件。系统将所有的中断事件统一进行了编号(0 ~ 255),这个编号称为中断向量。以 ucore 为例,操作系统内核启动以后,会通过 idt_init 函数初始化 idt 表 (参见 trap.c),而其中 vectors 中存储了中断处理程序的入口地址。vectors 定义在 vector.S 文件中,通过一个工具程序 vector.c 生成。其中仅有 System call 中断的权限为用户权限 (DPL_USER),即仅能够使用 int 0x80 指令。此外还有对 tickslock 的初始化,该锁用于处理时钟中断。vector.S 文件通过 vectors.c 自动生成,其中定义了每个中断的入口程序和入口地址 (保存在 vectors 数组中)。其中,中断可以分成两类:一类是压入错误编码的 (error code),另一类不压入错误编码。对于第二类, vector.S 自动压入一个 0。此外,还会压入相应中断的中断号。在压入两个必要的参数之后,中断处理函数跳转到统一的入口 alltraps 处。(3) 中断的处理过程trap 函数(定义在 trap.c 中)是对中断进行处理的过程,所有的中断在经过中断入口函数__alltraps 预处理后 (定义在 trapasm.S 中) ,都会跳转到这里。在处理过程中,根据不同的中断类型,进行相应的处理。在相应的处理过程结束以后,trap 将会返回,被中断的程序会继续运行。整个中断处理流程大致如下:trapasm.Strap.c1)产生中断后,CPU 跳转到相应的中断处理入口 (vectors),并在桟中压入相应的 error_code(是否存在与异常号相关) 以及 trap_no,然后跳转到 alltraps 函数入口:注意:此处的跳转是 jmp 过程在栈中保存当前被打断程序的 trapframe 结构(参见过程trapasm.S)。设置 kernel (内核) 的数据段寄存器,最后压入 esp,作为 trap 函数参数(struct trapframe * tf) 并跳转到中断处理函数 trap 处:注意:此时的跳转是 call 调用,会压入返回地址 eip,注意区分此处eip与trapframe中eip:trapframe的结构为:进入 trap 函数,对中断进行相应的处理:2)详细的中断分类以及处理流程如下:根据中断号对不同的中断进行处理。其中,若中断号是IRQ_OFFSET + IRQ_TIMER 为时钟中断,则把ticks 将增加一。若中断号是IRQ_OFFSET + IRQ_COM1 为串口中断,则显示收到的字符。若中断号是IRQ_OFFSET + IRQ_KBD 为键盘中断,则显示收到的字符。若为其他中断且产生在内核状态,则挂起系统;3)结束 trap 函数的执行后,通过 ret 指令返回到 alltraps 执行过程。从栈中恢复所有寄存器的值。调整 esp 的值:跳过栈中的 trap_no 与 error_code,使esp指向中断返回 eip,通过 iret 调用恢复 cs、eflag以及 eip,继续执行。(high)...产生中断时的 eip →eiperror_codeesp →trap_no(low)...Struct trapframe{uint edi;uint esi;uint ebp;…ushort es;ushort padding1;ushort ds;ushort padding2;uint trapno;uint err;uint eip;...}观察 trapframe 结构与中断产生过程的压桟顺序。需要明确 pushal 指令都保存了哪些寄存器,按照什么顺序?← trap_no← trap_error← 产生中断处的 eip图 13 ucore 中断处理流程至此,对整个 lab1 中的主要部分的背景知识和实现进行了阐述。请大家能够根据前面的练习要求完成所有的练习。
2. 实验环境准备安装 Git(1)在实验室页面右侧,单击单击 图标,切换至Web Terminal。补充:若Web Terminal界面没有显示,可以通过实验场景桌面的LX终端进入命令输入界面。执行如下命令切换连接本实验资源。(其中公网地址替换成本场景所提供的公网地址)。ssh root@ECS公网地址连接之后需要输入登录密码,注意密码的粘贴不会显示,粘贴后直接回车就能进入本实验环境。(2)执行以下命令进行 Git 的安装:yum install git -y(3)安装完成后,可以执行下方命令验证 Git 的版本:git --version如果 Git 的版本号可以成功展示,说明 Git 已经成功安装。安装 Node.js(1)在终端中,通过执行下方的命令,安装 Node.js:curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash - && sudo yum clean all && sudo yum makecache && sudo yum install -y gcc-c++ make && sudo yum install -y nodejs安装完成后,可以进行 node 和 npm 的版本信息验证。(2)验证 node 的版本信息:node -v(3)验证 npm 的版本信息:npm -v如果两者的版本信息都能成功展示,说明 Node.js 已经成功安装。安装 Hexo(1)在终端中,可以通过执行下方的命令,全局安装 Hexo 客户端:npm install -g hexo-cli(2)安装完成后,可以执行下方命令验证 Hexo 的版本及其依赖信息:hexo -v如果 Hexo 的版本及其依赖信息可以成功展示,说明 hexo 已经成功安装。
2. 登录对象存储OSS控制台本步骤指导您如何登录对象存储OSS控制台,并查看OSS Bucket。双击打开远程桌面的Chronmium网页浏览器。在RAM用户登录框中单击下一步,并复制粘贴页面左上角的子用户密码到用户密码输入框,单击登录。复制下方地址,在Chromium网页浏览器打开新页签,粘贴并访问对象存储OSS控制台。https://oss.console.aliyun.com/在左侧导航栏中,单击Bucket列表。在Bucket列表页面,您可以找到实验室分配的Bucket。说明:你可在云产品资源列表中查看云起实验室分配的对象存储OSS资源。在您的本机浏览器中打开新页签,复制下方地址到地址栏中,下载我们将要上传到OSS中的测试文件。说明:请您在本机浏览器中下载测试文件。https://labfileapp.oss-cn-hangzhou.aliyuncs.com/%E6%97%A
5. 基于阿里云OSS部署静态博客连接ECS服务器(1)在实验室页面右侧,切换至桌面界面。(2)双击打开虚拟桌面的Chromium浏览器。(3)在RAM用户登录框中单击下一步,并复制粘贴页面左上角的子用户密码到用户密码输入框,单击登录。2. 进入OSS控制台(1)复制下方地址,在Chromium网页浏览器打开新页签,粘贴并访问OSS控制台。https://oss.console.aliyun.com/(2)在对象存储OSS管理控制台左侧导航栏中,单击Bucket列表。(3)在Bucket列表页面,单击Bucket名称。说明:您可以在云产品资源列表中查看到您的OSS的Bucket名称。3. 修改存储桶配置(1)进入Bucket权限控制,将Bucket权限改为公共读写(2)进入Bucket文件管理,新增授权(3)授权用户给:所有账号,权限设置为读/写,访问方式设置为HTTP,点击确定。4. 将静态博客上传至阿里云OSS 中(1)在实验室页面右侧,单击单击 图标,切换至Web Terminal。(2)执行如下命令,编辑 _config.yml 文件vim _config.yml按shift加G跳转到文件末尾最后一行,按i键进入编辑模式,复制下方代码,粘贴并覆盖原 deploy 选项的代码:deploy: type: ali-oss region: yourRegion accessKeyId: yourSecretId accessKeySecret: yourSecretKey bucket: yourBucket其中,您的oss 区域代码,指的是阿里云Region 官方说明文档 https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.12.39972c20YS7sKQ中可以查看。 例如:我的Bucket概览中显示为华东2(上海)对应的值即:oss-cn-shanghaiaccessKeyId、accessKeySecret、bucket均可在云产品资源列表中查看注意:冒号一定要使用英文冒号,冒号前不能有空格,冒号后必须有一个空格,这是 yml 的固定写法。完成编辑后,按esc输入:wq,保存退出。5. 执行以下命令重新生成静态文件。hexo g -d
6. 创建文章创建文章 markdown 模板执行以下命令进入 blog 项目目录下,创建新文章cd ~/blog && hexo new first示例图如下:命令执行完毕后,会在 source/_posts 中会生成一个 markdown 文件,本条命令会生成一个“first.md”的文件。编写 markdown 文件(1)打开 first.md 文件,编写生成的文章模板:vi source/_posts/first.md进入编写界面后,自动生成的 markdown 文件如图所示:(2)在下方编辑自己想要编辑的内容,支持 markdown 语法,例子如图所示:按esc输入:wq,保存退出。(3)重新生成静态文件并部署至阿里云OSS执行命令,重新生成静态文件并部署至阿里云OSS :cd ~/blog && hexo g -d执行结果如图所示:
3. 利用图形化工具使用对象存储OSS在您的本机浏览器中,访问下方地址,根据您电脑的操作系统,下载对应的ossbrowser安装包。说明:因实验室环境不支持安装ossbrowser,请您使用本机的电脑进行安装ossbrowser。https://help.aliyun.com/document_detail/209974.html解压ossbrowser安装包,双击打开oss-browser.exe。说明:本实验以Windows系统为例,其他操作系统请参考安装并登录ossbrowser官网文档。在AK登录对话框中,填写实验室分配的AccessKeyId和AccessKeySecret,其他配置保持默认,单击登入。说明:本实验以通过AK登录ossbrowser方式为例,通过授权码方式登录ossbrowser请参考安装并登录ossbrowser官网文档。。找到实验室分配的Bucket,单击Bucket名称。说明:你可在云产品资源列表中查看云起实验室分配的对象存储OSS资源。实验室分配的子账号不支持创建和删除Bucket权限,若要体验创建和删除Bucket步骤,请您使用您自己的阿里云账号的AccessKeyId登录ossbrowser,并注意可能会产生费用问题。创建目录。5.1 在Bucket中,单击创建目录。5.2 在创建目录对话框中,输入目录名,例如test,单击确定。上传文件。6.1 在Bucket中,单击您刚刚创建的目录名,本实验以test为例。6.2 在test目录中,单击文件,6.3 在您的电脑中,找到测试数据test.log文件,并选择该文件,然后单击上传文件说明:文件路径以本地存储路径为准。返回如下结果,表示您已成功上传testlog.csv文件文件。查看文件。7.1 在test目录中,单击testlog.csv文件名称。7.2 在预览对话框中,单击尝试作为文本文件打开。返回如下结果,您可以预览testlog.csv文件的内容。下载文件。8.1 在test目录中,单击testlog.csv文件右侧操作列下的下载。8.2 选择您需要下载到的文件夹中,然后单击选择文件夹。返回如下结果,表示您已下载完成。分享文件。9.1 在test目录中,单击testlog.csv文件右侧操作列下的获取地址。9.2 在获取地址对话框中, 您可以通过复制、发送邮件和二维码三种方式分享testlog.csv给第三方预览或下载。删除文件。10.1 在test目录中,单击testlog.csv文件右侧操作列下的删除。10.2 在删除目录和文件对话框中,单击确定。返回如下结果,表示您已成功删除testlog.csv文件。
3. 利用图形化工具使用对象存储OSS在您的本机浏览器中,访问下方地址,根据您电脑的操作系统,下载对应的ossbrowser安装包。说明:因实验室环境不支持安装ossbrowser,请您使用本机的电脑进行安装ossbrowser。https://help.aliyun.com/document_detail/209974.html解压ossbrowser安装包,双击打开oss-browser.exe。说明:本实验以Windows系统为例,其他操作系统请参考安装并登录ossbrowser官网文档。在AK登录对话框中,填写实验室分配的AccessKeyId和AccessKeySecret,其他配置保持默认,单击登入。说明:本实验以通过AK登录ossbrowser方式为例,通过授权码方式登录ossbrowser请参考安装并登录ossbrowser官网文档。。找到实验室分配的Bucket,单击Bucket名称。说明:你可在云产品资源列表中查看云起实验室分配的对象存储OSS资源。实验室分配的子账号不支持创建和删除Bucket权限,若要体验创建和删除Bucket步骤,请您使用您自己的阿里云账号的AccessKeyId登录ossbrowser,并注意可能会产生费用问题。创建目录。5.1 在Bucket中,单击创建目录。5.2 在创建目录对话框中,输入目录名,例如test,单击确定。上传文件。6.1 在Bucket中,单击您刚刚创建的目录名,本实验以test为例。6.2 在test目录中,单击文件,6.3 在您的电脑中,找到测试数据test.log文件,并选择该文件,然后单击上传文件说明:文件路径以本地存储路径为准。返回如下结果,表示您已成功上传testlog.csv文件文件。查看文件。7.1 在test目录中,单击testlog.csv文件名称。7.2 在预览对话框中,单击尝试作为文本文件打开。返回如下结果,您可以预览testlog.csv文件的内容。下载文件。8.1 在test目录中,单击testlog.csv文件右侧操作列下的下载。8.2 选择您需要下载到的文件夹中,然后单击选择文件夹。返回如下结果,表示您已下载完成。分享文件。9.1 在test目录中,单击testlog.csv文件右侧操作列下的获取地址。9.2 在获取地址对话框中, 您可以通过复制、发送邮件和二维码三种方式分享testlog.csv给第三方预览或下载。删除文件。10.1 在test目录中,单击testlog.csv文件右侧操作列下的删除。10.2 在删除目录和文件对话框中,单击确定。返回如下结果,表示您已成功删除testlog.csv文件。
5. 数据可视化展现本步骤将指导您如何通过DataWorks的数据分析功能实现数据表rpt_user_info_d的可视化。编写SQL。1.1 在Chromium网页浏览器中,切换至数据开发页签。单击左上方的图标,选择全部产品>数据分析>SQL查询。1.2 在临时文件页签,输入如下SQL语句,单击 图标,查询rpt_user_info_d表数据情况。说明:SQL语句中字段dt的${bdp.system.bizdate}表示为业务日期。例如,任务运行的日期为20180717,则业务日期为20180716,即任务运行日期的前一天。select * from rpt_user_info_d where dt=${bdp.system.bizdate};1.3 在成本预估对话框中,单击运行。返回结果如下,您可查看到rpt_user_info_d表数据。修改字段类型。2.1 在结果区域,单击左侧的 图标。2.2 在待选字段列表中,找到pv字段,单击其右侧的 图标,选择修改类型>数值。2.3 在待选字段列表中,找到dt字段,单击其右侧的 图标,选择修改类型>日期。城市注册人数排行榜分析。3.1 在结果区域,图标类型选择条形图。3.2 在结果区域,将维度中的uid字段删除。3.3 在结果区域,将待选字段列表中的region字段拖入到维度中。3.4 在结果区域的维度中,选择region>设置字段信息。3.5 在设置字段信息对话框中,字段展示名输入为身份,单击确认。3.6 在结果区域的指标中,选择计数(uid)>设置字段信息。3.7 在设置字段信息对话框中,字段展示名输入为注册会员数,单击确认。3.8 在结果区域的指标中,选择聚合方式>去重计数。返回结果如下,您可查看到城市注册人数排行榜分析。注册会员访问次数年龄分布。4.1 在结果区域,图标类型选择饼图。4.2 在结果区域,将维度中的省份字段删除。4.3 在结果区域,将待选字段列表中的age_range字段拖入到维度中。4.4 在结果区域的维度中,选择age_range>设置字段信息。4.5 在设置字段信息对话框中,字段展示名输入为年龄区间,单击确认。4.6 在结果区域,将指标中的注册会员数字段删除。4.7 在结果区域,将待选字段列表中的pv字段拖入到指标中。4.8 在结果区域的指标中,选择求和(pv)>设置字段信息。4.9 在设置字段信息对话框中,字段展示名输入为访问次数,单击确认。4.10 在结果区域的指标中,选择访问次数>聚合方式>求和。返回结果如下,您可查看到注册会员访问次数年龄分布。注册会员性别访问次数分布。5.1 在结果区域,将维度中的年龄区间字段删除。5.2 在结果区域,将待选字段列表中的gender字段拖入到维度中。5.3 在结果区域的维度中,选择gender>设置字段信息。5.4 在设置字段信息对话框中,字段展示名输入为性别,单击确认。返回结果如下,您可查看到注册会员性别访问次数分布。男女星座访问次数分析。6.1 在结果区域,将待选字段列表中的zodiac字段拖入到维度中。6.2 在结果区域的维度中,选择zodiac>设置字段信息。6.3 在设置字段信息对话框中,字段展示名输入为星座,单击确认。6.4 在结果区域,图标类型选择柱状图。返回结果如下,您可查看到男女星座访问次数分析。
5. 卸载资源进行配置并提供服务只完成了一半的工作,完整的操作周期除了正向的安装,还包含了逆向的卸载。本步骤将指导您如何把配置的各种资源给卸载掉。说明:资源卸载我们就不使用yaml脚本了,直接使用kubectl命令就可以完成。执行如下命令,卸载ingress。kubectl delete ingress example-ingress -n app-ns执行如下命令,卸载service。kubectl delete service myapp-service -n app-ns执行如下命令,卸载deployment。kubectl delete deployment myapp-deployment -n app-ns至此我们就完成了卸载工作,现在我们需要确认卸载结果。依次执行如下命令,确认卸载结果。kubectl get deployment -n app-ns kubectl get service -n app-ns kubectl get ingress -n app-ns返回结果如下,表示卸载工作已完成。如果您的返回结果和如下截图不一样,并且您的返回结果中STATUS字段为Terminating,表示pod正在卸载中,请您稍等片刻,再次执行上述命令,确认卸载结果。
4. 配置ingress开放外部访问k8s是一个集群,deployment、service都是集群内部的资源,他们通过一个内部虚拟网络互相访问。但是对于外部的用户,这些所有的资源都是不可见的,所以我们还需要配置一个外部访问的入口到service的映射规则,从而将内部服务暴露出去。本步骤将指导您如何使用ingress的来实现服务对外暴露的需求。执行如下命令,创建ingress.yaml文件。vim ingress.yaml按i键进入编辑模式,将如下代码复制到文件中,定义ingress规则,然后按ECS退出编辑模式,输入:wq后按下Enter键保存并退出。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress namespace: app-ns spec: ingressClassName: nginx #default ingress class. optional rules: - http: paths: - backend: service: name: myapp-service port: number: 80 path: / pathType: Prefix执行如下命令,运行ingress.yaml文件,部署ingress。kubectl apply -f ~/ingress.yaml执行如下命令,查看部署的ingress。kubectl get ingress -n app-ns返回结果如下,您可查看到访问地址的IP,以及端口是80。如果您的返回结果中ADDRESS为空,表示ingress正在部署,请您稍等片刻,再次执行上述命令。在您的本机浏览器中,打开新页签,在地址栏中访问http://ADDRESS:80。说明:您需要将ADDRESS改为上一步骤的命令返回结果中的ADDRESS。返回如下页面,您即可开始使用部署的魔方应用。
3. 部署服务由于deployment是一个弹性组件,其管理的应用实例不是固定的,而是可以任意伸缩。这带来了很多的好处,例如可以支持弹性伸缩、滚动更新等等。但是相反的,这也会导致应用实例IP不固定,从访问者的角度我们不可能每次去查找当前的应用实例。所以,为了能提供稳定的访问入口,我们还需要部署服务来接收请求,并屏蔽内部的弹性机制。本步骤将指导您如何部署服务。执行如下命令,创建service.yaml文件。vim service.yaml按i键进入编辑模式,将如下代码复制到文件中,然后按ECS退出编辑模式,输入:wq后按下Enter键保存并退出。参数说明:selector:这是一个选择器,通过name=myapp这个条件来选择需要代理的Pod。ports:这里定义了服务自身暴露的端口和需要访问的应用的端口。apiVersion: v1 kind: Service metadata: name: myapp-service namespace: app-ns spec: ports: - port: 80 targetPort: 80 protocol: TCP type: ClusterIP selector: name: myapp执行如下命令,运行service.yaml文件,部署服务service。kubectl apply -f ~/service.yaml执行如下命令,查看部署的服务service。kubectl get service -n app-ns返回结果如下,您可查看到服务service的相关信息。这里我们就完成了服务的部署。下面我们通过ingress将内部的服务暴露出去。
2. 部署业务应用执行如下命令,创建名为app-ns的命名空间。说明:通常创建应用需要确定部署在k8s的哪个namespace中,一般不建议部署在default namespace下,索引我们创建名为app-ns的命名空间。kubectl create ns app-ns执行如下命令,创建deploy.yaml文件。说明:现在我们需要将业务应用部署到k8s集群中,这里我们已经准备好了一个应用并打包成镜像,镜像地址如下:仓库地址:registry.cn-hangzhou.aliyuncs.com/acr-toolkit/ack-cube。版本: 1.0。这是一个示例性质的魔方游戏应用。这个应用会监听80端口,接受根路径的访问,返回一个简单的魔方游戏界面。要让这个web应用的镜像在k8s中运行,我们首先要定义一个 deployment资源然后,通过创建deploy.yaml来描述deployment资源。vim deploy.yaml按i键进入编辑模式,将如下代码复制到文件中,然后按ECS退出编辑模式,输入:wq后按下Enter键保存并退出。参数说明:image:就是这个web应用的镜像地址。replicas:代表这个应用只部署一份。apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deployment namespace: app-ns labels: app: myapp spec: replicas: 1 selector: matchLabels: name: myapp template: metadata: labels: name: myapp namespace: app-ns spec: containers: - name: ack-kube image: registry.cn-hangzhou.aliyuncs.com/acr-toolkit/ack-cube:1.0 ports: - containerPort: 80 resources: requests: cpu: 0.25 memory: 512Mi limits: cpu: 1 memory: 1024Mi执行如下命令,运行deploy.yaml文件,部署应用。kubectl apply -f ~/deploy.yaml执行如下命令,查看部署的POD。kubectl get pod -n app-ns返回结果如下,当您看到status字段为Running时,代表应用部署完成,处于运行中的状态。如果您看到的是ContainerCreating,代表服务容器正在创建中,需要您耐心等待应用创建完成。至此,我们已经完成了应用本身的部署,下面我们看下如何在k8s里配置服务。
4. 加工数据设计业务流程双击新建的业务流程,打开业务流程编辑页面。鼠标单击EMR Hive并拖拽至右侧的编辑页面。在新建节点对话框中,输入节点名称,单击提交。此处需要新建3个EMR Hive节点,依次命名为ods_log_info_d、dw_user_info_d和ads_user_info_d,并配置如下图所示的依赖关系。业务流程节点间依赖关系的配置请参见步骤三中的新建业务流程。2. 创建udf函数在实验环境的chrome浏览器输入下方链接,下载ip2region-emr.jar存放至本地https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/49126/cn_zh/1576723860673/ip2region-emr.jar2. 新建资源2.1 在DW中新建资源,类型选择EMR jar2.2 存储路径选择准备工作中新建的OSS目录ip2Region,上传刚刚下载的资源,点击新建。2.3 点击工具栏的提交按钮,将资源提交到生产环境。3. 新建函数3.1 DW中新建函数3.2 弹窗中输入函数名字为getregion后,点击新建按钮3.3 在弹出的页面上填写以下信息参数描述EMR数据库下拉选择步骤三创建的数据库或default数据库所属资源选择刚刚新建的资源,ip2region-emr.jar类名org.alidata.emr.udf.Ip2Region函数类型其它函数注意:EMR数据库也可选择default;只需在配置EMR Hive节点的ods_log_info_d节点时,在节点代码中的getregion前加上前缀"default."。3.4 单击工具栏的提交按钮,将udf函数提交到生产环境。3. 配置EMR Hive节点配置ods_log_info_d节点。双击ods_log_info_d节点,进入节点配置页面。在节点编辑页面,编写如下语句。说明:如果您的工作空间绑定多个EMR引擎,需要选择EMR引擎。如果仅绑定一个EMR引擎,则无需选择。如果新建函数时,EMR数据库选择了default,则需要在下面代码中的getregion前加上前缀"default."。use workshopDB; --创建ODS层表 CREATE TABLE IF NOT EXISTS ods_log_info_d ( ip STRING COMMENT 'ip地址', uid STRING COMMENT '用户ID', `time` STRING COMMENT '时间yyyymmddhh:mi:ss', status STRING COMMENT '服务器返回状态码', bytes STRING COMMENT '返回给客户端的字节数', region STRING COMMENT '地域,根据ip得到', method STRING COMMENT 'http请求类型', url STRING COMMENT 'url', protocol STRING COMMENT 'http协议版本号', referer STRING COMMENT '来源url', device STRING COMMENT '终端类型 ', identity STRING COMMENT '访问类型 crawler feed user unknown' ) PARTITIONED BY ( dt STRING ); ALTER TABLE ods_log_info_d ADD IF NOT EXISTS PARTITION (dt=${bizdate}); set hive.vectorized.execution.enabled = false; INSERT OVERWRITE TABLE ods_log_info_d PARTITION (dt=${bizdate}) SELECT ip , uid , tm , status , bytes , getregion(ip) AS region --使用自定义UDF通过ip得到地域。 , regexp_extract(request, '(^[^ ]+) .*') AS method --通过正则把request差分为三个字段。 , regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') AS url , regexp_extract(request, '.* ([^ ]+$)') AS protocol , regexp_extract(referer, '^[^/]+://([^/]+){1}') AS referer --通过正则清洗refer,得到更精准的url。 , CASE WHEN lower(agent) RLIKE 'android' THEN 'android' --通过agent得到终端信息和访问形式。 WHEN lower(agent) RLIKE 'iphone' THEN 'iphone' WHEN lower(agent) RLIKE 'ipad' THEN 'ipad' WHEN lower(agent) RLIKE 'macintosh' THEN 'macintosh' WHEN lower(agent) RLIKE 'windows phone' THEN 'windows_phone' WHEN lower(agent) RLIKE 'windows' THEN 'windows_pc' ELSE 'unknown' END AS device , CASE WHEN lower(agent) RLIKE '(bot|spider|crawler|slurp)' THEN 'crawler' WHEN lower(agent) RLIKE 'feed' OR regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') RLIKE 'feed' THEN 'feed' WHEN lower(agent) NOT RLIKE '(bot|spider|crawler|feed|slurp)' AND agent RLIKE '^[Mozilla|Opera]' AND regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') NOT RLIKE 'feed' THEN 'user' ELSE 'unknown' END AS identity ----------- 数据清洗,字段映射 FROM ( SELECT SPLIT(col, '##@@')[0] AS ip , SPLIT(col, '##@@')[1] AS uid , SPLIT(col, '##@@')[2] AS tm , SPLIT(col, '##@@')[3] AS request , SPLIT(col, '##@@')[4] AS status , SPLIT(col, '##@@')[5] AS bytes , SPLIT(col, '##@@')[6] AS referer , SPLIT(col, '##@@')[7] AS agent FROM ods_raw_log_d ----------- 原始日志表中字段提取 WHERE dt = ${bizdate} ) a;单击工具栏中的。2. 配置dw_user_info_d节点。双击dw_user_info_d节点,进入节点配置页面。在节点编辑页面,编写如下语句。说明:如果您的工作空间绑定多个EMR引擎,需要选择EMR引擎。如果仅绑定一个EMR引擎,则无需选择。use workshopDB; --创建DW层表 CREATE TABLE IF NOT EXISTS dw_user_info_d ( uid STRING COMMENT '用户ID', gender STRING COMMENT '性别', age_range STRING COMMENT '年龄段', zodiac STRING COMMENT '星座', region STRING COMMENT '地域,根据ip得到', device STRING COMMENT '终端类型 ', identity STRING COMMENT '访问类型 crawler feed user unknown', method STRING COMMENT 'http请求类型', url STRING COMMENT 'url', referer STRING COMMENT '来源url', `time` STRING COMMENT '时间yyyymmddhh:mi:ss' ) PARTITIONED BY ( dt STRING ); ALTER TABLE dw_user_info_d ADD IF NOT EXISTS PARTITION (dt = ${bizdate}); INSERT OVERWRITE TABLE dw_user_info_d PARTITION (dt=${bizdate}) SELECT COALESCE(a.uid, b.uid) AS uid , b.gender , b.age_range , b.zodiac , a.region , a.device , a.identity , a.method , a.url , a.referer , a.`time` FROM ( SELECT * FROM ods_log_info_d WHERE dt = ${bizdate} ) a LEFT OUTER JOIN ( SELECT * FROM ods_user_info_d WHERE dt = ${bizdate} ) b ON a.uid = b.uid;单击工具栏中的。3. 配置ads_user_info_d节点。双击ads_user_info_d节点,进入节点配置页面。``在节点编辑页面,编写如下语句。说明:如果您的工作空间绑定多个EMR引擎,需要选择EMR引擎。如果仅绑定一个EMR引擎,则无需选择。use workshopDB; --创建ads层表 CREATE TABLE IF NOT EXISTS ads_user_info_d ( uid STRING COMMENT '用户ID', region STRING COMMENT '地域,根据ip得到', device STRING COMMENT '终端类型 ', pv BIGINT COMMENT 'pv', gender STRING COMMENT '性别', age_range STRING COMMENT '年龄段', zodiac STRING COMMENT '星座' ) PARTITIONED BY ( dt STRING ); ALTER TABLE ads_user_info_d ADD IF NOT EXISTS PARTITION (dt=${bizdate}); INSERT OVERWRITE TABLE ads_user_info_d PARTITION (dt=${bizdate}) SELECT uid , MAX(region) , MAX(device) , COUNT(0) AS pv , MAX(gender) , MAX(age_range) , MAX(zodiac) FROM dw_user_info_d WHERE dt = ${bizdate} GROUP BY uid;单击工具栏中的。4. 提交业务流程给每个hive节点,新增日期参数。单击右侧的调度配置,在面板的参数位置,添加bizdate参数配置。在业务流程的编辑页面,单击,运行业务流程。待业务流程中的所有节点后出现,单击,提交运行成功的业务流程。选择提交对话框中需要提交的节点,此处全选即可。勾选忽略输入输出不一致的告警。单击确认。提交成功后,会出现以下提示。s5. 在生产环境运行任务任务发布成功后,单击页面上方的运维中心,进入运维中心页面。单击左侧导航栏中的周期任务运维 > 周期任务,进入周期任务页面,单击workstart虚节点。在右侧的DAG图中,右键单击workstart节点,选择补数据 > 当前节点及下游节点。输入业务日期(选择今天即可),勾选需要补数据的任务(全选即可),单击确定,自动跳转至补数据实例页面。单击刷新,直至SQL任务全部运行成功即可产出数据。恭喜您!到这里,您已经成功使用了EMR + DLF + OSS-HDFS + DataWorks这个产品组合。给自己点个赞吧~如果你想体验更多产品能力,您可以继续进行实验,完成用户画像分析。
准备实验环境1.配置独享资源组的DataWorks工作空间本步骤将完成独享调度资源组和独享数据集成资源组的DataWorks工作空间配置。双击打开远程桌面的Chromium网页浏览器。在RAM用户登录框中单击下一步,并复制粘贴页面左上角的子用户密码到用户密码输入框,单击登录。复制下方地址,在Chromium网页浏览器打开新页签,粘贴并访问DataWorks管控台。https://workbench.data.aliyun.com/?#/resourcelist?region=cn-shanghai&tab=exclusive在左侧导航栏中,单击资源组列表,分别对调度资源组和数据集成资源组,单击修改归属工作空间。说明:您可在云资源产品列表中查看调度资源组和数据集成资源组的名称。分别在调度资源组和数据集成资源组的修改归属工作空间对话框中,选择实验分配的DataWorks工作空间,单击绑定。注意:由于实验环境有限制性,因此您只能进行“将资源组绑定到自己的工作空间”这一操作,不能修改其它配置,否则会导致后续实验无法进行!2. 在DataWorks绑定EMR集群在导航栏中,单击工作空间列表。在工作空间列表页面,找到您的DataWorks工作空间,在其右侧的操作列下单击工作空间配置。在配置页面的计算引擎信息区域,选择E-MapReduce,单击增加实例。在新增E-MapReduce中,参考如下配置相关信息。配置完成之后在资源组初始化区域,单击初始化,进行独享调度资源组与EMR集群的打通。参数说明:参数描述实例显示名称自定义实例显示名称。访问模式选择快捷模式。集群ID选择实验室分配的EMR集群。如果网络连通性失败,不要担心,单击查看原因,进行网络连通性诊断;诊断完成后,然后重试即可。初始化完成后,点击确定按钮,绑定该引擎实例。3. 为oss bucket开通HDFS服务并新建目录备用复制下方地址,在Chromium网页浏览器打开新页签,粘贴并访问对象存储OSS管控台。https://oss.console.aliyun.com/bucket在左侧导航栏中,单击Bucket列表。进入以实验室子账号为名的Bucket,选择进入oss bucket的数据湖管理下的HDFS服务,单击开通HDFS服务。进入文件管理下的文件列表,单击新建目录。填写目录名(实例为ip2region),用于存放任务运行所需的jar包资源。创建成功后,会在文件列表中展示名为ip2region的目录,后续实验所需的jar包会存在该oss bucket。
3. 加工数据您本步骤将指导您如何通过DataWorks计算和分析已采集的数据。新建三张数据表,分别为数据运营层表(ods_log_info_d)、数据仓库层表(dw_user_info_all_d)和数据产品层表(rpt_user_info_d)。1.1 在临时查询页面的左侧导航中,单击数据开发。1.2 在数据开发页面,选择业务流程>MaxCompute,右键单击表,单击新建表。1.3 在新建表对话框中,表名输入为ods_log_info_d,单击新建。1.4 在表ods_log_info_d的编辑页面上方,单击DDL。1.5 在DDL模式对话框中,输入如下创建数据运营层表的建表语句,单击生成表结构。CREATE TABLE IF NOT EXISTS ods_log_info_d ( ip STRING COMMENT 'ip', uid STRING COMMENT 'uid', time STRING COMMENT 'timeyyyymmddhh:mi:ss', status STRING COMMENT 'status', bytes STRING COMMENT 'bytes', region STRING COMMENT 'region', method STRING COMMENT 'method', url STRING COMMENT 'url', protocol STRING COMMENT 'protocol', referer STRING COMMENT 'referer', device STRING COMMENT 'device', identity STRING COMMENT 'identity' ) PARTITIONED BY ( dt STRING );1.6 在确认操作对话框中,单击确认。1.7 在表ods_log_info_d的编辑页面,中文名输入为数据运营层表,单击提交到生产环境。1.8 在提交到生产环境对话框中,单击确认。1.9 重复1.2~1.8的步骤,根据如下两个建表语句,新建dw_user_info_all_d表和rpt_user_info_d表,中文名分别输入为数据仓库层表和数据产品层表,然后单击提交到生产环境。CREATE TABLE IF NOT EXISTS dw_user_info_all_d ( uid STRING COMMENT 'uid', gender STRING COMMENT 'gender', age_range STRING COMMENT 'age_range', zodiac STRING COMMENT 'zodiac', region STRING COMMENT 'region', device STRING COMMENT 'device', identity STRING COMMENT 'identity', method STRING COMMENT 'method', url STRING COMMENT 'url', referer STRING COMMENT 'referer', time STRING COMMENT 'timeyyyymmddhh:mi:ss' ) PARTITIONED BY ( dt STRING );CREATE TABLE IF NOT EXISTS rpt_user_info_d ( uid STRING COMMENT 'uid', region STRING COMMENT 'uid', device STRING COMMENT 'device', pv BIGINT COMMENT 'pv', gender STRING COMMENT 'gender', age_range STRING COMMENT 'age_range', zodiac STRING COMMENT 'zodiac' ) PARTITIONED BY ( dt STRING );设计业务流程。2.1 在数据开发页面左侧,双击您的业务流程。2.2 在业务流程开发面板,单击ODPS SQL并拖拽至右侧的编辑页面。2.3 在新建节点对话框中,节点名称输入为ods_log_info_d,单击提交。2.4 在业务流程开发面板,单击ODPS SQL并拖拽至右侧的编辑页面。2.5 在新建节点对话框中,节点名称输入为dw_user_info_all_d,单击提交。2.6 在业务流程开发面板,单击ODPS SQL并拖拽至右侧的编辑页面。2.7 在新建节点对话框中,节点名称输入为rpt_user_info_d,单击提交。2.8 在右侧的编辑页面,通过拖拽连线,配置如下图所示的依赖关系。创建用户自定义函数。3.1 复制下方地址,在Chromium浏览器打开新页签,粘贴并访问,下载ip2region.jar。https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/85298/cn_zh/1532163718650/ip2region.jar?spm=a2c4g.11186623.0.0.43df4d0dwSRLzd&file=ip2region.jar3.2 切换回数据开发页面,选择业务流程>您的业务流程>MaxCompute,右键单击资源,选择新建资源>JAR。3.3 在新建资源对话框中,单击点击上传。3.4 在打开文件面板中,单击下载目录,选择ip2region.jar,单击选择。3.5 在新建资源对话框中,单击新建。3.6 在上方工具栏中,单击图标提交。3.7 在提交新版本对话框中,单击提交。3.8 在数据开发页面,选择业务流程>您的业务流程>MaxCompute,右键单击函数,单击新建函数。3.9 在新建函数对话框中,函数名称输入为getregion,单击新建。3.10 在注册函数页签,配置如下参数,其他配置保持默认,单击 图标保存。参数说明:类名:输入org.alidata.odps.udf.Ip2Region。资源列表:输入ip2region.jar。描述:输入IP地址转换地域。命令格式:输入getregion(‘ip’)。参数说明:输入IP地址。3.11在注册函数页签,单击 图标提交。3.12 在提交新版本对话框中,单击确定。配置ODPS SQL节点。4.1 在数据开发页面,选择业务流程>您的业务流程>MaxCompute>数据开发,双击ods_log_info_d。4.2 在ods_log_info_d节点编辑页面,输入如下SQL语句,单击 图标保存。INSERT OVERWRITE TABLE ods_log_info_d PARTITION (dt=${bdp.system.bizdate}) SELECT ip , uid , time , status , bytes , getregion(ip) AS region --使用自定义UDF通过IP得到地域。 , regexp_substr(request, '(^[^ ]+ )') AS method --通过正则把request差分为3个字段。 , regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') AS url , regexp_substr(request, '([^ ]+$)') AS protocol , regexp_extract(referer, '^[^/]+://([^/]+){1}') AS referer --通过正则清晰refer,得到更精准的URL。 , CASE WHEN TOLOWER(agent) RLIKE 'android' THEN 'android' --通过agent得到终端信息和访问形式。 WHEN TOLOWER(agent) RLIKE 'iphone' THEN 'iphone' WHEN TOLOWER(agent) RLIKE 'ipad' THEN 'ipad' WHEN TOLOWER(agent) RLIKE 'macintosh' THEN 'macintosh' WHEN TOLOWER(agent) RLIKE 'windows phone' THEN 'windows_phone' WHEN TOLOWER(agent) RLIKE 'windows' THEN 'windows_pc' ELSE 'unknown' END AS device , CASE WHEN TOLOWER(agent) RLIKE '(bot|spider|crawler|slurp)' THEN 'crawler' WHEN TOLOWER(agent) RLIKE 'feed' OR regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') RLIKE 'feed' THEN 'feed' WHEN TOLOWER(agent) NOT RLIKE '(bot|spider|crawler|feed|slurp)' AND agent RLIKE '^[Mozilla|Opera]' AND regexp_extract(request, '^[^ ]+ (.*) [^ ]+$') NOT RLIKE 'feed' THEN 'user' ELSE 'unknown' END AS identity FROM ( SELECT SPLIT(col, '##@@')[0] AS ip , SPLIT(col, '##@@')[1] AS uid , SPLIT(col, '##@@')[2] AS time , SPLIT(col, '##@@')[3] AS request , SPLIT(col, '##@@')[4] AS status , SPLIT(col, '##@@')[5] AS bytes , SPLIT(col, '##@@')[6] AS referer , SPLIT(col, '##@@')[7] AS agent FROM ods_raw_log_d WHERE dt = ${bdp.system.bizdate} ) a;4.3 在数据开发页面,选择业务流程>您的业务流程>MaxCompute>数据开发,双击dw_user_info_all_d。4.4 在dw_user_info_all_d节点的编辑页面,输入如下SQL语句,单击 图标保存。INSERT OVERWRITE TABLE dw_user_info_all_d PARTITION (dt='${bdp.system.bizdate}') SELECT COALESCE(a.uid, b.uid) AS uid , b.gender , b.age_range , b.zodiac , a.region , a.device , a.identity , a.method , a.url , a.referer , a.time FROM ( SELECT * FROM ods_log_info_d WHERE dt = ${bdp.system.bizdate} ) a LEFT OUTER JOIN ( SELECT * FROM ods_user_info_d WHERE dt = ${bdp.system.bizdate} ) b ON a.uid = b.uid;4.5 在数据开发页面,选择业务流程>您的业务流程>MaxCompute>数据开发,双击rpt_user_info_d。4.6 在rpt_user_info_d节点的编辑页面,输入如下SQL语句,单击 图标保存。INSERT OVERWRITE TABLE rpt_user_info_d PARTITION (dt='${bdp.system.bizdate}') SELECT uid , MAX(region) , MAX(device) , COUNT(0) AS pv , MAX(gender) , MAX(age_range) , MAX(zodiac) FROM dw_user_info_all_d WHERE dt = ${bdp.system.bizdate} GROUP BY uid;提交业务流程。5.1 在数据开发页面左侧,双击您的业务流程。5.2 在业务流程的编辑页面上方菜单栏中,单击 提交图标,提交业务流程中已配置完成的节点。5.3 在提交对话框中,选择所有的节点,备注输入提交业务流程,选择忽略输入输出不一致的告警,单击提交。返回如下页面, 等待所有节点提交成功,单击 图标关闭即可。运行业务流程。6.1 在业务流程的编辑页面上方菜单栏中,单击 图标运行。返回如下页面,等待3~5分钟,所有节点显示为 后,表示任务运行完成。6.2 在左侧导航栏中,单击临时查询。6.3 在临时查询面板,右键单击临时查询,选择新建节点>ODPS SQL。6.4 在新建节点对话框中,单击提交。6.5 在SQL查询页签,输入如下SQL语句,单击 图标运行,查看rpt_user_info_d数据情况,确认数据产出。说明 :SQL语句中字段dt中的${bdp.system.bizdate}表示业务日期。例如,任务运行的日期为20180717,则业务日期为20180716,即任务运行日期的前一天。select * from rpt_user_info_d where dt=${bdp.system.bizdate} limit 10;6.6 在参数对话框中,单击确定。6.7 在MaxCompute计算成本估计对话框中,单击运行。返回如下结果,表示数据已产出。在生产环境运行任务。7.1在页面上方,单击运维中心。7.2 在运维中心页面,选择周期任务运维>周期任务。7.3 在周期任务页面,单击workshop_start业务流程。7.4 在DAG图中,右键单击workshop_start节点,选择补数据>当前节点及下游节点。7.5 在补数据对话框中,选择所有任务,单击确定。7.6 在确认框对话框中,单击确认。返回如下页面,单击刷新,等待3~5分钟,直至所有任务运行成功即可。
2. 连接ECS服务器本步骤将指导您如何使用Workbench远程连接ECS服务器。双击打开虚拟桌面的Chromium浏览器。在RAM用户登录框中单击下一步,并复制粘贴页面左上角的子用户密码到用户密码输入框,单击登录。复制下方地址,在Chromium浏览器打开新页签,粘贴并访问云服务器ECS控制台。https://ecs.console.aliyun.com在左侧导航栏中,选择实例与镜像>实例。在实例页面顶部,选择资源所在地域。例如下图中,地域切换为华东2(上海)。在实例页面,找到您的ECS实例,单击操作列下的远程连接。说明:您可在云产品资源列表中查看目标实例的实例ID。在连接与命令对话框的通过Workbench远程连接区域中,单击立即登录。在登录实例对话框中,输入ECS服务器的密码,单击确定。说明:您可以在云产品资源列表中查看到您的ECS服务器密码。返回如下页面,表示您已经成功远程连接到ECS服务器。
3. 创建在线教育网站界面本步骤将指导您创建HTML,创建完成后,通过浏览器访问相应地址,可看到在线教育网站。在ECS命令行界面,复制并执行以下命令,将在根目录下创建新目录。mkdir /alidata/www/default/edu执行命令后,将返回如下界面。复制并执行以下命令,命令行将跳转到如下界面,在此可编辑index.html文件。vim /alidata/www/default/edu/index.html按下 i 键进入编辑模式。将如下代码复制并粘贴:<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>在线教育</title> <meta name="keywords" content="在线教育"> <meta name="description" content="在线教育"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <!--Favicon--> <link rel="shortcut icon" href="" title="Favicon"> <style> @media all and (orientation : portrait) { /*竖屏*/ .mypic { width: 80%; } } @media all and (orientation : landscape) { /*横屏*/ .mypic { width: 40%; } } a { color: #000000; } a:link { color: #000000; } a:visited { color: #000000; } a:hover { color: #000000; } a:active { color: #000000; } </style> </head> <body style=" font-family: 'Microsoft JhengHei UI';"> <div id="wrapper" style=" margin: 0 15px; padding: 15px 0; position: relative;"> <div style="padding:20px;"> <p><font size="10" color="green">在线教育网</font></p> <p><font size="3" >您贴身的教育管家</font></p> </div> <p style="text-align:center"><font size="6" color="green">您可在此获取到丰富的教学视频,充分满足学习要求</font></p> <div style="padding:50px;"> <h1 style="text-align:center">精选课程</h1> <p style="text-align:center">口碑好,效益佳</p> <div> <table style="float: center;"> <tbody> <tr> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>实战视频1</p> <p>您可以在此视频学习相关内容1</p> </div> </center> </b> </td> <td> <b></b> <center><b> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>实战视频2</p> <p>您可以在此视频学习相关内容2</p> </div> </b></center> </td> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>实战视频3</p> <p>您可以在此视频学习相关内容3</p> </div> </center> </b> </td> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>实战视频4</p> <p>您可以在此视频学习相关内容4</p> </div> </center> </b> </td> </tr> </tbody> </table> </div> <h1 style="text-align:center">观看金牌讲师直播</h1> <p style="text-align:center">所有导师来自名企,有多年教学经验</p> <div> <table style="float: center;"> <tbody> <tr> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>张佳佳</p> <p>擅长教学产品知识</p> </div> </center> </b> </td> <td> <b></b> <center><b> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>李文</p> <p>多年web开发经验</p> </div> </b></center> </td> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>王杰</p> <p>专攻大数据计算</p> </div> </center> </b> </td> <td> <b> <center> <div class="video"> <video width="320" height="240" controls> <source src="movie.mp4" type="video/mp4"> </video> <p>刘子雯</p> <p>运营专家</p> </div> </center> </b> </td> </tr> </tbody> </table> </div> <p style=" margin: 0 auto; text-align: center; "> <img class="mypic" src="" style=" vertical-align: middle; text-align: center;"> </p> <p> 更多: <a style=" font-size:14px; font-family: 'Microsoft JhengHei UI'" href="aliyun.com" target="_blank">aliyun.com</a> </p> <p>电话号码:xxxxx;</p> </div> </div> <div style="text-align: center; margin: 0px; width: 100%; font-size: 14px; font-family: 'Microsoft JhengHei UI';"> @ <a href="https://beian.miit.gov.cn/" target="_blank" style="text-decoration:none">京ICP备15047403号-1</a> </div> </body> </html>粘贴后将出现以下对话框,点击确定。确定后出现如下界面,此时按下 Esc 键,并输入 :wq 后按下Enter键保存并退出。以上操作后,将返回命令行界面。在浏览器打开新页签,在地址栏输入http:///edu,访问在线教育网站。创建的网站如图所示:说明:ECS公网地址可在云产品资源列表查看。
问题描述系统部署后遇到这个问题,因该环境提供的服务器配置偏低导出出现 java.lang.OutOfMemoryError: unable to create new native thread异常问题分析问题本质原因是项目创建了线程,而系统能创建的线程数是有限制的,导致了异常的发生。能创建的线程数的具体计算公式如下:(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads当创建线程时虚拟机会在JVM内存创建线程对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。由公式得出结论:你给JVM内存越多,那么你能创建的线程越少,越容易发生 java.lang.OutOfMemoryError: unable to create new native thread问题解决修改linux 最大进程数1.查看用户打开到最大进程数ulimit -amax user processes (-u) #系统限制某用户下最多可以运行多少进程过线程2.修改进程数1.在/etc/security/limits.conf 文件里添加如下内容* soft nproc 65535 #打开进程数* hard nproc 65535 #打开进程数操作方法:echo "* soft nproc 65535" >> /etc/security/limits.confecho "* hard nproc 65535" >> /etc/security/limits.conf在 /etc/sysctl.conf 中添加 kernel.pid_max = 65535# vim /etc/sysctl.confkernel.pid_max = 65535或者echo "kernel.pid_max = 65535" >> /etc/sysctl.conf然后重启机器。保存退出,输入以下命令执行使其生效 其他问题排查1.如果程序中有bug,导致创建大量不需要的线程或者线程没有及时回收,那么必须解决这个bug,修改参数是不能解决问题的。2.如果程序确实需要大量的线程,现有的设置不能达到要求,那么可以通过修改MaxProcessMemory,JVMMemory,ThreadStackSize这三个因素,来增加能创建的线程数:MaxProcessMemory 使用64位操作系统VMMemory 减少 JVMMemory 的分配ThreadStackSize 减小单个线程的栈大小3.优化代码里面的线程池的线程配置,减少 JVMMemory 的分配思考问题具体的排查方法1:通过堆栈信息,确定是否存在创建线程过多的问题创建了多少线程?过度创建的线程的堆栈信息是什么?谁创建了这些线程?一旦了解清楚,就能很快解决OOM的问题2:增加操作系统的线程数通过ulimit -a查看操作系统的最大线程数,这里假设为1024如果应用程序所需的线程数超过1024,可以通过增大操作系统的线程数解决问题3:增加机器内存如果发现操作系统的线程数并未达到上限,说明应用程序需要更多的内存来创建线程此时,增加机器内存便可以解决问题4:减少JVM堆内存(-Xmx)增加机器内存涉及硬件操作,不是最佳的选择这时,不要忘记:线程不是在JVM的堆内存中创建的!如果为JVM分配堆内存后,剩余内存较少,也会导致OOM: unable to create new native thread在JDK 1.8中,需要减少–Xmx配置的值;在JDK 1.7中,需要减少堆内存和永久代内存( –Xmx和-XX:MaxPermSize)5: 减少进程数减少进程数,与较少堆内存相似如果Java进程数过多,即使每个进程使用的堆内存不大,但累积起来也会导致剩余内存较少,从而出现OOM: unable to create new native thread6: 减少线程栈大小(-Xss)如果每个线程的堆栈较大,应用程序整体消耗的内存也会变大可以根据实际情况,减少线程的堆栈大小,即通过–Xss设置线程堆栈为一个合理的、较小值注意:-Xss不是设置得越小越好,过小的-Xss值会导致应用程序出现java.lang.StackOverflowError错误,甚至都无法启动JVM
问题描述问题排查:通常该问题有几个原因造成1、hosts文件缺少映射或配置错误2、防火墙没关检查问题1.检查各节点之间是否互通,互ping之后发现皆互通,证明两台机器之间接口互通,可排除2.查看防火墙状态,果然没有关闭centos6系统下查看防火墙状态命令 - service iptables status关闭防火墙 - chkconfig iptables offcentos7系统下查看防火墙状态 - firewall-cmd --state关闭防火墙 - systemctl stop firewalld.service3.关闭防火墙测试临时和永久关闭防火墙临时:不重启立即关闭,永久:重启后永久关闭 关闭完之后,再次尝试,问题完美解决4.防火墙不可关闭因安全策略问题防火墙不可关闭解决方案:linux下添加路由:一:使用 route 命令添加使用route 命令添加的路由,机器重启或者网卡重启后路由就失效了,方法://添加到主机的路由# route add –host 192.168.168.110 dev eth0# route add –host 192.168.168.119 gw 192.168.168.1//添加到网络的路由# route add –net IP netmask MASK eth0# route add –net IP netmask MASK gw IP# route add –net IP/24 eth1//添加默认网关# route add default gw IP//删除路由# route del –host 192.168.168.110 dev eth0二:在linux下设置永久路由的方法:1.在/etc/rc.local里添加方法:route add -net 192.168.3.0/24 dev eth0route add -net 192.168.2.0/24 gw 192.168.3.2542.在/etc/sysconfig/network里添加到末尾方法:GATEWAY=gw-ip 或者 GATEWAY=gw-dev三:将linux系统配置为一台路由器步骤:1. 开启路由转发功能:a. sysctl -w net.ipv4.ip_forward=1 路由转发功能临时有效,重启失效。b. echo 1 > /proc/sys/net/ipv4/ip_forward 临时有效,适合脚本。c. 修改/etc/sysctl.conf 文件,将net.ipv4.ip_forward的变量值改为1.再输入sysctl -p 重启配置文件。2. 添加默认路由和静态路由a. route add default gw IP_address 添加一条默认路由。b. route del default 删除默认路由c. route add -net 网络地址 gw IP_address 添加一条静态路由。d. route del -net 网络地址 gw Ip_address 删除静态路由e. vi /etc/sysconfig/network-scipts/route-eth0 然后在文件中添加静态路由 格式如下:network/24 via IP_address 这种添加方式为永久静态路由。在网络的配置目录下,如果没有添加route-bondX路由,重启网络路由会缺失,在现网环境中,这会造成业务中断,影响正常业务;通常有人习惯把路由写进入rc.local,其实也可以,但是不建议这么操作,虽然主机重启的情况下,可以通过rc.local加载路由;如果添加route-bondX路由,无论主机或者网络重启都要去加载相关文件,可以减少路由缺失造成的麻烦。因此,建议将路由写入route-bondX文件。
近期运维排查服务器,发现某个应用的服务器在某个时间端内发现一些IP地址或是一些IP段请求量巨大,持续一段时间后停止,类似情况持续半个月时间了,经过对IP端定位发现为外省IP,考虑小程序的使用用户群体,初步确定为非法请求,需要尽快处理。网站服务器运营环境由NGINX搭建集群使用,方案考虑通过nginx中禁止IP的方法禁止,并思考增加自动化屏蔽的方案,避免人工维护的问题。NGINX禁止IP访问的方法1.在 nginx 的安装目录中,查到 conf 文件夹,并在其文件夹中 创建 blocksip.conf 文件。2.编辑 blocksip.conf 文件,并加入以下代码 :127.0.0.1 为要封禁的 IP 地址3.编辑 conf 文件夹下的 nginx.conf 文件,并加入以下代码4.重启nginx 服务经过测试完成对IP的封禁。学习 关于deny的使用1.nginx封锁禁止单个IP2.nginx 封锁禁止多个IPnginx封锁多个IP地址,多个IP地址由空格分开3.nginx 封IP段即从127.255.0.1到127.255.255.254的命令blocksip.conf:的格式还有许多种,可以配置只允许的IP访问或者IP段访问:其中网段的写法是这样的:192.168.1.0/24这样的形式。deny 192.168.1.11; deny 192.168.1.123; deny 10.0.1.0/24;如果你想实现这样的应用,除了几个IP外,其他全部拒绝,在ip.balcklist中这样写allow 192.168.1.1;allow 192.168.1.2;deny all;也可以对单独网站屏蔽IP的方法在server"{}",在这个大括号内加入deny IP地址是限制某IP地址访问;allow IP地址是只允许某IP地址访问;思考:根据固定IP端的封禁已经实现,那么如何实现nginx的自动禁封IP的功能?分析现有步骤:1.封禁IP的blocksip.conf 文件已增加,并已生效。2.获取需要封禁IP的内容:通过查看NGINX的access.log,获取IP内容。NGINX的access.log的日志格式为:相关说明解释:1.$remote_addr 与$http_x_forwarded_for 用以记录客户端的ip地址;2.$remote_user :用来记录客户端用户名称;3.$time_local : 用来记录访问时间与时区;4.$request : 用来记录请求的url与http协议;5.$status : 用来记录请求状态;成功是200,6.$body_bytes_s ent :记录发送给客户端文件主体内容大小;7.$http_referer :用来记录从那个页面链接访问过来的;8.$http_user_agent :记录客户端浏览器的相关信息;其实nginx access日志的格式不是一成不变的,是可以自定义的。在nginx的nginx.conf配置文件找到:log_format 这里就是日志的格式。以某个日志案例说明:查看日志可以获取到该IP再不断的请求路径,访问数据。查看日志中该IP的出现次数:某天居然出现了2000+次,查看该IP地址中检索到的访问者的地址信息为外地某省份,需要封禁该IP。将IP地址增加到blocksip.conf 文件中,并重启nginx服务生效。如何实现自动禁封?考虑使用shell 和定时任务来实现实现思路:1.通过shell 脚本抓取nginx 的错误日志中统计超过100次某请求的IP2.将新获取的IP增加到nginx的配置文件blocksip.conf中,如果有新增的IP,则重启nginx服务,如没有新增IP则不需要重启。3.将脚本加入定时任务重执行。因Nginx 的错误日志进行根据时间切割,读取日志文件时需要注意时间格式对齐。增加对统计频率的判断为 10分钟一次,根据指定的频率执行脚本,获取攻击的IP,将攻击的IP追加到黑名单。判断是否有增加IP信息,如有增加重启 reload Nginx 服务 。若没有新增的IP则什么都不做。自动执行脚本执行一段时间后,可人工介入进行检查,并适当调整脚本。思考:攻击IP较多的情况可直接加入防火墙禁止IP端的请求。vi /etc/rc.d/init.d/iptables 保存规则 service iptables restart 重启iptables服务以便生效。
一.读卡器的调用过程1.本地客户端下载相关的读卡器的驱动包,里面已经写好了浏览器读卡身份证基本信息的demo信息。2.将读卡器连接电脑,通常为USB接口插入即可,按照操作说明安装驱动信息。3.解压下载的文件后先找到sdk里自带的测试工具测试读卡器是否正常工作,指示灯是否亮等。4.放入二代身份证点击获取身份证信息按钮能获取到信息表示读卡器连接成功.5.通过浏览器的访问demoHTML文件访问默认接口,读卡器放上有效身份证,点击按钮如果控件安装失败会有脚本报错信息,如果出现如下所示说明IE已经读取到身份证信息,接下来就是根据你实际的业务模块集成到你的web系统了。二.问题反馈近期有项目反馈该功能出问题了,需要在浏览器中通过读卡器设备读取身份证信息,使用谷歌打开,报了如下跨域问题看错误信息为跨域问题。开始考虑问题原因,经过测试发现部分浏览器功能正常使用,没有出现跨域报错信息,换其他环境测试出现偶发现象。三.问题定位1.何为跨域 前后端不在同一个域就会存在跨域问题。2.跨域原因产生跨域的原因:浏览器,具体说是浏览器的“同源策略”。浏览器的同源策略用于隔离潜在恶意文件的重要安全机制。跨域产生的原因是下面三点:1、浏览器的限制2、跨域3、请求是XHR(XMLHttpRequest)现在跨域问题很常见,很多项目采用了前后端分离的模式进行开发的,前后端代码部署再不同的服务器上。 问题排查刚开始想还是代码问题处理有bug,但该功能已正常使用一段时间,突然出现bug问题,还是外部环境因素较大,经过对比浏览器配置和版本信息,发现还是有很大差别的,考虑客户端版本的问题,应该是升级了谷歌浏览器后出现一些配置的问题。四.问题解决问题找到了,如何解决有多种方案,通常百度搜索解决浏览器跨域问题解决方案有很多。1.浏览器配置变更,通过修改特定配置。进入谷歌浏览器,在网址栏输入下方路径访问 chrome://flags/找到关键字查询 Block insecure private network requests. 查询后,修改为“Disabled”,并Relaunch重新启动下,点击后会自动重启浏览器。重启后,就解决了此跨域问题。但考虑到客户端的用户群体,这样的操作太过于负责,不推荐。2.研发人员修改代码 JSONP实现JSONP包含两部分:回调函数和数据。回调函数是当响应到来时要放在当前页面被调用的函数。数据就是传入回调函数中的json数据,也就是回调函数的参数了。jsonp虽然很简单,但是有如下缺点:1)安全问题(请求代码中可能存在安全隐患)2)要确定jsonp请求是否失败并不容易经过测试,身份证读取跨域问题得到解决。总结跨域问题在目前后端分离的架构中普遍存在,互联网中也有很多解决方案,虽然都能够解决跨域问题,但其实各有优劣。比如Jsonp方式实现起来较为简单,但只支持GET请求方式,在原生JavaScript脚本中使用方便,但是当利用了如Vue.js这种MVVM框架时就有些难以施展了。我们要在实际情形中比较分析,选择最合适的方案。
进入函数计算控制台, 点击左侧的应用按钮:注:如果之前使用过应用中心,点击应用之后出现类似于下面的页面:点击“创建应用”按钮继续流程点击应用/通过模版创建应用之后,可以看到模版列表, 此时选择“web应用/serverless vscode webide”即可:选择立即创建,然后选择直接部署:此时还需要关注页面上角色名称部分,此时需要点击前往授权进行授权, 点击同意授权。完成Service role的角色授权和填写一个指定region的 OSS 存储桶,点击页面最下面的创建按钮可以打开oss 控制台,比如你准备部署在杭州, 如下面, 那就在杭州创建一个 oss 的 bucket3. 等待几分钟待应用创建部署完成,就可以看到访问域名浏览器打开域名, 就可以访问一个 vscode 版本的 web ide 了注意: 通过浏览器打开 web ide ,会产生少许的流量费用, 收费标准为 0.5 元/GB。请您在使用完成后及时删除应用,避免欠费。
开通函数计算使用您自己的阿里云账号登录阿里云控制台,然后进入函数计算产品详情页。单击【免费开通】。 阅读《函数计算服务协议》勾选同意服务协议,最后单击【立即开通】。 单击【管理控制台】进入函数计算控制台。 开通对象存储同理, 进入[对象存储产品详情页](https://www.aliyun.com/product/oss) 走开通流程
本场景带你体验如何基于函数计算一键部署一个Serverless VSCode WebIDE。Serverless VSCode WebIDE是基于Serverless架构和Vscode的即开即用,用完即走的轻量Web IDE服务。主要特点:全功能 Vscode Web IDE,支持海量的插件。虚拟机级别的多租安全隔离。数据实时保存。用户可以随时关闭页面而不必担心数据丢失。状态实时恢复。依托于函数计算极致的启动速度,秒级恢复到上次的状态。用户可随时继续。资源利用率高,低成本。绝大多数 IDE 的使用是碎片化的,只在一天中的少部分时间被使用,因此 IDE 实例常驻是不明智的。借助函数计算完全按需付费,忙闲时单独定价的计费策略,成本比常驻型 IDE 实例低 3-10x。背景知识函数计算(FunctionCompute)函数计算是事件驱动的全托管计算服务。使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为您准备好计算资源,弹性地可靠地运行任务,并提供日志查询、性能监控和报警等功能。函数计算帮助您无需管理服务器(Serverless),仅专注于函数代码就能快速搭建应用。函数计算能够弹性地伸缩,您只需要按使用量付费。 函数计算 Serverless 服务和自建服务相比有以下优点: 上手简单, 只专注业务逻辑开发, 极大提高工程开发效率。自建方案有太多学习和配置成本,例如针对不同场景,ESS需要做各种不同的参数配置系统环境的维护升级麻烦等。免运维,函数执行级别粒度的监控和告警。毫秒级弹性扩容,保证弹性高可用,同时能覆盖延迟敏感和成本敏感类型。Serverless Devs开发者工具Serverless Devs 是一个组件化与插件化的 Serverless 开发者平台,开发者可以在平台中可插拔式的使用不同 Serverless 的服务和框架,同时可参与组件和插件的开发。无论是工业级的 Serverless 服务,还是各类开源的 Serverless 框架,Serverless Devs 都可友好支持。开发者无需对市面上每一款 Serverless 工具进行研究和学习,只需通过 Serverless Devs ,就可以简单、快捷的“上手”主流 Serverless 服务和框架。
登录SAE管理控制台(https://sae.console.aliyun.com/)。在左侧导航栏中,单击任务模板列表。在任务模板列表页面顶部,切换到华南1(深圳)region。在任务模板列表页面,单击创建任务模板。在任务基本信息页面,任务模板名称输入为sae-hot-news,专有网络配置选择自动配置,单击下一步:部署配置。注意:任务模板名称请您一定设置为sae-hot-news,我们将会根据此名称校验并判断您是否完成了任务。在部署配置页面,参考如下说明进行配置,然后单击下一步:任务设置。参数说明:技术栈语言:选择Java。任务部署方式:选择JAR包部署。任务运行环境:选择标准Java应用运行环境。Java环境:选择Open JDK 8。文件上传方式:选择JAR包地址。JAR包地址:输入https://sae-demo-cn-shenzhen.oss-cn-shenzhen.aliyuncs.com/resou-1.0-SNAPSHOT-jar-with-dependencies.jar。版本:默认配置即可,无需改动。时区设置:默认UTC+8,无需改动。在任务设置页面,单击开通EventBridge。说明:如果您已开通Event Bridge,可忽略此步骤。在事件总线 EventBridge(按量付费)页面,阅读并勾选事件总线 EventBridge(按量付费)服务协议,单击立即开通。返回如下页面,表示您已成功开通Event Bridge。切换至任务设置页面,单击立即授权。在任务设置页面,Cron表达式输入为0 */1 * * ?,即每小时执行一次,单击下一步:确认规格。在确认规格页面,单击确认创建。返回如下页面,表示您已完成创建任务模板。
使用MD5校验iso文件,进行md5的校验我们需要首先知道文件的md5值,一般来说文件的下载地址会为我们提供检验用的md5文件,一般来说这些文件使用如下的命令生成的,接着把qt-everywhere-opensource-src-4.8.6.tar.gz和其验证文件qt-everywhere-opensource-src-4.8.6.tar.gz.md5放到同一目录下(图2-5),然后使用下面的命令进行检验(图2-6)。md5sum qt-everywhere-opensource-src-4.8.6.tar.gz > qt-everywhere-opensource-src-4.8.6.tar.gz.md5---使用qt-4.8.6的源码包生成一个md5文件mkdir md5test---创建一个md5test文件mv qt-everywhere-opensource-src-4.8.6.tar.gz qt-everywhere-opensource-src-4.8.6.tar.gz.md5 md5test/---将qt-4.8.6的源码包和验证文件放到统一目录下md5sum -c qt-everywhere-opensource-src-4.8.6.tar.gz.md5---使用md5sum检验下载文件的完整性当然我们下载的文件,用md5进行检验是检查下载文件的完整性,自然不能用自己生成的md5校验文件,一般来说文件的下载网站会为我们提供检验用的md5文件,其中qt-everywhere-opensource-src-4.8.6.tar.gz的md5校验文件可以从http://download.qt.io/archive/qt/4.8/4.8.6/md5sums-4.8.6 来下载,我们可以查看md5sums-4.8.6中的内容。wget http://download.qt.io/archive/qt/4.8/4.8.6/md5sums-4.8.6 ---下载校验文件cat md5sums-4.8.6---查看校验文件中的内容)最后我们将qt-everywhere-opensource-src-4.8.6.tar.gz软件和md5sums-4.8.6校验文件都放置在/root/家目录中,然后进行文件的校验。此时我们发现第一个文件是确定存在的,其他的由于没有下载显示打开失败,因此校验有效。md5sum -c md5sums-4.8.6
实战演练(3.1)获取字符串的MD5值,字符串“hello”的MD5,我们使用md5sum用来显示或检查MD5(128-bit)校验和,若没有文件选项,或者文件出为“-”,则从标准输入读取,cut用来从标准输入或文本文件中剪切列或域,剪切文本可以将之粘贴到一个文本文件,-d指定域空格和tab键不同的域分隔符,-f1表示第一个域。echo -n "hello" | md5sum | cut -d ' ' -f1---获取字符串的MD5值获取文件的MD5值,我们使用了从download.qt.io下载的源码包qt-4.8.6,可以使用下面的命令从官网下载,然后再计算出文件的MD5值。wget http://download.qt.io/archive/qt/4.8/4.8.6/qt-everywhere-opensource-src-4.8.6.tar.gz ---将软件从官网下载下来md5sum qt-everywhere-opensource-src-4.8.6.tar.gz | cut -d '' -f1---获取qt-4.8.6源码包的MD5值使用MD5校验iso文件,进行md5的校验我们需要首先知道文件的md5值,一般来说文件的下载地址会为我们提供检验用的md5文件,一般来说这些文件使用如下的命令生成的,接着把qt-everywhere-opensource-src-4.8.6.tar.gz和其验证文件qt-everywhere-opensource-src-4.8.6.tar.gz.md5放到同一目录下(图2-5),然后使用下面的命令进行检验(图2-6)。md5sum qt-everywhere-opensource-src-4.8.6.tar.gz > qt-everywhere-opensource-src-4.8.6.tar.gz.md5---使用qt-4.8.6的源码包生成一个md5文件mkdir md5test---创建一个md5test文件mv qt-everywhere-opensource-src-4.8.6.tar.gz qt-everywhere-opensource-src-4.8.6.tar.gz.md5 md5test/---将qt-4.8.6的源码包和验证文件放到统一目录下md5sum -c qt-everywhere-opensource-src-4.8.6.tar.gz.md5---使用md5sum检验下载文件的完整性
(一)MD5介绍(1.1)MD5即Message-Digest Algorithm 5(信息-摘要算法 第5版),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又名:摘要算法、哈希算法),主流编程语言普遍已由MD5实现。将数据运算为另一固定长度值(十六进制的为32位),是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。MD5的作用是大容量信息在用数字签名软件签署私人密钥前被“压缩”成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。除了MD5以外,其中比较有名的还有sha-1、RIPEMD以及Haval等。(1.2)在网络传输、设备之间转存、复制大文件等时,可能出现传输前后数据不一致的情况。这种情况在网络这种相对更不稳定的环境中,容易出现,那么校验文件的完整性,也是势在必行的。(二)md5sum命令(2.1)md5sum命令用于生成和校验文件的md5值,它会逐位对文件的内容进行校验,校验的是文件的内容,与文件名无关,即文件内容相同,其md5值相同。md5值是一个128位的二进制数据,转换成16进制则是32位(128/4)的进制值。md5校验,有很小的概率不同的文件生成的md5可能相同,比md5更安全的校验算法还有SHA系列的。在网络传输时,我们校验源文件获得其md5sum,传输完毕后,校验其目标文件,并对比如果源文件和目标文件md5一致的话,则表示文件传输无异常,否则说明文件在传输过程中未正确传输。(2.2)用法:md5sum [选项] [文件],显示或检查MD5(128-bit)校验和,若没有文件选项,或者文件处为“-”,则从标准输入读取。校验和会按照RFC 1321规范生成,当进行检查时,给出的输入格式应该和程序的输出样板格式相同,默认的输出模式时输出一行校验和校验结果,并有一个字符来表示文件类型(“*”代表二进制,“ ”空格代表纯文本),并同时显示每个文件的名称。-b,--binary:以二进制模式读取-c,--check:从文件中读取MD5的校验值并予以检查-t,--text:以纯文本模式读取(默认)以下三个选项在进行校验时非常有用:--quiet:不为校验成功的文件输出OK--status:不输出任何内容,使用退出状态号显示成功-w,--warn:对格式不正确的校验和进行警告--strict with,--check:exit non-zero for any invalid input--help:显示此帮助信息并退出--version:显示版本信息并退出
5. 数据可视化展现本步骤将指导您如何通过Quick BI创建网站用户分析画像的仪表板,实现数据表rpt_user_info_d的可视化。1、复制如下地址,在Chromium网页浏览器打开新页签,粘贴并访问Quick BI控制台。http://das.base.shuju.aliyun.com/console.htm1.1、选择个人用户或者企业用户,点击免费试用1.2、再弹出框中填写内容,点击确定2、申请成功就自动跳转至Quick BI工作台界面3、在左侧导航栏中,单击数据源,再选择个人空间,点击进入空间4、在数据源页面,单击右上方的新建数据源。5、在添加数据源对话框中,选择云数据库>MaxCompute。6、在添加MaxCompute数据源对话框中,配置如下参数,单击连接测试,待数据源连通性正常后,单击确定。参数说明:显示名称:自定义显示名称,示例为test。数据库地址:使用默认地址,无需修改。项目名称:输入MaxCompute项目名称。AcessKey ID:输入子账号的AccessKey ID。AcessKey Secret:输入子账号的AcessKey Secret。说明 :您可在云产品资源列表中查看MaxCompute项目名称、AccessKey ID和AcessKey Secret。7、在数据源页面,选择您刚刚添加的数据源,单击同步并刷新页面。8、在数据源页面,找到rpt_user_info_d表,单击操作列下 图标。9、转换字段的维度类型。1)转换日期字段的维度类型。在数据集编辑页面的维度区域,右键单击dt字段,选择维度类型切换>日期>yyyyMMdd。2)转换地理信息字段的维度类型。在数据集编辑页面的维度区域,右键单击region字段,选择维度类型切换>地理>省/直辖市。10、在数据集编辑页面,单击右上方的保存。11、在保存数据集对话框中,名称输入为rpt_user,位置选择根目录,单击确定。12、制作仪表板。随着数据的更新,让报表可视化地展现最新数据,这个过程叫制作仪表板。仪表板的制作流程为:确定内容、布局和样式、制作图表和完成动态联动查询。1)在数据集编辑页面,选择右上方的开始分析>创建仪表板。2)在仪表板编辑页面右侧,单击线图,选择指标>指标看板。3)在数据面板,将维度中的dt(year)拖拽到过滤器,将度量中的pv拖拽到看板指标/度量。4)在指标看板面板,单击过滤器中dt(year)的 图标。5)在设置过滤器对话框中,过滤条件的开始于输入为0,过滤条件的结束于输入为0,单击确定。6)在指标看板面板,单击更新。返回如下页面,您可以看到当下的数据。7)在上方菜单栏中,将线图拖拽到下方画布中。8)在数据面板,将维度的dt(day)拖拽到类别轴/维度,将度量的pv拖拽到值轴/度量,将维度的age_range拖拽到颜色图例/维度,将维度中的dt(year)拖拽到过滤器。9)在线图面板,单击过滤器中dt(year)的 图标。10)在设置过滤器对话框中,过滤方式选择单年,过滤条件输入为0,单击确定。11)在线图面板,单击更新。12)在上方菜单栏中,将色彩地图拖拽到下方画布中。13)在数据面板,将维度的region将拖拽到地理区域,将维度中的dt(year)拖拽到过滤器,将度量的pv拖拽到色彩饱和度/度量。14)在色彩地图面板,单击过滤器中dt(year)的 图标。15)在设置过滤器对话框中,过滤方式选择单年,过滤条件输入为0,单击确定。16)在色彩地图面板,单击更新。返回如下页面,您可以看到色彩地图的数据展示。17)在仪表板编辑页面,单击右上方的保存。18)在仪表板对话框中,名称输入为rpt_user,位置选择根目录,单击确定。19)在仪表板编辑页面,单击右上方的预览。返回如下页面,您可以看到色彩地图、指标看板和线图的展示效果。
5. 数据可视化展现本步骤将指导您如何通过Quick BI创建网站用户分析画像的仪表板,实现数据表rpt_user_info_d的可视化。1、复制如下地址,在Chromium网页浏览器打开新页签,粘贴并访问Quick BI控制台。http://das.base.shuju.aliyun.com/console.htm1.1、选择个人用户或者企业用户,点击免费试用1.2、再弹出框中填写内容,点击确定2、申请成功就自动跳转至Quick BI工作台界面3、在左侧导航栏中,单击数据源,再选择个人空间,点击进入空间4、在数据源页面,单击右上方的新建数据源。5、在添加数据源对话框中,选择云数据库>MaxCompute。6、在添加MaxCompute数据源对话框中,配置如下参数,单击连接测试,待数据源连通性正常后,单击确定。参数说明:显示名称:自定义显示名称,示例为test。数据库地址:使用默认地址,无需修改。项目名称:输入MaxCompute项目名称。AcessKey ID:输入子账号的AccessKey ID。AcessKey Secret:输入子账号的AcessKey Secret。说明 :您可在云产品资源列表中查看MaxCompute项目名称、AccessKey ID和AcessKey Secret。7、在数据源页面,选择您刚刚添加的数据源,单击同步并刷新页面。8、在数据源页面,找到rpt_user_info_d表,单击操作列下 图标。9、转换字段的维度类型。1)转换日期字段的维度类型。在数据集编辑页面的维度区域,右键单击dt字段,选择维度类型切换>日期>yyyyMMdd。2)转换地理信息字段的维度类型。在数据集编辑页面的维度区域,右键单击region字段,选择维度类型切换>地理>省/直辖市。10、在数据集编辑页面,单击右上方的保存。11、在保存数据集对话框中,名称输入为rpt_user,位置选择根目录,单击确定。12、制作仪表板。随着数据的更新,让报表可视化地展现最新数据,这个过程叫制作仪表板。仪表板的制作流程为:确定内容、布局和样式、制作图表和完成动态联动查询。1)在数据集编辑页面,选择右上方的开始分析>创建仪表板。2)在仪表板编辑页面右侧,单击线图,选择指标>指标看板。3)在数据面板,将维度中的dt(year)拖拽到过滤器,将度量中的pv拖拽到看板指标/度量。4)在指标看板面板,单击过滤器中dt(year)的 图标。5)在设置过滤器对话框中,过滤条件的开始于输入为0,过滤条件的结束于输入为0,单击确定。6)在指标看板面板,单击更新。返回如下页面,您可以看到当下的数据。7)在上方菜单栏中,将线图拖拽到下方画布中。8)在数据面板,将维度的dt(day)拖拽到类别轴/维度,将度量的pv拖拽到值轴/度量,将维度的age_range拖拽到颜色图例/维度,将维度中的dt(year)拖拽到过滤器。9)在线图面板,单击过滤器中dt(year)的 图标。10)在设置过滤器对话框中,过滤方式选择单年,过滤条件输入为0,单击确定。11)在线图面板,单击更新。12)在上方菜单栏中,将色彩地图拖拽到下方画布中。13)在数据面板,将维度的region将拖拽到地理区域,将维度中的dt(year)拖拽到过滤器,将度量的pv拖拽到色彩饱和度/度量。
参数说明:数据源:选择OSS>oss_workshop_log数据源。文件名(含路径):输入user_log.txt。文件类型:选择text类型。列分隔符:输入列分隔符为|。3)在oss_di页面的数据去向中,配置如下参数,其他配置保存默认。参数说明:数据源:选择ODPS>odps_first数据源。说明:odps_first数据源是工作空间绑定MaxCompute实例时,系统自动生成的默认数据源。odps_first数据源写入至当前工作空间下的MaxCompute项目中。表:选择数据源中的ods_raw_log_d表。4)在页面右侧,单击调度配置。5)在调度配置面板的时间属性区域,重跑属性选择为运行成功或失败后皆可重跑。在调度依赖面板的本节点的输出区域,输入本节点的输出名称为工作空间名称.ods_raw_log_d,单击添加。6)在页面右侧,单击数据集成资源组配置。7)在数据集成资源组配置面板,单击更多选项。8)在警告对话框中,单击确认。9)在数据集成资源组配置面板,方案选择调试资源组。10)在上方工具栏中,单击 图标。11)在数据开发页面左侧,选择业务流程>数据集成,双击rds_di(即下图中的rds_数据同步)。12)在rds_di页面的数据来源中,配置如下参数,其他配置保持默认。参数说明:数据源:选择MySQL>rds_workshop_log数据源。表:选择数据源中的ods_user_info_d表。13)在rds_di页面的数据去向中,配置如下参数,其他配置保存默认。参数说明:数据源:选择ODPS>odps_first数据源。表:选择数据源中的ods_user_info_d表。14)在页面右侧,单击调度配置。15)在调度配置面板的时间属性区域,重跑属性选择为运行成功或失败后皆可重跑。在调度依赖面板的本节点的输出区域,输入本节点的输出名称为工作空间名称.ods_user_info_d,单击添加。16)在页面右侧,单击数据集成资源组配置。17)在数据集成资源组配置面板,单击更多选项。18)在数据集成资源组配置面板,方案选择调试资源组。19)在上方工具栏中,单击 图标。7、提交业务流程。1)在数据开发页面左侧,双击您的业务流程。2)在业务流程的编辑页面上方的菜单栏中,单击 图标。3)在提交对话框中,选择所有的节点,输入备注,选择忽略输入输出不一致的告警,单击提交。返回如下页面, 等待所有节点提交成功,单击 图标即可。
2、新建RDS数据源。1)在数据源管理页面,单击右上方的新增数据源。2)在新增数据源对话框中,选择数据源类型为MySQL。3)在新增MySQL数据源对话框中,配置各项参数,单击更多选项。参数说明:数据源类型:选择阿里云实例模式。数据源名称:输入rds_workshop_log。地域:选择华东2(上海)。RDS实例ID:输入rm-bp1z69dodhh85z9qa。RDS实例主账号ID:输入1156529087455811。数据库名:输入workshop。用户名:输入workshop。密码:输入workshop#2017。4)在资源组列表,单击公共资源组后的测试连通性。返回如下结果,连通状态为可联通,表示数据集成资源组能够与数据源连通。5)在新增MySQL数据源对话框中,单击完成。3、创建业务流程。1)在数据源管理页面,单击左上方的 图标,单击DataStudio(数据开发)。2)在数据开发页面,右键单击业务流程,选择新建业务流程。3)在新建业务流程对话框中,输入业务名称,例如test,单击新建4)在业务流程开发面板,单击虚拟节点并拖拽至右侧的编辑页面。5)在新建节点对话框中,节点名称输入为workshop_start,单击提交。6)在业务流程开发面板,单击离线同步并拖拽至右侧的编辑页面。7)在新建节点对话框中,节点名称输入为OSS_数据同步,单击提交。说明:由于当前实验环境的虚拟桌面不支持直接复制粘贴中文(复制粘贴会出现乱码),您可以单击虚拟桌面右下角的 图标切换虚拟桌面的输入法,手动输入中文。8)在业务流程开发面板,单击离线同步并拖拽至右侧的编辑页面。9)在新建节点对话框中,节点名称输入为rds_数据同步,单击提交。说明:由于当前实验环境的虚拟桌面不支持直接复制粘贴中文(复制粘贴会出现乱码),您可以单击虚拟桌面右下角的 图标切换虚拟桌面的输入法,手动输入中文10)在右侧的编辑页面,通过拖拽连线,将workshop_start节点设置为两个离线同步节点的上游节点。
本场景将为您提供DataWorks、MaxCompute环境以及进行数据集成的数据源资源。通过本教程的操作,您可以完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。背景知识本场景主要涉及以下云产品和服务:DataWorksDataWorks(大数据开发治理平台)是阿里云重要的PaaS(Platform-as-a-Service)平台产品,为您提供数据集成、数据开发、数据地图、数据质量和数据服务等全方位的产品服务,一站式开发管理的界面,帮助企业专注于数据价值的挖掘和探索。DataWorks支持多种计算和存储引擎服务,包括离线计算MaxCompute、开源大数据引擎E-MapReduce、实时计算(基于Flink)、机器学习PAI、实时数仓Hologres,云原生数据仓库 AnalyticDB for PostgreSQL,云原生数据仓库AnalyticDB for MySQL,并且支持用户自定义接入计算和存储服务。DataWorks为您提供全链路智能大数据及AI开发和治理服务。云原生大数据计算服务MaxComputeMaxCompute是面向分析的企业级SaaS(Software-as-a-Service)模式云数据仓库,以Serverless架构提供快速、全托管的在线数据仓库服务,消除了传统数据平台在资源扩展性和弹性方面的限制,最小化用户运维投入,使您可以经济并高效的分析处理海量数据。数以万计的企业正基于MaxCompute进行数据计算与分析,将数据高效转换为业务洞察。
使用Python运算符使用Python的条件语句使用Python的循环语句使用Python的异常处理
K8S基础K8S基础架构K8S解决的问题是什么?k8s全景图k8s Secret对象声明式APIK8S基础架构基础架构图如下所示,我们可以看到master 节点和Node节点。Master节点是控制节点,由三个紧密协作的独立组件组合而成。其中,APIServer负责API服务;Controller Manager负责负责容器编排;Scheduler负责容器调度。Node节点是计算节点,其中也有很多其他组件(1)kubelet,主要负责同容器运行时打交道(2)CRI,远程调用接口,定义了容器运行时的各项核心操作(3)OCI,通过容器运行时规范同底层的Linux系统进行交互,即把CRI请求翻译成对Linux操作系统的调用(操作Linux的Cgroups和Namespace)
对于所有程序员来说,第一次成功建站带来的喜悦都是非常深刻的。能否成功建站,是很多程序员走向成熟的一个关键动作。本次实验将以搭建 Z-Blog博客为例,让各位感受函数计算建站和网站维护的低成本、免运维、高可用等优势。Zblog 是一款小巧而强大的基于Asp平台的Blog程序。非常利于新手操作,可以快速搭建属于自己的网站。本场景将使用阿里云函数计算,Serverless 应用中心,NAS 带大家 1分钟 Serverless 极速搭建个人网站。函数计算:函数计算是事件驱动的全托管计算服务。使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为您准备好计算资源,弹性地可靠地运行任务,并提供日志查询、性能监控和报警等功能。函数计算帮助您无需管理服务器(Serverless),仅专注于函数代码就能快速搭建应用。函数计算能够弹性地伸缩,您只需要按使用量付费。Serverless 应用中心:阿里云 Serverless 应用全生命周期管理平台。通过 Serverless 应用中心,用户在部署应用之前无需进行额外的克隆、构建、打包和发布操作,即可快速部署和管理应用。Serverless 应用中心帮助用户快速联动云上的上下游服务,轻松沉淀最佳实践。文件存储NAS:文件存储NAS是一个可大规模共享访问,弹性扩展的高性能云原生分布式文件系统。支持智能冷热数据分层,有效降低数据存储成本。广泛应用于企业级应用数据共享、容器、AI机器学习、Web 服务和内容管理、应用程序开发和测试、媒体和娱乐工作流、数据库备份等场景。
体验PolarDB-X集群扩容编辑polardb-x.yaml文件。vim polardb-x.yaml按i键进入编辑模式,将CN、DN和CDC的replicas参数改为2,进行扩容操作。按ECS退出编辑模式,输入:wq后按下Enter键保存并退出。将修改后的polardb-x.yaml文件应用到已经创建的PolarDB-X集群中。kubectl apply -f polardb-x.yaml观察集群的变化情况。kubectl get polardbxCluster polardb-x -o wide -w看到PolarDB-X集群扩容过程中各个节点的变化。请您耐心等待两分钟左右,当PHASE显示为Running时,表示PolarDB-X集群已经扩容完成。按Ctrl+C键,退出查看PolarDB-X集群状态。获取PolarDB-X集群登录密码。kubectl get secret polardb-x -o jsonpath="{.data['polardbx_root']}" | base64 -d - | xargs echo "Password: "可以查看到PolarDB-X集群登录密码。46jvb4pd将PolarDB-X集群的端口转发到本地的3306端口。kubectl port-forward svc/polardb-x 3306在实验页面,单击右上角的 图标,创建新的终端二窗口。连接PolarDB-X集群。mysql -h127.0.0.1 -P3306 -upolardbx_root -p46jvb4pd检查扩容后的状态。show storage;可查看到PolarDB-X集群扩容后的状态。体验PolarDB-X集群缩容切换至终端一,按Ctrl+C键,停止PolarDB-X集群端口转发。编辑polardb-x.yaml文件。vim polardb-x.yaml按i键进入编辑模式,将CN、DN和CDC的replicas参数改为1,进行缩容操作。按ECS退出编辑模式,输入:wq后按下Enter键保存并退出。将修改后的polardb-x.yaml文件应用到已经创建的PolarDB-X集群中。kubectl apply -f polardb-x.yaml观察集群的变化情况。kubectl get polardbxCluster polardb-x -o wide -w可以看到PolarDB-X集群缩容过程中各个节点的变化。当PHASE显示为Running时,表示PolarDB-X集群已经缩容完成。按Ctrl+C键,退出查看PolarDB-X集群状态。将PolarDB-X集群的端口转发到本地的3306端口。kubectl port-forward svc/polardb-x 3306切换至终端二,执行如下SQL语句,检查缩容后的状态。show storage;可查看到PolarDB-X集群缩容后的状态。
使用kubectl查看集群信息。kubectl cluster-info部署 PolarDB-X Operator。创建一个名为polardbx-operator-system的命名空间。kubectl create namespace polardbx-operator-system安装PolarDB-X Operator。helm repo add polardbx https://polardbx-charts.oss-cn-beijing.aliyuncs.comhelm install --namespace polardbx-operator-system polardbx-operator polardbx/polardbx-operator查看PolarDB-X Operator组件的运行情况。kubectl get pods --namespace polardbx-operator-system部署 PolarDB-X 集群。创建polardb-x.yaml。vim polardb-x.yamlapiVersion: polardbx.aliyun.com/v1kind: PolarDBXClustermetadata: name: polardb-xspec: topology:nodes: cdc: replicas: 1 template: resources: limits: cpu: "1" memory: 1Gi requests: cpu: 100m memory: 500Mi cn: replicas: 1 template: resources: limits: cpu: "2" memory: 4Gi requests: cpu: 100m memory: 1Gi dn: replicas: 1 template: engine: galaxy hostNetwork: true resources: limits: cpu: "2" memory: 4Gi requests: cpu: 100m memory: 500Mi gms: template: engine: galaxy hostNetwork: true resources: limits: cpu: "1" memory: 1Gi requests: cpu: 100m memory: 500Mi serviceType: ClusterIPupgradeStrategy: RollingUpgrade保存后退出编辑创建PolarDB-X集群。kubectl apply -f polardb-x.yaml查看PolarDB-X集群创建状态。kubectl get polardbxCluster polardb-x -o wide -w按Ctrl+C键,退出查看PolarDB-X集群创建状态。体验PolarDB-X集群扩容编辑polardb-x.yaml文件。vim polardb-x.yaml按i键进入编辑模式,将CN、DN和CDC的replicas参数改为2,进行扩容操作。
安装环境安装Docker。curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun启动Docker。systemctl start docker安装kubectl。下载kubectl文件。curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl赋予可执行权限。chmod +x ./kubectl移动到系统目录。mv ./kubectl /usr/local/bin/kubectl安装minikube。下载并安装minikube。curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64sudo install minikube-linux-amd64 /usr/local/bin/minikube安装Helm3。下载Helm3。wget https://labfileapp.oss-cn-hangzhou.aliyuncs.com/helm-v3.9.0-linux-amd64.tar.gz解压Helm3。tar -zxvf helm-v3.9.0-linux-amd64.tar.gz移动到系统目录。mv linux-amd64/helm /usr/local/bin/helm安装MySQL。yum install mysql -y使用PolarDB-X Operator安装PolarDB-X使用minikube创建Kubernetes集群。新建账号galaxykube,并将galaxykube加入docker组中。minikube要求使用非root账号进行部署,所有需要新建一个账号。useradd -ms /bin/bash galaxykubeusermod -aG docker galaxykube切换到账号galaxykube。su galaxykube进入到home/galaxykube目录。cd启动一个minikube。minikube start --cpus 4 --memory 12288 --image-mirror-country cn --registry-mirror=https://docker.mirrors.sjtug.sjtu.edu.cn返回结果如下,表示minikube已经正常运行,minikube将自动设置kubectl的配置文件。
本场景提供了一个托管版Kubernates集群,通过本场景的实验操作,您将掌握Kubernetes集群的命令行工具kubectl的基本使用方法。背景信息KubernetesKubernetes是Google开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。在Kubernetes中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。阿里云容器服务Kubernetes版ACK阿里云容器服务Kubernetes版ACK(Alibaba Cloud Container Service for Kubernetes)是全球首批通过Kubernetes一致性认证的服务平台,提供高性能的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理,让您轻松高效地在云端运行Kubernetes容器化应用。kubectlkubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。
七、搭建OwnCloud环境OwnCloud是一款开源的云存储软件,基于PHP的自建网盘。OwnCloud基本上是私人使用,没有用户注册功能,但是有用户添加功能,您可以无限制地添加用户。OwnCloud支持多个平台(Windows、MAC、Android、IOS、Linux)。1. 执行如下命令,下载owncloud服务器包。wget https://cdn.damo.alibaba.com/adc/owncloud-10.0.9.zip2. 执行如下命令,解压owncloud服务器包。unzip owncloud-10.0.9.zip3. 执行如下命令,将owncloud包移动到/var/www/html目录下。mv owncloud /var/www/html4. 执行如下命令,赋予owncloud文件夹权限。chown -R www-data:www-data /var/www/html/owncloud/ chmod -R 755 /var/www/html/owncloud/5. 执行如下命令,重启Apache服务。/etc/init.d/apache2 restart6. 打开浏览器,访问http:///owncloud,依次输入管理员用户名、管理员密码、数据库用户名、数据库密码、数据库名、localhost,单击安装完成。参数说明:管理员用户名:设置一个用户名。管理员密码:设置一个用户密码。数据库用户名:输入owncloud。数据库密码:输入owncloud。数据库名:输入owncloud。localhost:输入localhost:5432。7. 在登录界面,输入上一步设置的用户名和用户密码,并单击图标登录。登录成功界面如下。
2023年03月
2023年02月
啥活动
学习
学习
学习
学习
什么
学习
学习
学习
学习
什么什么
学习学习
学习学习
学习
111
111
111
111
学习学习