〇、操作系统基础
1、什么是操作系统
操作系统(Operating System, OS),介于硬件资源和应?程序之间的?个系统软件。
2、操作系统的功能
操作系统位于硬件资源之上,管理硬件资源;应?程序之下,为应?程序提供服务,同时管理应?程序。
1.进程管理:
①进程控制:创建和撤销进程,分配资源、资源回收,控制进程运行过程中的状态转换。
②进程同步:多进程运行进行协调–进程互斥(临界资源上锁)、进程同步。
③进程通信:实现相互合作之间的进程的信息交换。
④调度:作业调度,进程调度。
2.内存管理:
为多道程序的运行提供良好的环境,提高存储器的利用率,方便用户使用,并能从逻辑上扩充内存。
①内存分配:静态分配、动态分配。
②内存保护:各在其内存空间内运行(设两界限寄存器),互不干扰。
③地址映射:地址空间中的逻辑地址转换为内存空间中与之对应的物理地址。
④内存扩充:借助于虚拟存储技术,逻辑上扩充内存容量。
3.设备IO管理:
完成用户进程提出的 I/O 请求,为其分配所需I/O设备,完成指定I/O操作;提高CPU和I/O设备的利用率,提高I/O速度,方便用户使用I/O设备。
①缓冲管理
②设备分配
③设备处理:设备驱动程序,用于实现CPU和设备控制器之间的通信。
4.文件管理:
对用户文件和系统文件进行管理以方便用户使用,并保证文件的安全性。
①文件存储空间的管理:为文件分配合理外存空间,文件存储空间的使用情况。
②目录管理:为每个文件建立一个目录项。
③文件的读/写管理和保护
1. 资源分配,资源回收
计算机必要重要的硬件资源??就是 CPU、内存、硬盘、I/O设备。
?这些资源总是有限的,因此需要有效管理,资源管理最终只有两个问题:资源分配、资源回收。
- 资源分配: 体现在CPU上,?如进程调度,多个进程同时请求CPU下,应该给哪?个进程呢?如内存分配,内存不够了怎么办进程?法访问了B进程的内存地址怎么办存内、外碎?问题等。
- 资源回收: 考虑内存回收后的合并等等。
2. 为应用程序提供服务
操作系统将硬件资源的操作封装起来,提供相对统?的接?(系统调?)供开发者调?。
如果没有操作系统,应?程序将直接面对硬件,除去给开发者带来的编程困难不说,直接访问硬件,使?不当极有可能直接损坏硬件资源。
3. 管理应用程序
即控制进程的?命周期:进程开始时的环境配置和资源分配,进程结束后的资源回收、进程调度等。
4. 操作系统内核的功能
(1)进程调度能力: 管理进程、线程,决定哪个进程、线程使?CPU。
(2)内存管理能力: 决定内存的分配和回收。
(3)硬件通信能力: 管理硬件,为进程和硬件之间提供通信。
(4)系统调用能力: 应?程序进行更?限权运行的服务,需要系统调用,?户程序和操作系统之间的接口
3、用户程序与操作系统的关系(相互调用)
-
操作系统的角度
计算机启动后启动的第?个软件就是操作系统,随后启动的所有进程都运行在操作系统之上,使?操作系统提供的服务,同时被操作系统监控,进程结束后也由操作系统回收。 -
进程角度
调用操作系统提供的服务,实现自己的功能。
4、Linux 常见命令大全
5、Linux 内核态 和 用户态
- 内核空间和用户空间
操作系统的核心是内核(kernel),它独立于普通的应用程序,负责管理系统的进程、内存、设备驱动程序、文件和 络系统,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。
为了保证内核的安全,现在的操作系统一般都强制用户进程不能直接操作内核。
在 32 位的操作系统的虚拟地址空间中,最高的 1G 字节(从虚拟地址 0xC0000000 到 0xFFFFFFFF)由内核使用,称为内核空间。而较低的 3G 字节(从虚拟地址 0x00000000 到 0xBFFFFFFF)由各个进程使用,称为用户空间。
因此,最高 1G 的内核空间是被所有进程共享的,只有剩余的 3G 才归每个进程自己使用。
- 区分内核空间和用户空间的原因
CPU 的指令分为特权指令和非特权指令,有些指令使用不当会非常危险,比如清内存、设置时钟、修改用户访问权限、分配系统资源等等,可能导致系统崩溃。
当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态。只有处于内核态的进程才能使用特权指令和非特权指令,即内核态进程可以调用系统的一切资源;而用户态进程只能使用非特权指令,也就是说用户态进程只能执行简单运算,不能直接调用系统资源。
CPU中有一个程序状态字PSW(Program Status Word),标志着线程的运行状态。用户态和内核态对应着不同的值,用户态为3,内核态为0。
对于 Linux 来说,通过区分内核空间和用户空间的设计,隔离了操作系统代码与应用程序代码(操作系统的代码要比应用程序的代码健壮很多)。即便是单个应用程序出现错误也不会影响到操作系统的稳定性,这样其它的程序还可以正常的运行。
「所以,区分内核空间和用户空间本质上是要提高操作系统的稳定性及安全性。」
- 如何从用户空间进入内核空间
其实所有的系统资源管理都是在内核空间中完成的。比如读写磁盘文件,分配回收内存,从 络接口读写数据等等。
我们的应用程序是无法直接进行这样的操作的。但是我们可以通过内核提供的接口来完成这样的任务。
比如应用程序要读取磁盘上的一个文件,通过一个特殊的指令让进程从用户态进入到内核态(到了内核空间),在内核空间中,CPU 可以执行任何的指令,当然也包括从磁盘上读取数据。具体过程是先把数据读取到内核空间中,然后再把数据拷贝到用户空间并从内核态切换到用户态。
从用户态切换到内核态的方式有三种:
- 系统调用
这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,而其核心是使用了操作系统为用户特别开放的一个**(软)中断**来实现。
进程调用:exit、fork
文件系统访问:chmod、chown
设备调用:read、write
信息读取:读取设备信息
通信:mmap、pipe等
- 异常
当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
- 外设中断
当外设完成用户的请求时,会向CPU发送中断信 。
一、进程 和 线程
我们编译的代码可执行文件只是储存在硬盘的静态文件,运行时被加载到内存,CPU执行内存中指令,这个运行的程序被称为进程。
进程是对运行时程序的封装,是操作系统进行资源(CPU、内存等)调度和分配的基本单位。
线程是CPU调度和分配的基本单位(程序执行的最小单位)。
1、进程与线程的区别与联系
-
拥有资源:进程是资源分配的基本单位,而线程是CPU分配和调度的基本单位。
进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。 - 调度:线程是实现独立调度的基本单位。在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
- 系统开销:由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
- 通信:线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC (Inter-Process Communication)。
2、 Linux理论上最多可以创建多少个进程个进程可以创建多少线程,和什么有关
因为进程的 pid 是用 来表示的,pid_t 的最大值是 32768,所以理论上最多有32768个进程。
至于线程,进程最多可以创建的线程数是根据分配给调用栈的大小,以及操作系统位数(32位和64位不同)共同决定的。Linux32位下是300多个。
3、并发与并行
-
单个核心在很短时间内分别执行(轮流执行)多个进程,称为并发(concurrency)。
对于并发来说,CPU需要从?个进程切换到另?个进程,这个过程需要保存进程的状态信息。 - 多个核心同时执行多个进程称为并行(parallel)。
4、进程的执行过程
进程的执行过程需要经过三大步骤:编译,链接和装入。
-
编译:将源代码编译成若干模块;
-
链接:将编译后的模块和所需的库函数进行链接;
链接包括三种形式:静态链接,装入时动态链接(将编译后的模块在链接时一边链接一边装入),运行时动态链接(在执行时才把需要的模块进行链接) -
装入:将模块装入内存运行。
将进程装入内存时,通常使用分页技术,将内存分成固定大小的页,进程分为固定大小的块,加载时将进程的块装入页中,并使用页表记录。减少外部碎片。
通常操作系统还会使用虚拟内存的技术将磁盘作为内存0-的扩充。 -
把 main 函数的入口地址写入到下一行指令寄存器中
5、进程的状态和转换图
(1)执行:进程分到CPU时间片,正在执行
(2)就绪:进程已经就绪,只要分配到CPU时间片,随时可以执行
(3)阻塞(等待):有IO事件或者等待其他资源(请求I/O,申请缓冲空间等);阻塞态的进程占?着物理内存,但无法参与系统进程调度。
(3.1)可中断睡眠状态:也称为浅度睡眠,表示睡的不够“死”,还可以被唤醒,一般来说可以通过信 来唤醒;
(3.2)不可中断睡眠状态:也称为深度睡眠,深度睡眠无法被信 唤醒,只能等待相应的条件成立才能结束睡眠状态。
(4)新建:进程刚被创建时的状态,尚未进入就绪队列。创建步骤包括:申请空白的 PCB,向 PCB 中填写一些控制和管理信息,系统向进程分配运行时所需的资源。
(5)终止:进程完成任务到达正常结束点,或出现无法克服的错误而异常终止,或被操作系统及有终止权的进程所被终止时所处的状态。
(6-7)挂起:把阻塞的进程置换到磁盘中,此时进程未占用物理内存,我们称之为挂起;挂起不仅仅可能是物理内存不足,比如sleep系统调用,或用户执行Ctrl+Z也可能导致挂起。
–(6)就绪挂起:进程在外存(硬盘),但只要进入内存,马上运?。
–(7)阻塞挂起:进程在外存(硬盘)并等待某个事件的出现(进入就绪挂起态)。
-
三态模型
-
七态模型:新建态、就绪挂起态、就绪态、运行态、阻塞态、阻塞挂起态、终止态
-
.text 代码段:
用来存放程序执行的二进制代码,也有可能包含一些只读的常数变量(字符串常量等)。
该段内存为静态分配,只读(某些架构可能允许修改)。
这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个text段。 -
.data 数据段:
也叫GVAR(global value),用来存放程序中已初始化的全局/静态变量;静态分配。 -
.bss 数据段:
存放程序中未初始化的全局/静态变量。静态分配,在程序开始时通常会被清零。
代码段和初始化数据段存放在程序可执行文件中,在编译时已经分配了空间;
而 .bss 段并不占用可执行文件的大小,它是由链接器来获取内存的。-
heap 堆区:
用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
堆是从低地址位向高地址位增长,采用链式存储结构。
进程调用malloc / free等函数进行动态分配和手动释放;频繁操作会造成内存空间的不连续,产生碎片。
堆空间的大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。 -
stack 栈区:
存放程序临时创建的局部变量(static声明的变量存放在数据段);除此以外,在函数被调用时的参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中(动态分配)。
堆是从高地址位向低地址位增长。
内存由编译器自动分配和释放,是一块连续的内存区域。
栈空间的最大大小在编译时确定,速度快,但自由性差,最大空间不大(2M)。
每个线程都会从内存区域中划分出自己的栈和代码段,但是其他空间是共用的。
18、线程有哪几种状态
- 就绪(Runnable): 线程准备运行, 不一定立马就能开始执行
- 运行中(Running):进程正在执行线程的代码
- 等待中(Waiting):线程处于阻塞的状态, 等待外部的处理结束
- 睡眠中(Sleeping):线程被强制睡眠
- I/O阻塞(Blocked on I/O):等待I/O操作完成
- 同步阻塞(Blocked on Synchronization):等待获取锁
- 死亡(Dead):线程完成了执行
19、内存映射的内存空间在进程结束后会自动释放吗/h3>
映射的内存在用户区堆和栈之间的共享库中,进程结束后会自动释放。
20、创建共享内存后,进程结束,共享内存是否会消失/h3>
不会。进程间通信使用的管道、socket、共享内存、消息队列、信 量等,是属于内核级的,一旦创建后就由内核管理,若进程不对其主动释放,那么这些变量会一直存在,除非重启系统。
21、进程结束后打开的文件描述符会自动关闭吗/h3>
会,每个进程的PCB也都不一样,维护的文件描述符表也不一样。
22、wait() 函数
wait 函数只有两种返回值,即成功则返回终止的子进程对应的进程 ;失败则返回 -1。
如果其所有子进程都还在运行,则wait()会一直阻塞等待,直到某一个子进程终止,然后返回该子进程的进程 ;
如果该进程并没有子进程,也就意味着该进程并没有需要等待的子进程,那么 wait() 将返回错误,也就是返回 -1,并且会将 errno 设置为ECHILD。
23、atexit() 函数
atexit 函数是 linux标准 I/O 自带的库函数,使用该函数需要包含头文件
;
用于注册一个进程在正常终止时要调用的函数,我们可以通过该函数打印或者执行一些其他功能代码。该函数原型是:;
function:函数指针,指向注册的函数,此函数无需传入参数、无返回值。
返回值:成功返回0;失败返回非0。24、Linux 的特殊进程(PID = 0, 1, 2 …)
PID 为 0 的是调度进程,该进程是内核的一部分,也称为系统进程;
PID 为 1 的是 init 进程,它是由内核启动的第一个用户进程,以超级用户特权运行,管理着系统上所有其它进程,因此理论上说它没有父进程。它是所有子进程的父进程,一切从1开始、一切从init进程开始!
PID 为 2 的是页守护进程,负责支持虚拟存储系统的分页操作。
25、单例模式运行
在linux中,对于有些程序设计来说,程序只能被执行一次,只要该程序没有结束,就无法再次运行,我们把这种情况称为单例模式运行。
譬如系统中守护进程,这些守护进程一般都是服务器进程,服务器程序只需要运行一次即可,能够在系统整个的运行过程中提供相应的服务支持,多次同时运行并没有意义、甚至还会带来错误!
多核和单核CPU对进程运行几次没有关系。26、实时信 和非实时信 、可靠信 和不可靠信
在Linux系统中,
从可靠性方面将信 分为可靠信 和不可靠信 ;
从时间关系上将信 分为实时信 和非实时信 。前31个信 编 (1~31)为非实时信 ,等同于不可靠信 ,不支持排队;
其他(32~64)为实时信 ,等同于可靠信 ,都支持排队。Linux信 机制基本上是从Unix系统中继承过来的,早期不可靠信 的主要问题是:
进程每次处理信 后,就将对信 的响应重置为默认动作。在某些情况下,将导致对信 的错误处理;因此,用户如果不希望这样的操作,那么就要在信 处理函数结尾再一次调用 signal(),重新安装该信 。
因此,早期unix下的不可靠信 主要指的是进程可能对信 做出错误的反应以及信 可能丢失。
Linux支持不可靠信 ,但是对不可靠信 机制做了改进:在调用完信 处理函数后,不必重新调用该信 的安装函数(信 安装函数是在可靠机制上的实现)。
因此,Linux下的不可靠信 问题主要指的是信 可能丢失,因为这些不可靠信 阻塞的时候是不支持排队的,即未决信 集只有一个 0 或 1 的标记位,不能记录相关信 的触发次数。
信 值位于SIGRTMIN和SIGRTMAX(32~64)之间的信 都是可靠信 ,可靠信 克服了信 可能丢失的问题。
信 的可靠与不可靠只与信 值有关,与信 的发送及安装函数无关。
目前Linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信 ,在信 处理函数的结尾也不必再调用一次信 安装函数。同时,由signal()安装的实时信 支持排队,同样不会丢失。27、进程的特性和构成
- 动态性:进程的实质是程序的一次执行过程,进程是动态产生、动态消亡的。
- 并发性:是指多个进程实体同存于内存中,任何进程都可以同其他进程一起并发执行。
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
- 结构性:进程由程序段、数据段和PCB组成。
程序段、相关数据段和PCB三部分构成进程的实体,一般简称为进程。
所谓创建进程就是创建进程实体中的PCB,而撤销进程也就是撤销进程的PCB。28、线程池
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些 “池化资源”技术 产生的原因,线程池为线程生命周期开销问题和资源不足问题提供了解决方案。
线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
创建线程池时可以设置线程池的最大线程数和最小线程数;
当任务队列当中没有任务时,线程池阻塞在条件变量上,等待任务;
当有任务进来时,条件变量发信 或者广播,唤醒线程,此时对任务队列而言属于共享资源,需要使用互斥量,避免资源冲突。线程池的伸缩性对性能有较大的影响。
1、创建太多线程,将会浪费一定的资源,有些线程未被充分使用。
2、销毁太多线程,将导致之后浪费时间再次创建它们。
3、创建线程太慢,将会导致长时间的等待,性能变差。
4、销毁线程太慢,导致其它线程资源饥饿。线程池的主要组成部分:
1、线程池管理器(ThreadPoolManager):用于创建并管理线程池;
2、工作线程(WorkThread):线程池中线程;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行;
4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。线程池的应用场景:
1、需要大量的线程来完成任务,且完成任务的时间比较短;
2、对性能要求苛刻的应用;
3、接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。29、产生信 的情况
在linux中,信 是事件发生时对进程的通知机制,也可以把它称为软件中断。一个具有合适权限的进程能够向另一个进程发送信 ,产生信 的情况包括:
-
硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信 给相关进程。
硬件检测到异常的例子包括执行一条异常的机器语言指令,诸如,除数为0、数组访问越界导致引用了无法访问的内存区域等,这些异常情况都会被硬件检测到,并通知内核、然后内核为该异常情况发生时正在运行的进程发送适当的信 以通知进程。 - 用于终端下输入了能够产生信 的特殊字符。
譬如在终端上按下CTRL + C组合按键可声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!
-