本质上是一个软件程序
功能是对计算机上的软硬件进行管理
完整的操作系统:内核+外部应用
管理:先描述后组织
系统调用接口:操作系统内核向上提供的用于访问内核指定功能的接口
库函数:对系统调用接口进行封装,用起来更加方便
冯诺依曼:奠定了现代计算机的硬件体系结构
计算机硬件体系结构:
输入设备(键盘)、输出设备(显示器)、存储器(内存)、运算器、控制器(中央处理器-CPU)
内存 VS 硬盘:
内存–数据吞吐量大,但内存是易失性介质–断电数据丢失;
硬盘–持久化存储

cpu 想要处理数据,是从内存中取出数据进行处理的。
cpu 执行一个程序,首先就是将程序从硬盘加载到内存中
什么是进程?什么是程序?
程序:一堆指令+数据(硬盘上)
进程:运行中的程序,进程是系统对运行中程序动态运行过程的描述-PCB(进程控制块)
CPU 只有一个,但是想要运行的程序有很多,那 CPU 应该处理哪一个指令和程序呢?
因此提出了---- CPU 分时机制(多任务操作系统):由操作系统进行管理,CPU 可以一直处理数据,但是操作系统进行控制,CPU 处理一个程序很短一段时间,然后处理下一个程序,以快速的切换达到一种效果--多个程序好像在同时运行的效果。在切换过程中为保证程序能够在上一个运行截止时继续进行,需要使用操作系统对程序的运行过程进行描述,记录上一次运行的位置信息和数据信息。
PCB --- 进程控制块,在 Linux 下是一个 task_struct 结构体,操作系统调度管理程序运行就是通过 pcb 实现的。
对操作系统来说进程就是 pcb ,它是操作系统对程序运行过程的动态描述,通过 pcb 来实现对程序运行的调度管理(有 pcb 程序才能运行)。
pcb 中的描述信息:标识符 pid、内存指针(程序数据在内存中的地址)、程序计数器、上下文数据(切换前 CPU 寄存器中的数据会保存下来)、进程状态、IO信息...
每一个运行中的程序,都应该有一个状态,该状态标记了一个进程该如何被系统进行调度运行
状态:标记了当前的进程该如何被操作系统进行调度管理(表示一个进程什么状态该干什么事)
时间片:系统中的 cpu 分时机制,让每个程序只在 CPU 上执行一段很短的时间
就绪---准备好了,拿到时间片就可以运行
阻塞---某些特殊原因导致不能运行,满足某种条件之后,被置为就绪状态,拿到时间片切换到运行状态才能运行。
Linux 下进程状态:运行、可中断休眠态、不可中断休眠态、停止态、僵尸态
前台进程:指占据了一个终端的进行
后台进程:没有关联的终端,默默运行在系统中
ps -aux :查看所有进程的运行状态
正在被执行,以及拿到时间片就能被执行的状态
R+ :+表示前台进程
一种阻塞态(因为某种运行条件不足而暂时不能被调度运行的进程态度,例如 sleep(3) )
无法被中断打断阻塞,只能等待阻塞的唤醒条件满足之后才能被调度执行
什么都不做,休眠是阻塞,停止还可以被调度
进程退出运行了,但是资源没有完全被释放,等待处理的一种状态
(1)僵尸进程:处于僵尸态的进程。进程退出运行了,但是资源没有完全被释放,等待处理的一种状态,只有被它的父进程处理之后获取了退出返回值,才能被释放资源
echo $? :查看进程退出的原因
(2)fork:创建一个子进程 pid_t fork(void):通过复制调用父进程来创建一个新进程(子进程),创建一个进程其实就是创建一个 task_struct 结构体,也就是创建一个 pcb
返回值:在父进程中返回子进程 PID,子进程中返回 0 ,若失败返回 -1
分辨父子进程:

复制父进程意味着创建新的 task_struct 里边大部分数据都是从父进程 pcb 中复制过来的

父进程与子进程平等,不存在子集的关系,一个进程只有一个父进程,但是可以有多个子进程。
注意:
父子进程的运行顺序没有特定,系统调度到谁就先运行谁
父子进程的调用顺序没有特定:


(3)僵尸进程:
子进程先于父进程退出,为了保存退出原因给父进程,因此退出后没有完全释放资源成为僵尸进程
若父进程也退出了,这时候僵尸子进程就失去了存在的意义
孤儿进程:父进程先于子进程退出,造成子进程变成了孤儿进程
(4)僵尸进程的危害:
资源没有被完全释放------------资源泄漏
创建一个进程是为了完成一项任务,多个任务可以创建多个进程来处理,一个进程完成任务之后就退出形成僵尸进程
(5)如何避免僵尸进程:
进程等待----等待子进程退出,获取子进程退出的返回值,释放子进程资源避免僵尸进程
父进程先于子进程退出,形成孤儿进程
孤儿进程特性:运行在后台(此时的子进程还会继续正常运行);父进程成为 1 号进程(以前是 init 进程,现在是 systemd)(服务器程序都是运行在后台------不受影响)
孤儿进程退出后不会形成僵尸进程
特殊的孤儿进程,是运行在后台的一种特殊进程,脱离控制终端并且周期性的执行某种任务或等待某种事情的发生。脱离终端是为了避免进程在执行过程中的信息在任何终端上显示,并且进程也不会被任何终端产生的中断信号所终止。
终端 shell 中有两种变量:环境变量、普通变量:环境变量具有进程间的继承特性
在程序中访问环境变量:
(1)char* getenv(char* name)
(2)main 函数的第三个参数
main(int argc,char* argv[] ,char* env[])
argc :程序运行参数的个数
argv:字符数组,逐个保存程序运行参数
(3)全局变量:
extern char** environ;
环境变量:查看所有环境变量 env
保存了当前程序运行环境的变量;通过环境变量可以给运行的程序传递数据
echo:打印指定内容到终端显示(变量内容)
echo $(PATH) 打印 PATH 变量内容
echo $value 打印变量 value 的值
echo “nihaoa” 打印 nihaoa 到终端

export:声明一个变量为环境变量
set:查看所有变量(包含普通变量和环境变量)
unset:删除一个变量(包含环境变量)

内存地址空间:对内存区域进行编号(以字节为单位)
程序地址空间更应该称为:进程地址空间(因为运行起来的程序才会占据内存)
在一个进程中,所访问到的所有变量地址都是一个虚拟地址(并非真正的物理内存地址)
进程并不直接访问物理地址,而是通过访问虚拟地址,进而映射访问物理地址来进行
(1)实现数据在物理内存上的离散式存储,提高内存利用率;
(2)实现内存访问控制
程序地址空间其实是操作系统为每一个进程描述的一个虚拟的地址空间,进程中每个变量的地址其实都是虚拟地址,虚拟地址经过页表映射之后得到物理地址进而访问物理内存。
虚拟地址空间是一个结构体:mm_struct,系统通过该结构体向每个进程描述了一个虚拟的、连续的、完整的、线性的地址空间
(一)分段式内存管理
将一个整体的地址空间划分为多个段(代码段、数据段、堆区、栈区…);
优点:更加利于编译器对地址的管理
关键要素:段表、地址组成
虚拟地址的组成:段号 、段内偏移量
段表:一种数据结构,其中描述的信息–段号(物理内存的一个起始地址)
通过段号找到段表项,得到一块物理内存的起始地址,物理内存起始地址+偏移量=实际存储数据在物理内存中的位置
(二)分页式内存管理
将一个整体的地址空间划分为大量的小的分页page(当前一般默认都是 4096 字节为一页);
优点:实现数据的离散式存储,提高内存利用率
关键要素:页表(页号,物理内存起始地址、缺页中断位、访问权限位)+虚拟地址组成(页号+页内偏移)
通过虚拟地址中的页号,在页表中找到对应的页表项,得到一个物理内存的起始地址,加上页内偏移=实际要访问的位置

缺页中断:
当进程要访问一块内存时,经过页表映射的时候发现缺页中断位被置位(这个地址原先的数据不在内存内),则触发缺页中断。
缺页中断触发之后需要重新进行内存置换,将数据从交换分区(swap)置换到内存中进行访问。
内存置换算法:
内存不够时将部分数据移除放到交换分区来进行存储
典型算法:LRU—最久未使用
LFU—最少未使用
一旦产生大量内存置换,则运行效率会大大降低,因为硬盘的数据交互吞吐量很小。
(三)段页式内存管理
先将地址空间进行分段,然后在每个分段内使用分页进行管理
ps:有问题欢迎评论留言~