ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux操作系统总结报告

2021-05-18 20:34:15  阅读:236  来源: 互联网

标签:RW +------------------------------+ 操作系统 总结报告 -- 虚拟地址 中断 Linux 进程


目录

前言

操作系统是管理计算机硬件的一种大型软件,我们所有运行的日常软件都基于操作系统之上。操作系统本质上也是软件,也处处体现着软件设计的本质思想,比如:抽象,虚拟,中间层等。从其功能等几大部分来看,内存管理,进程管理,I/O设备,文件管理等等,都具有抽象和虚拟特征,并且操作系统本身就是一大中间层,介于应用软件和硬件中间,平滑我们与硬件的联系。

内存管理

对接物理层面

操作系统的底层与主存对接,对主存空间按页划分,每一页放在比如pageinfo的struct里,这样我们有了所有内存物理页的信息。在这一层面,操作系统着重于对内存的使用与分配,目的是使得内存得以充分使用,减少外部碎片(内部碎片问题由分页机制解决,最大内部碎片不会超过一页)。

与现实使用之间的矛盾

如果直接在实模式下编程,直接面对物理空间的话,可能会面临没有足够大的连续内存块的问题,实际上多个小的内存块加起来是够用的,这就意味着我们需要离散地去使用。虽然这样也并不难实现,但是对用户来说,空间的使用丧失逻辑性,我们使用一个连续的数组,顺序遍历时,地址可能要跳着走,会造成使用的不方便。
同时,还会面临安全问题,因为直接使用物理地址,意味着我们可以使用任意一块地址,这可能是别的用户的,或别的进程的,甚至可能是内核的。而操作系统在管理这些内存时,是根据内存空间合理分配的一块空间,并不会根据你是什么进程/用户/等情况来分配的,即第 i 块内存可能是进程P1的,而i + 1块可能是进程P9的。所以操作系统不会在此处标记属于谁的,这管理太麻烦了,而且这样设计不够单一性。

段页机制和虚拟地址

虚拟地址的设计,便是符合抽象,虚拟的。它是对内存空间的地址做了逻辑化的展现,背后与物理页的关联交给其映射机制,硬件上由MMU处理。比如一个4G的地址空间大概如下:

/*
 * Virtual memory map:                                Permissions
 *                                                    kernel/user
 *
 *    4 Gig -------->  +------------------------------+
 *                     |                              | RW/--
 *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *                     :              .               :
 *                     :              .               :
 *                     :              .               :
 *                     |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/--
 *                     |                              | RW/--
 *                     |   Remapped Physical Memory   | RW/--
 *                     |                              | RW/--
 *    KERNBASE, ---->  +------------------------------+ 0xf0000000      --+
 *    KSTACKTOP        |     CPU0's Kernel Stack      | RW/--  KSTKSIZE   |
 *                     | - - - - - - - - - - - - - - -|                   |
 *                     |      Invalid Memory (*)      | --/--  KSTKGAP    |
 *                     +------------------------------+                   |
 *                     |     CPU1's Kernel Stack      | RW/--  KSTKSIZE   |
 *                     | - - - - - - - - - - - - - - -|                 PTSIZE
 *                     |      Invalid Memory (*)      | --/--  KSTKGAP    |
 *                     +------------------------------+                   |
 *                     :              .               :                   |
 *                     :              .               :                   |
 *    MMIOLIM ------>  +------------------------------+ 0xefc00000      --+
 *                     |       Memory-mapped I/O      | RW/--  PTSIZE
 * ULIM, MMIOBASE -->  +------------------------------+ 0xef800000
 *                     |  Cur. Page Table (User R-)   | R-/R-  PTSIZE
 *    UVPT      ---->  +------------------------------+ 0xef400000
 *                     |          RO PAGES            | R-/R-  PTSIZE
 *    UPAGES    ---->  +------------------------------+ 0xef000000
 *                     |           RO ENVS            | R-/R-  PTSIZE
 * UTOP,UENVS ------>  +------------------------------+ 0xeec00000
 * UXSTACKTOP -/       |     User Exception Stack     | RW/RW  PGSIZE
 *                     +------------------------------+ 0xeebff000
 *                     |       Empty Memory (*)       | --/--  PGSIZE
 *    USTACKTOP  --->  +------------------------------+ 0xeebfe000
 *                     |      Normal User Stack       | RW/RW  PGSIZE
 *                     +------------------------------+ 0xeebfd000
 *                     |                              |
 *                     |                              |
 *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *                     .                              .
 *                     .                              .
 *                     .                              .
 *                     |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
 *                     |     Program Data & Heap      |
 *    UTEXT -------->  +------------------------------+ 0x00800000
 *    PFTEMP ------->  |       Empty Memory (*)       |        PTSIZE
 *                     |                              |
 *    UTEMP -------->  +------------------------------+ 0x00400000      --+
 *                     |       Empty Memory (*)       |                   |
 *                     | - - - - - - - - - - - - - - -|                   |
 *                     |  User STAB Data (optional)   |                 PTSIZE
 *    USTABDATA ---->  +------------------------------+ 0x00200000        |
 *                     |       Empty Memory (*)       |                   |
 *    0 ------------>  +------------------------------+                 --+
 *

从上至下大致为:内核区,cpu对应的内核栈,与用户公共访问的区域(mmio,页表,进程等),用户区域(栈,堆,data,bss,text等)
可见其特征是符合逻辑的,是线性的。整个空间是分成一段段的,每个段的起始地址,和段长度需要记录,这就用到了段机制。

从段表到页表,再到虚拟地址,最后到物理地址的过程

linux的可执行文件一般是ELF格式,其逻辑上是按照段的方式存储的,data段,text段等等,每个段前有一个结构存储其长度,和加载的位置,即虚拟地址:

struct Proghdr {
	uint32_t p_type;
	uint32_t p_offset;
	uint32_t p_va;
	uint32_t p_pa;
	uint32_t p_filesz;
	uint32_t p_memsz;
	uint32_t p_flags;
	uint32_t p_align;
};

这些段按照上述信息,能放入恰当的虚拟地址位置,并且会把段信息记录在LDT表中
在进程运行时,会把这些段的选择子放入寄存器中,这些寄存器会在LDT中找出段描述符,其中会有段的起始地址(虚拟地址),之后根据段偏移组合成数据的虚拟地址,再通过页表机制,找到其对应的物理地址。

段页机制的安全性

以上的虚拟地址都只是对一个进程有用的,每个进程的虚拟地址空间是不同的(除了内核的部分),这个虚拟地址只会映射到 程序刚加载进来时分配的内存。此时虚拟地址空间就像一个中间层,使得物理地址空间对用户不透明,用户根本不能知道其他用户/进程的物理地址。
同时,段之间是被隔离开了,访问一个内存时,都要经过一个段选择+段偏移的过程,这个由操作系统来完成,使得用户不会用错段。
虽然好像用户可能会通过修改虚拟地址,轻易地去访问内核区,但是这在保护模式下,内核区需特权级0,才能访问,用户是访问不到的。同时页表项pte的最后12位是flag位,标记了用户的访问权限的。

中断与异常

这里的中断指硬件中断,异常是process exception,由cpu发出的中断。
虽然多核可以实现真正意义上的并行,但是我们同时需要运行的进程太多,可能上几千个,靠硬件是远远不够的,所以需要并发,而并发的关键就是中断。
硬件中断的意义是:响应硬件中断的请求,尽快让其运行,因为硬件是与cpu并行的。
异常分为:故障,陷阱,错误。除了错误,会终止进程外,其余的本质上是,在正常的任务运行过程中,出现了需要暂时停下来去处理的事情。
对于并发:并发就是为了能够让任务A停一下,再让B做一会,这肯定是需要中断了,比如典型地是用时钟中断,当一个进程时间片用完,便去切换下一个进程。

中断的过程

我们的外设,会经常发出中断的请求,目的是为了让其得到cpu的响应,来处理准备事务或读取结果等。

  • 外设的端口上会有存放irq请求,其会通过总线发出,当其没有被屏蔽,且CPU来查询时,中断请求会正式发出:
  • 随后便会进入排队器,进入排队器的都是有资格争用cpu的,这时候根据中断优先级,来选出“胜者”:
  • 随后排队器的结果输出到中断向量形成部件,生成向量地址:
  • 之后会在IDT表中找到对应的表项,其中有段选择符,offset,在GDT表中找到对应的段,然后根据offset找到入口地址;
    这个地址便是handler的地址,在idt init的时候,用SETGATE设置好了idt中的表项,里面便存了handler的selector。
  • 随后的代码流程:
    • handler汇编内容:
#define TRAPHANDLER_NOEC(name, num)					\
	.globl name;							\
	.type name, @function;						\
	.align 2;							\
	name:								\
	pushl $0;							\
	pushl $(num);							\
	jmp _alltraps

这是定义handler的宏汇编内容,因为irq是没有错误号,的所以push了0

  • 之后跳去_alltraps:
_alltraps:
	pushl %ds
	pushl %es
	pushal 

	movl $GD_KD, %eax
	movw %ax, %ds
	movw %ax, %es

	push %esp
	call trap

主要是保存现场,并调用trap函数

  • trap:如果是陷入指令,这就是从用户态陷入内核态的地方,之后会调用trap_dispatch函数
  • trap_dispatch:会根据传入的trapframe里的trapno(中断号),来选择不同的中断处理函数
  • 异常与系统调用
    异常的处理与上述雷同,只不过少了前期硬件处理阶段,但会传入错误号。
    而系统调用,是先进入syscall中断函数后,根据eax寄存器里的系统调用号选择不同的系统调用函数

进程管理

进程的切换,主要是由schedule通过算法得出下一个运行的进程,算法主要由Robin-Round,FIFO,Normal等。之后使用switch-to进行切换,在之前,会切换到内核栈,会由硬件push进cs, ip, ss, sp, eflag等寄存器值,之后由指令SAVE_ALL保存余下诸如通用寄存器等寄存器。随后该进程进入就绪队列,而新的进程进来,先进入其内核栈,将其保存的寄存器值pop到寄存器,即restore_all,然后硬件恢复保存的内容,最后切换至用户栈,进入用户态运行。

文件管理

VFS又是一种虚拟与抽象,并且是介于用户系统调用与文件操作之间的中间层。关系图如下:

VFS是所有文件系统所面对的一种接口,它提供了最一般的组织形式,具体数据结构如下:

  • 系统里会有一个系统文件打开表,里面是用双链表将file对象连接起来,file里有文件的打开次数,读写位置,dentryfile_operations等。其中file_operations对应具体的文件操作实现。
  • dentry是一个目录项,它有文件最基本的信息,以及inode的指针
  • inode存放数据的地址等信息,如果这个文件是个目录,那么这个数据是其子目录/文件的目录项信息
  • 一个进程里会有一个文件打开表,会用fd_array存放dentry的指针,而对应的fd文件描述符即fd_array的下标
  • fs_struct存放文件系统的信息,如根目录的dentry,当前目录的dentry

驱动设备程序

使用设备

这里存在对文件的进一步抽象,即所有的设备都会用一个文件来代表。用操作文件的方法,实现对设备的操作,因为操作设备实际上就是对端口的编程,而这和对磁盘的操作如出一辙。

驱动程序

file的操作函数,需要驱动程序的进一步的解释,来让设备识别

案例

因为redis比较注重性能,下面都以它举例。
在redis里,它会频繁地计算时间用于不同用途,比如10s一次更新LRU时钟,更新日志,计算服务器上线时间等,这需要获取系统的时间,那么必然要需要使用系统调用,如上所述,系统调用要触发中断,中断需要保护现场,完了还要恢复现场,需要耗费一些时间,但如果频繁的使用势必影响高性能服务器的性能。
为了不那么频繁地使用系统调用,对于一些对精度要求不高的功能,redis采用了服务器时间缓存的方法,即100ms获取一下系统时间,存在unixtime里,当其他功能需要使用时,直接读取unixtime,而不是使用系统调用。
redis采用了单线程模型,主要是考虑了线程切换的代价,它采用了epoll/kqueue等多路复用的方法,去监听多个socket,而不是一个socket创建一个线程。考虑到linux里没有线程,它是用进程模拟的,所以可以从进程切换角度来解释。由上面的论述可得知,进程在切换时,需要保护现场,切换三次栈,第一次是pre进程进入内核栈,之后切换至next进程的内核栈,最后进入next的用户栈,等等,开销很大。一个redis服务器在使用时,可能会面临巨量的socket连接,这种用线程的方式无疑会严重影响性能,即使使用线程池,也只是省去创建和撤销线程的开销,切换线程代价并没有省去。所以redis用单线程,会大大提高性能表现。

标签:RW,+------------------------------+,操作系统,总结报告,--,虚拟地址,中断,Linux,进程
来源: https://www.cnblogs.com/huth/p/14782142.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有