操作系统:
操作系统是一个管理计算机软硬件资源的软件
操作系统=操作系统内核+一组应用
操作系统管理软硬件资源: 描述+组织
描述 :用struct结构体
组织: 用链表或其他高效的数据结构
程序和进程的区别:
程序是一个静态的概念,它就是一些预先编译好的指令和数据集合的文件(一个文本文件)
进程是一个动态的概念,是程序运行时的一个过程
描述进程:
进程被放到一个叫做进程控制块的数据结构中(PCB)。
Linux中PCB是task_struct
进程状态:就绪状态,阻塞状态,运行状态
task_struct内容分类:
标识符:描述本进程的唯一标识符,用来区别其他进程。
状态:任务状态、退出码、退出信号等。
优先级:相对于其他进程的优先级
内存指针:程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针(指向进程地址空间 mm_struct)
程序计数器:保存程序中即将被执行的下一条指令的地址。
上下文信息:保存进程执行时处理器的寄存器中数据
I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息 :可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
其他信息
CPU密集型程序:程序大量的时间都在使用CPU进行计算
IO密集型程序:程序大量时间都在和磁盘打交道,进行IO
并行:在同一时间,多个进程,每一个进程都拥有一个CPU 进行运算
并发:多个进程,只拥有少量的CPU,每个进程只能独占CPU一小会, 就会让出CPU供其他进程运算
系统调用:操作系统提供的函数,称之为系统调用函数
库函数:封装了系统调用函数,提供了一些封装后的函数供程序员去使用,被封装的函数称之为库函数
组织进程:
可以在内核源代码里找到它,所有运行在系统里的进程都以task_struct链表的形式存在内核里
创建子进程
使用fork函数
- fork有两个返回值
- 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
pid_t fork (void)
pid_t相当于int
失败:返回小于0的值
成功:
0,返回给子进程
大于0 ,返回子进程的pid给父进程
fork失败原因:内存不足,创建PCB是需要内存的(内核)
获取当前进程的pid,使用getpid函数
获取当前进程的父进程的pid,使用getppid函数
写时拷贝:当数据发生数据修改的时候,才分配给一个物理内存,并将页表当中的映射关系改掉。
当父进程定义了一个全局变量,fork出来一个子进程,当子进程发生对该全局变量发生修改的时候,操作系统会开辟一段空间来保存子进程更改后的值,并且子进程会更改自己的页表映射关系
进程状态
R | 运行状态 |
S | 睡眠状态(可被打断) |
D | 磁盘睡眠状态(不可被打断) |
T | 暂停状态(命令:Ctrl+z) |
t | 跟踪状态 |
Z(zombie) | 僵尸状态 |
X | 死亡状态 |
R+前台进程 | R后台进程 |
. /[可执行程序 ] 将进程从前台进程变为后台进程
fg [可执行程序 ] 将进程从后台进程变为前台进程
进程状态查看
使用ps aux/ ps axj 命令
僵尸进程
产生原因:由于子进程退出,而父进程没有回收子进程的退出状态,导致子进程变成僵尸状态。
子进程退出,父进程还在运行,但父进程没有读取子进程的状态,子进程会进入Z状态。
僵尸进程会以终止状态保持在进程表中,并且一直在等待父进程读取退出状态代码。
僵尸进程的危害:
内存泄漏
由于子进程变成僵尸进程,(子进程的PCB在内核中还存在,并且还在双向链表当中挂着,这意味着使用ps aux这个命令可以看到),也就是,PCB的资源还没有释放,即内核泄漏
孤儿进程
孤儿进程不是进程状态,而是一种进程的种类名称
产生原因:
由于父进程先于子进程退出,导致子进程需要将自己的退出状态返回给一个进程(通常是操作系统当中的1号进程,也称之为Init进程。Init进程本身就会创建许多进程,本身Init进程创建的子进程,不能称为孤儿进程)
僵尸进程使用kill命令杀不掉
那解决僵尸进程草率的做法是,使子进程变成孤儿进程
如果产生了僵尸状态的进程,则将僵尸状态的进程的父进程杀掉,则僵尸状态的进程变成了孤儿进程,也是会被1号进程将进程的返回的状态信息给回收掉,同时在内核当中释放当前孤儿进程的PCB
进程虚拟地址空间:
我们C、C++语言看到的地址,全部是虚拟地址!物理地址,用户一概看不到,由OS统一管理,OS负责将虚拟地址转换成物理地址。
页表:页表的作用是将虚拟地址映射成物理地址
分页式:
将虚拟地址空间分成一页一页的格式,会将物理内存分为一块一块的格式
页号+页内偏移
通过页号找到块号,通过块号,算出块的起始地址,最后块的起始地址加上页内偏移
页号=虚拟地址 / 块大小
页内偏移 = 虚拟地址%块大小
块号:根据页号在页表当中的映射去查找块号
块的起始地址=块号*大小
物理地址=块的起始地址+页内偏移
分段式:将虚拟地址映射成物理地址的结构不是页表了,而是段表
段号+段内偏移
通过段号,找到段的起始地址,然后段的地址加上段内偏移
段页式:存储的时候采用了段表结构和页表结构
段号+页号+页内偏移
首先通过段号,找到页的起始地址,通过页表当中的页号找到对应的块号,通过块号计算出块的起始地址,块的起始地址加上页内偏移。
对比分页式和分段式的优缺点
分页式数据存储效率高,分段式效率低
分段式对程序很友好,可以通过段表结构,找到虚拟地址空间当中的一段
进程优先级
进程优先级是为了保证操作系统调用进程的时候,比较合理
进程优先级可以使用top命令去查看
PR 进程优先级的值,不能直接去修改PR这个值
NI 进程优先级的修正值 可以通过修改NI值来修改PR值
PR(new)=PR(old)+NI
修改NI值 : top ---> r ---> 输入进程的pid ---> 输入修改后的NI值
NI其取值范围是-20至19,一共40个级别
进程优先级的数值越小代表优先级越高
虽然用户可以改变NI值,从而改变PR值,但操作系统调度的解释权归操作系统所有