自学内容网 自学内容网

奔跑吧Linux内核(入门篇)- 心得笔记总结

1、Red Hat、CentOS、Debian、Ubuntu都是不同的Linux发行版本

Red Hat:商业和技术上做的最好的厂商,主要面向服务器版本的开发。

CentOS:基于Red Hat源码重新编译的,可免费使用。

Debian:自由操作系统,一般指Debian GNU/Linux,因其特有的apt-get/dpkg软件包管理工具而风靡全球。

Ubuntu:基于Debian打造的以桌面应用为主的Linux发行版,界面友好,具有完善的包管理系统,应用多。但对于服务器操作系统,并不需要太多应用,使用稳定、操作方便、维护简单的系统即可。

2、本地内核源码路径:BASEINCLUDE ?= /lib/modules/$(shell uname -r)/build

3、Linux内核通过系统调用(指令)完成内核态、用户态间ring0与ring3两种模式的切换。

4、GCC:GNU编译器套件(GNU Compiler Collection, 即GCC)是使用GPL协议(开源协议)的自由软件,是GNU Linux操作系统支持的默认编译器。

5、GCC支持本地编译和交叉编译。

6、交叉编译器工具链:不只是GCC,还包括binutils、glibc等工具链组成的综合开发环境,开源实现编译、链接等功能,其本质也是个程序,需要在shell中运行。

7、用git进行开源工作的一般流程:

       (1)复制项目的git仓库到本地工作目录。

(2)在本地工作目录里添加或修改文件。

(3)在提交修改之前检查补丁格式等。

(4)提交修改。

(5)生成补丁并发给评审,等待评审意见。

(6)评审发送修改意见,再次修改并提交。

(7)直到评审同意补丁并且合并到主干分支。

8、编译器链接阶段:任务是对编译好的二进制文件进行链接,默认链接C语言标准库libc。C语言标准库中包含代码调用中的malloc()、memset()、printf()等函数,链接过程会将程序的目标文件所需的库文件链接起来。

9、动态链接库和静态链接库

静态库链接方式:程序编译时被连接到目标代码中参与编译,连接时将库完整的拷贝至可执行文件中,被多次使用则有多份冗余拷贝

动态库链接方式:程序运行时由系统动态加载动态库到内存,供程序调用,系统只加载一次,多个程序共用,节省内存

静态链接库和动态链接库区别_动态链接库和静态链接库的区别-CSDN博客

10、Linux5.4内核已经能够支持几十种处理器架构,目前最流行的是x86_64和ARM64。x86在服务器和PC中占主导地位,而ARM64则是在手机芯片、嵌入式中占主导地位。

11、指令集用来构建整个ARM架构的软件生态系统。

12、ARM架构:ARM的处理器采用的都是精简指令集(RISC)计算机架构

(1)处理单元:将处理器处理事务的过程抽象为处理单元。

(2)执行状态:处理器运行时的环境,包括寄存器的位宽、支持的指令集、异常模型、内存管理以及编程模型等。ARMv8架构提供了两种模式

  1. AArch64:64位的执行状态
    1. 提供31个64位的通用寄存器
    2. 提供64位的程序计数(PC)寄存器(存储当前或下一条指令的地址)、栈指针(SP)寄存器(指向栈顶的地址,管理栈内存)以及异常链接寄存器(ELR)(保存异常或中断发生时的返回地址)
    3. 提供A64指令集
    4. 定义异常模型,提供4个异常等级-EL0~EL3。
    5. 提供64位内存模型
    6. 定义一组处理器状态(PSTATE)用来保护PE的状态
  2. AArch32:32位的执行状态

(3)ARMv8指令集

根据不同的执行状态提供对不同指令集的支持

A64指令集:运行在AArch64状态,提供64位指令集支持。

A32指令集:运行在AArch32状态,提供32位指令集支持。

(4)系统寄存器命名
许多系统寄存器根据异常等级不同提供不同的变种寄存器,如SP_EL0表示EL0下的栈指针寄存器,而SP_EL1表示EL1下的栈指针寄存器。

13、异常

ARM64中,异常是处理器在执行指令时可能遇到的不同寻常的情况事件。可由软件或硬件引发。ARM64定义了一套异常模型,用于处理和响应这些异常情况。

(1)Exception levels(EL)

Armv8-A体系结构定义了一组异常级别,EL0到EL3,其中

EL0:无特权执行,应用层

EL1:特权执行,操作系统内核层

EL2:提供对虚拟化的支持。

EL3:支持在两种安全状态(安全和非安全状态)间切换。

EL0的execution previlege最低,EL3的execution privilege最高。当发生异常时,系统的exeception会迁移到更高exception level或保持不变,但绝不会降低。

(2)系统调用SVC指令

属于异常的同步软件异常,系统调用通过SVC指令触发。

执行SVC指令会生成一个Supervisor Call,是一个针对EL1的同步异常。为EL0执行的软件提供了一种调用在EL1执行的操作系统或其他软件的机制。

ARM64架构中EL0是用户空间,EL1是内核空间。

SVC指令提供了在不同指令级别(EL)之间进行通信和交互的机制。通过执行SVC指令,EL0中的用户空间程序可以请求EL1中运行的操作系统或其他软件执行特权操作

完成异常操作后,执行ERET指令从异常返回。

参考自:Linux arm64异常简介和系统调用过程_synchronous exceptions-CSDN博客

14、页表查询:完整的页表翻译和查找的过程。页表查询由硬件自动完成,但是页表的维护由软件完成。

15、设备节点:主设备号代表一类设备,次设备号代表同一类设备的不同个体,每个次设备号都有一个不同的设备节点。所有设备节点都放在/dev/目录中。

16、设备节点的生成有两种方式:一种使用mknod命令手动生成,一种使用udev机制动态生成。(仔细查下mknod和udev)

17、udev是一个在用户空间中使用的工具,能够根据系统中硬件设备的状态动态地更新设备节点,包括设备节点创建、删除等。该机制必须联合sysfs和tmpfs来实现。sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间。

18、ls -l /dev/查看文件属性时,第一列中c表示字符设备,d表示块设备,后面还会显示主次设备号。

19、进行I/O多路复用的原因:一个用户进程要监控多个I/O设备,在访问一个I/O设备后就会进入睡眠状态,就无法进行其他操作。若要监控多个,就需要采用多进程或多线程,导致大量的资源浪费。

20、POSIX(Portable Operating System Interface of Unix)实际上系统调用的接口标准,而libc库则是定义了用户态一系列系统调用的API接口。

21、不同的接口函数可能调用了同一个系统调用,封装了不同功能,如libc库中的malloc()、calloc()、free()函数,都是调用brk系统调用实现,通过传递新的堆的末尾地址给brk。

22、brk()用于调整进程的堆大小,malloc和calloc()调用来扩展堆空间大小,新的堆末尾地址比原先的大,free()则是缩小堆空间,新的堆末尾地址小于当前的堆末尾地址。

23、系统调用

Linux中为每个系统调用都赋予了一个系统调用号,系统调用号一旦分配,就不会有任何变更。

(1)SYSCALL_DEFINE宏:

系统调用宏SYSCALL_DEFINE1表示有1个参数,SYSCALL_DEFINE2表示有2个参数。SYSCALL_DEFINEx宏则表示有x个参数。

宏展开后:包含两个函数,__arm64_sys_open()和__se_sys_open()。其中__arm64_sys_open()函数的地址放在系统调用表sys_call_table中,最后该函数变为__do_sys_open()。

(2)__SYSCALL宏:

用来设置某个系统调用的函数指针到sys_call_table[]数组中。

(3)系统初始化时会把__arm64_sys_xx()函数添加到sys_call_table[]数组里。

24、应用程序对系统调用的访问:通常不会直接访问系统调用,而是通过C标准库(如glibc)函数(open、read等)访问系统调用。若是给添加了新的系统调用,则卡与直接调用syscall()函数来访问新添加的系统调用。

25、早期操作系统中,程序都是单个地运行在一台计算机中,CPU利用率低下。为了提高CPU利用率,人们设计了在一台计算机中加载多个程序到内存中并让它们并发运行的方案。每个加载到内存中的程序称为进程,操作系统管理着多个进程的并发执行。

26、内存的一部分就是为装载程序设计的,程序(原先可能在磁盘中)加载至内存后,成为进程。

27、程序和进程的定义

程序:通常是完成特定任务的一系列指令集合或一个可执行文件(ELF)。

进程:一段执行中的程序。除了代码段、数据段等信息外,还包括许多运行时需要的资源。

28、程序和进程的组成区别

程序ELF可执行文件):

       1)头部:文件的基本信息,如ELF类型,目标架构等。

       2)程序头表:描述如何将文件各个部分加载到内存。

       3)节点表:描述ELF文件各个节(如代码段、数据段)的属性。

       4)代码段:存储可执行的程序代码。

5)数据段:存储程序运行时的全局数据、静态变量等。通常分为初始化数据段(.data)和未初始化数据段(.bss)。

       6)符号表:存储程序中所有符号(如函数名、变量名)的信息,用于链接。

       7)重定位信息:用于动态链接过程中修改地址的辅助信息。

进程正在执行的程序实例,除ELF文件的代码和数据外,还包括系统为程序分配的内存资源、寄存器状态、打开的文件描述符等信息):

       1)代码段:与ELF文件中代码段一致。进程中,代码段是只读的,防止进程修改代码。

2)数据段:与ELF中数据段一致,存储全局变量和静态变量。进程中,数据段内容可被修改。同样有.data和.bss。

3)堆:用于动态分配内存,通常由程序运行时的malloc或new等函数管理。堆大小通常动态增长。

4)栈:用于管理函数调用、局部变量和返回地址等。

5)共享库映射

6)寄存器和程序计数器(PC):存储处理器的状态,以及表示程序当前执行的位置。

7)打开的文件和设备:进程运行过程中打开的文件、网络套接字等,通过文件描述符管理。

29、执行ELF文件时,装载器将ELF文件装载至内存中,CPU读取内存中的指令和数据,程序就被执行起来了。

30、典型操作系统中进程的5种状态:创建、就绪、运行、阻塞、终止态。

31、Linux内核中定义的进程的5种状态:TASK_RUNNING(可运行态或就绪态)、TASK_INTERRUPTIBLE(可中断睡眠态)、TASK_UNINTERRUPTIBLE(不可中断态)、_TASK_STOPPED(终止态)、EXIT_ZOMBIE(僵尸态)。

32、进程task_struct

(1)进程状态的设置:task_struct中的state成员,如p->state = TASK_RUNNING。

(2)进程的所有task_struct结构都通过list_head类型的双向链表链在一起。

(3)current宏找到当前正在运行的进程的task_struct。

(4)current宏寻找过程(4.0内核中):首先通过ARM32的SP寄存器获取当前内核栈的地址,对齐后可以获取thread_info数据结构(位于内核栈底部)的指针,最后通过thread_info->task成员获取task_struct数据结构。

(5.0内核中)thread_info数据结构中去掉了指向进程描述符的task成员,获取task_struct的方法随之变化。在内核态,ARM64处理器运行在EL1下,sp_el0寄存器在EL1上下文没有使用,用其存放task_struct数据结构的地址。

33、进程的创建

(1)ELF文件执行的详细过程(shell下运行):运行后,shell会通过fork()创建一个新进程,然后调用execve()执行该新进程。

  1. fork()通过写时复制技术复制当前进程的相关信息创建一个全新的子进程,此时子进程和父进程运行在各自的进程地址空间中,但是共享相同的内容。
  2. execve()负责读取可执行文件,将其装入子进程的地址空间并开始运行,此时子进程和父进程才开始分道扬镳。

(2)写时复制技术:即父进程创建子进程时不需要复制进程地址空间的内容,只需要复制父进程的进程地址空间的页表给子进程,父子进程就可以共享相同的物理内存。当父子进程中有一方需要修改某个物理页面内容时,触发写保护的缺页异常,才将共享页面的内容复制出来,让父子进程拥有各自的副本。写时复制可以推迟甚至避免复制数据。

(3)fork()函数同样要通过系统调用进入内核,之后通过_do_fork()函数实现。

(4)vfork()的父进程会阻塞,直到子进程调用exit()或execve()。

34、线程创建

(1)Linux中将线程当成普通进程看待,并同样以task_struct结构来描述。

(2)clone()函数用以创建用户线程,与父进程共享进程地址空间,从而创建进程。

(3)内核线程没有独立的进程地址空间,所有内核线程共享内核地址空间,常见的内核线程有页面回收线程”kswapd”等。

(4)内核线程创建方法:(1)kthread_creat(需要唤醒)(2)kthread_run可马上运行。

35、_do_fork()函数:

内核中,fork()、vfork()、clone()系统调用都通过_do_fork()函数实现。共有6个参数:

clone_flags:创建进程的标志位集合。(通过该标志位控制是否共享进程地址空间等)

stack_start:用户态栈的起始地址

stack_size:用户态栈的大小,通常设置为0

parent_tidptr:指向父进程的ID

child_tidptr:指向子进程的ID

tls:传递的TLS参数

clone_flags常见标志位:CLONE_VM父子进程共享地址空间、CLONE_FILES共享打开的文件等。

36、父进程通过wait()系统调用获取已终止的子进程信息后,内核才会释放子进程的task_struct。之所以进程会处于僵尸状态,是为了让系统能够得知子进程的终止原因

37、wait系统调用的两个功能

  1. 获取进程终止的原因
  2. 销毁进程的task_struct数据结构的最后资源

38、进程0(空闲进程)有好几个别名,如进程0、idle进程或swapper进程

39、当CPU上的就绪队列中没有其他可运行的进程时,调度器才会选择执行进程0,并让CPU进入空闲(idle)状态。在SMP中,每个CPU都有一个进程0(swapper进程)。

40、进程调度器产生的最大原因是为了提高处理器的利用率。

41、进程优先级数值越低,优先级越高。

42、进程切换

(1)__schedule()是调度器的核心函数,让调度器选择和切换到一个合适的进程并运行。

(2)schedule()调度函数实现的功能:1、pick_next_task()选择下一个要运行的进程2、调用context_switch()函数进行上下文切换

(3)进程切换主要为两步(核心函数context_switch):

  1. 切换进程的地址空间,即切换next进程的页表到硬件页表中,由switch_mm()实现。(进程地址空间的切换,其实是页表的切换
  2. 切换next进程的内核态栈和硬件上下文,由switch_to()实现。(硬件上下文即进程恢复执行前需要装入CPU寄存器的数据)

(4)switch_mm():将新进程的页表基地址设置到页表基地址寄存器、刷新TLB等,完成硬件相关设置。

(5)switch_to():栈空间切换。

(6)cpu_context结构:勾画了进程切换时,CPU需要保存哪些寄存器,即进程硬件上下文。对ARM64处理器,切换进程时需要把prev进程的x19~x28寄存器以及fp、sp和pc寄存器保存到cpu_context结构体中,把next进程上一次保存的cpu_context结构体中的值恢复至实际的硬件寄存器中。

struct cpu_context{

       unsigned long x19;

       unsigned long x20;

       unsigned long x21;

       unsigned long x22;

       unsigned long x23;

       unsigned long x24;

       unsigned long x25;

       unsigned long x26;

       unsigned long x27;

       unsigned long x28;

       unsigned long fp;

       unsigned long sp;

       unsigned long pc;

}

43、MMU(硬件单元)位于处理器中,MMU会处理虚拟内存到物理内存的映射关系,即页表的翻译工作。

44、进程所需存放的三种数据在内存:

进程本身占用内存,如代码段、数据段。

栈空间,保存函数调用关系关系、局部变量、函数参数以及函数返回值等。

堆空间,程序运行时动态分配使用的内存。

45、进程访问虚拟地址时,不用关心具体物理地址在哪,由MMU实现。

46、虚拟地址分为页帧号(页表索引)页内偏移量两部分。

47、页表项包括两部分:(1)对应的物理页帧号(PFN)(2)页表项属性如是否有效等

48、每个进程都有一套自己的页表,在进程切换时需要切换页表基地址。

49、多级页表访问过程,一开始只将一级页表加入到内存中,二级页表只有访问到时才加入。

50、物理内存管理

(1)Linux内核中每个物理页面用一个page数据结构描述

(2)伙伴系统是操作系统中最常用的动态存储管理方法之一。成为伙伴的三个条件为:

  1. 两个内存块的大小相同
  2. 两个内存块的地址连续
  3. 两个内存块必须是从同一个大的内存块中分离出来的。

51、虚拟内存管理

进程地址空间在内核中使用vm_area_struct(VMA)结构描述,这些地址空间归属于各个用户进程,用户进程的mm_struct结构中有相应的成员,来管理这些VMA。

(1)内存描述符mm_struct

用以描述每个进程所有内存区域及它们所对应的页表映射。进程控制块task_struct中有一个指针mm指向该mm_struct。其主要成员包括:

  1. mmap:进程中所有VMA连成一个单链表,mmap是该链表头
  2. mm_rb:红黑树根节点
  3. start_code和end_code:代码段的起始和结束地址。
  4. start_data和end_data:数据段的起始和结束地址
  5. start_brk:堆空间的起始地址
  6. brk:当前堆中VMA的结束地址
  7. total_vm:已使用的进程地址空间总和。

(2)VMA管理

vm_start和vm_end:VMA在进程地址空间中的起始和结束地址

vm_next和vm_prev:进程VMA被链接成一个链表

vm_rb:VMA作为节点加入红黑树中,每个进程的mm_struct结构都有一棵红黑树mm->mm_rb。

vm_mm:指向VMA所属进程的mm_struct结构。

vm_page_prot:VMA访问权限

vm_flags:用于描述VMA的一组标志位

vm_file:指向File实例,描述一个被映射的文件。

当进程拥有大量VMA时,扫描链表和查找特定的VMA非常低效,需要使用红黑树查找VMA节点,加快查找速度,其中包含了所需内存块的信息)

每个VMA指向一段单独的空间(代码段、数据段等),通过页表将每块空间与物理内存页相映射。

(3)VMA属性:

即该部分内存的状态,如:

       VM_READ:可读属性

       VM_WRITE:可写

       VM_EXEC:可执行

       VM_SHARED:运行被多个进程共享

       VM_UFFD_MISSING:适用于用户态的缺页异常处理

       VM_IO:I/O内存映射

(4)页表属性

VMA属性可以任意组合,但最终会落实到硬件机制上,即页表项的属性,使用vm_get_page_prot()函数可以把vm_flags标志位转换成具体的页表项的硬件标志位。属性如:

       PTE_PXN:表示该内存不能在特权模式下执行。

       PTE_UXN:表示该内存不能在用户模式下执行。(实现了用户态、内核态内存访问权限的限制)

       PTE_RDONLY:表示只读属性

52、每个进程都有一个mm_struct结构,包含了一份属于进程自己的页表、一棵管理VMA的红黑树以及链表。

53、mmap()/munmap()函数

mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

是用户空间中最常用的两个系统调用的接口函数,函数参数包括:

  1. addr:指定映射到进程地址空间的起始地址,设置为NULL,可让内核来选择合适的地址。该地址为进程地址(虚拟内存地址),该函数会返回该块内存的起始地址。
  1. length:映射到进程地址空间的大小
  2. prot:设置内存映射区域的读写属性等。
  3. flags:设置内存映射的属性,如共享映射、私有映射等。
  4. fd:表示这是文件映射,fd是打开文件的句柄
  5. offset:文件映射时,表示文件的偏移量。

53、malloc()和mmap()只建立了进程地址空间,可以在用户空间内看到虚拟内存,但没有建立虚拟内存和物理内存之间的映射关系。当进程访问这些虚拟内存时,会自动触发“缺页中断”。

54、内存置换时,优先换出文件缓存页面而不是匿名页面,匿名页面换出消耗大,需要被写入交换分区才能换出,而文件缓存不需要回写磁盘,除非页面内容被修改。

55、Linux中可通过“/proc/meminfo”查看系统所有物理页面信息。

56、多线程访问“i++”不加锁可能效果不理想的原因:从CPU角度看,首先将变量值读取到通用寄存器中,然后在通用寄存器中做i++运算,最后将寄存器的值写回变量i所在的内存。可能一个线程还未将数值写回,另一个线程又读取了变量值。

57、相比自旋锁,信号量可以睡眠,适用于加锁时间较长的场景。

58、虽信号量为1可实现互斥锁功能,但互斥锁的数据结构比信号量小,语义简单轻便,由此执行速度更快,可拓展性更好。

59、互斥锁可以睡眠,不允许在中断上下文中使用。

60、在中断上下文中可以使用自旋锁,但若临界区(锁住的区域)有睡眠、隐含睡眠的动作以及内核API,避免使用自旋锁。

61、读写锁特性:(1)多个读者可同时进入临界区(2)同时刻只能一个写着进入(3)读者和写者不能同时进入临界区。

62、读写锁有两种实现:自旋锁和信号量。

63、RCU,Read-Copy Update(读-复制-更新)机制:RCU记录了所有指向共享数据的指针的使用者,修改共享数据时,首先创建副本,在副本中修改。所有读访问线程离开读临界区后,使用者指针指向新修改后的副本,并且删除旧数据。

64、中断机制产生背景:与其轮询外设,不如让其主动发送一个信号。

65、中断注册API函数request_irq/request_threaded_irq()注册的是软件中断号(IRQ号),而不是硬件中断号。

66、软中断的执行时间点是在硬件中断返回前,即退出硬中断上下文时(硬中断处理结束后,返回到正常执行流之前),首先检查是否有等待的软中断,然后才检查是否需要抢占当前进程。

67、printk()默认有8个输出等级,只有输出等级高于4才会输出到终端或串口。

68、cat /proc/sys/kernel/printk查看输出等级

echo 8 > /proc/sys/kernel/printk 打开所有的输出信息

69、proc文件系统并不是真正意义上的文件系统,虽在内存中,但不占用磁盘空间。既可以向用户呈现内核中的信息,又可以用作用户空间向内核发送信息的一种手段。ps、top等命令就是从proc中读取信息的。

70、sys文件系统:对计算机上的所有设备统一的进行表示和操作,包括设备本身和设备之间的连接关系。sys主要目录:“

目录

描述

block

系统中的块设备

class

设备类模型

device

系统中的设备,根据类型分层

fs

系统中所有的文件系统

module

系统中所有模块

bus

将系统中的所有设备连接到某个总线

dev

字符、块设备主次设备号连接到真实设备的连接文件

71、debugfs文件系统:用于调试内核的文件系统。当需要在运行中修改某些内核数据,printk()略显无力,若是通过修改内存代码重新编译运行,过于低效,且有时系统无法重启。为此,可使用临时的文件系统将数据映射到用户空间。

72、当中断关闭后,CPU就不能响应其他事件,此时若有一个鼠标中断,只能在下一次开中断时才能响应,这段延迟为中断延迟。(即中断服务响应事件被延后了)

73、malloc/mmap并不会直接将虚拟内存与物理内存完成映射,只完成了虚拟内存的分配,此时未将所需物理内存块调入内存。在对该虚拟内存再次访问时,才会引发缺页中断,将外存(磁盘中)对应的内存块调入内存,完成虚拟内存和物理内存的映射。

74、objdump反汇编工具。

75、一般的内存访问错误:(1)越界访问(2)访问已经释放的内存(3)重复释放(4)内存泄漏(5)栈溢出。

76、kdump原理:基于kexec(kernel execution)实现。kexec可以快速启动新内核,跳过BIOS或bootloader等引导程序的初始化阶段。让系统上崩溃时快速切换到备份的内核,第一个内核(生产内核)的内存就得到了保留。在第二个内核(捕获内核)中,可以对第一个内核产生的崩溃数据继续分析(一般只有简单的初步分析,具体的分析要使用专门的工具,且在正常运行的系统中)。

77、生产内核指产品或线上服务器主要运行的内核,当生产内核崩溃时就会快速切换到捕获内核,将生产内核的完整信息(CPU寄存器、栈数据等)转储到指定文件中。接着可使用crash工具分析这个转储文件。

78、kdump使用的条件是系统能热启动,内存不丢失。若因硬件错误导致CPU无法启动,则kdump也无法适用。

79、与操作系统中用进程抽象描述处理器、进程地址空间抽象内存类似,文件是对磁盘等存储设备的抽象。

80、文件系统中重要的两个概念:文件和目录。

81、文件的类型有:(1)普通文件:分为文本和二进制文件(2)目录文件(3)特殊文件:如设备文件、sysfs节点、procfs节点等。

82、目录记录文件的位置,通常包括一组文件或其他一些子目录,目录由目录项组成。

83、inode结构:管理文件的信息,包括文件中包含的数据块、文件的大小、所有者、访问权限、创建时间、修改时间等信息。

84、文件系统的重要任务就是分配和索引数据块(磁盘块),以及确定数据块的位置。

85、ext2文件系统中有12个直接指针,直接指向12个数据块。还提供了一个一级指针,和一个二级指针,一级指针指向的块包含1024个指针,指向1024个数据块。二级指针又指向了包括1024个指针引用的数据块。

86、若每个数据块大小为4KB,则计算二级索引最大能支持多大文件时,将直接指针、一级指针、二级指针都考虑进去。只有直接指针和一级指针都用完了才会用二级指针。计算公式为:(12+1024+1024*1024)* 4KB,最后计算为4GB

87、虚拟化技术:利用虚拟化监控程序(Virtual Machine Monitor, VMM)在同一物理硬件上创建多个虚拟机。

88、虚拟化技术的缺点:(1)虚拟化管理程序和虚拟机本身的资源消耗较大。(2)虚拟机仍然是一个个独立的操作系统,对许多类型的业务应用,显得笨重,往往更关注业务应用的部署而非完整的操作系统。

89、容器技术:一种轻量级的虚拟化技术,通过CGroup和命名空间(namespace)机制实现资源隔离。

90、CGroup可以为特定的进程组限定可使用的资源。命名空间是Linux内核用来隔离内核资源的方式,通过命名空间可以让一些进程只看到与自己相关的一部分资源,另一组进程也只能看到它们自己相关的资源,这两组进程互相感受不到对方的存在。

91、Linux容器技术相比传统虚拟化有如下优势:(1)与宿主机使用同一个内核,性能损耗小。(2)不需要指令集模拟(3)可避免进入和退出VM时产生的消耗(4)与宿主机共享资源。

92、Docker:开源的应用容器引擎,开发者可打包应用和依赖库到可移植的镜像中,然后发布到任何流行的Linux发行版。


原文地址:https://blog.csdn.net/ZHUhapi/article/details/144328961

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!