操作系统
概述
简介:操作系统是管理硬件和软件的一种应用程序,作为软硬件的中间层,让用户无需关注硬件的实现,主要功能是对进程、内存、设备、文件进行管理,以及为用户提供访问硬件的接口。
程序运行过程
预编译: 主要处理源代码文件中的以“#”开头的预编译指令。(如:处理“#include”预编译指令,就是将文件内容替换到它的位置)
编译:对预编译后的代码进行词法分析、语法分析、语义分析,然后经过优化,生成相应的汇编代码文件,再将汇编代码翻译为机器指令。
链接:将不同源文件产生的目标文件进行链接,从而形成一个可以执行的程序。
静态链接:直接将需要的执行代码拷贝到调用处(优:不需要依赖库,缺:体积大)
动态链接:不直接拷贝可执行代码,操作系统将需要的动态库加载到内存中。然后程序在运行到指定的代码时,去共享已加载动态库中的可执行代码。
CPU内核态和用户态
内核态和用户态是指程序当前处于的一种状态。
用户态:CPU 只能访问指定的内存,不可以访问外接设备。CPU可能被抢占。
内核态:CPU 可以访问任意的数据,包括外接设备,且CPU 不会被抢占(处于特权级0)。
转换时机:
用户态转为内核态:当程序需要申请外部资源时(申请外部资源包括:系统调用,中断,异常。系统调用包括:读写文件,申请堆内存)。
内核态转用户态:当内核态操作执行完成。
状态切换步骤:1. 用户态程序将必要数据值放在寄存器中。2. 用户态程序执行陷阱指令,CPU切换到内核态,并执行请求的服务。3. 系统调用完成后, 操作系统会重置CPU为用户态并返回系统调用的结果。
为什么区分:一些指令失败了会导致整个操作系统崩溃,所以限制用户态执行一些指令
中断
正常运行的程序被中断源打断运行,马上进入中断处理,然后恢复正常运行的过程。中断可以用于处理时间不确定事件,比如键盘输入,如果一直监听就浪费资源,所以可以当键盘有输入时,中断其他程序,马上处理。(通过DMA实现,DMA控制器是独立CPU专门处理IO的)
外中断和内中断
外中断: CPU 执行指令以外的事件引起,如:I/O 完成中断
内中断: CPU 执行指令的内部事件引起,如:地址越界
进程和线程
进程和线程
进程线程区别
1. 进程是资源分配的基本单位,而线程则是任务调度和执行的基本单位(算轻量级进程)。2. 同个进程下的线程共享进程的地址空间与资源。3. 线程间切换开销小,进程间切换开销大
为什么需要线程:进程在同一时间只能干一件事情,如果进程阻塞,整个进程就会被挂起,即使进程中有些工作不依赖与等待的资源,仍然不会执行。所以引入线程提高并发性能。
举例:打开qq,浏览器就是打开了两个进程,在qq里中一边发消息,一边逛空间就是在进程中开了多个线程处理任务
协程
定义:协程可以理解为用户态线程,一个线程可以创建多个协程,操作系统调度线程,线程调度协程(切换协程时也需要保护执行现场,还有用户程序不能操作内核空间,所以给协程分配的是用户栈)
为什么需要协程:多线程情况下,由于线程间切换时需要切换对应的内核栈,导致切换代价高。所以引出协程。
进程或线程的三个状态及转换
阻塞
阻塞—>就绪:处于阻塞状态的进程,其等待的事件已经发生(如等待的输入/输出完成,资源得到满足时),进程便从阻塞状态转变为就绪状态。
就绪
就绪 —> 执行:进程调度程序按某种策略,选中一个就绪进程分配处理机后,该进程便由就绪状态变为运行状态;
运行
执行 —> 就绪:因时间片用完或在采用抢先式优先级调度算法的系统中,有更高优先级的进程要运行时,该进程便由执行状态转变为就绪状态。
执行 —> 阻塞:正在执行的进程因发生某等待事件而无法执行(进程申请资源得不到满足时),则进程由执行状态变为阻塞状态。
进程组成
程序段:就是程序的代码
数据段:就是程序运行时产生的数据(比如全局变量、局部变量等)。
PCB:进程相关息(如进程标识符PID,进程当前状态,进程优先级)。
特殊的几种进程
守护进程:在后台运行的特殊进程。它周期性地执行某种任务。(如web服务器进程http等)
孤儿进程:一个父进程退出,而它子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程
含义:如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了它的退出状态才真正结束,这个时候子进程就称为僵尸进程。(可以通过signal通知内核回收)
如何避免僵尸进程:
1. 在 fork() 子进程之后我们都要及时在父进程中使用 waitpid 系统调用,等子进程结束后,父进程回收子进程 PCB 的资源(waitpid()会导致父进程阻塞)。
2. 当子进程退出的时候,内核都会给父进程一个SIGCHLD 信号,所以可以建立一个捕获 SIGCHLD 信号的信号处理函数,在函数体中调用 waitpid ,就可以清理退出的子进程以达到防止僵尸进程的目的。
线程同步
互斥锁:只能一个线程拥有互斥锁,其他线程只有等待
条件变量:条件变量被用来阻塞一个线程,当条件不满足时,线程会解开互斥锁,进入睡眠状态,条件满足时再唤醒。(互斥锁效率低,所以引入条件变量,与互斥锁一起使用)
读写锁:可以多个读,只允许一个写,写优先于读。
信号量(pv):信号量可以同步多个资源以达到线程同步。(比如营业厅5个窗口,10个顾客进入,当窗口被占满就只能等待,当窗口小于5就可以服务新的顾客,这里窗口就相当于信号量)p操作信号量减,v操作信号量加。
进程间通信IPC
无名管道PIPE:存在内核中,只能用于父子进程或者兄弟进程之间的进程的通信,它是半双工的(同一时间只能读或写)
命名管道FIFO:存在磁盘(占磁盘大小为0,对应内核上的一个缓存),可以在无关的进程之间交换数据。也是半双工
消息队列:存放在内核中的链接表,全双工,消息队列独立于发送与接收进程,读取消息不一定要以先进先出的次序,也可以按消息的类型或优先级读取。
共享内存:多个进程共享一个给定的存储区(最快的进程间通信)
信号量:信号量是一个计数器。用于实现进程间的互斥与同步。
进程调度(运行)
任务
1. 保存处理机的现场信息.2. 按某种算法选取进程。3.把处理器分配给进程
两种进程调度方式:
非抢占式:一旦分配给某个进程,则一直运行至结束
抢占式:(三个原则:优先权原则,短作业优先原则,时间片原则)
三种进程调度算法
轮转调度算法(就绪队列上的每个进程每次运行一个时间片)
优先级调度算法.(在轮转调度算法基础上引入优先级)
多队列调度算法.(多个就绪队列,不同队列可用不同调度算法,也可设置不同优先级。)
并发和并行
并行是指多个事件在同一时刻发生。
并发是指多个事件在同一时间间隔发生。
死锁
定义
多个进程因争夺资源而造成的一种僵局(例子:一个线程持有资源a,它要获取资源b。另一个线程持有资源b,他要获取资源a)
产生死锁的4个必要条件
互斥条件:资源在某一段时间内,仅为某一进程所占用。
请求和保持条件:当进程因请求资源导致阻塞时,对持有的资源保持不放。
不剥夺条件:进程已获得的资源不能剥夺,只在使用完时才释放。
环路等待条件:必然存在进程和资源的环形链,如A进程正在等待B进程占用的资源,而B又在等待A占用的资源.
解决死锁方法
破除四个必要条件,比如撤销进程或剥夺资源至某个进程解除死锁。
预防死锁(破除4个必要条件之一)
避免死锁.(防止系统进入不安全状态.)
检测死锁. (简化资源分配图使进程成为孤立点即无死锁)
解除死锁.(撤销进程或剥夺资源至某个进程解除死锁)
同步、异步、阻塞和非阻塞
A调用B的同一个过程,对调用者A来说就是阻塞和非阻塞,对被调用者B来说就是同步和异步。
例子:A调用B,不用等待B,继续执行。对A来说就是非阻塞,对B就是异步。A调用B,需要等待B,才继续执行。对A来说就是阻塞,对B就是同步。
内存管理
名词解释
物理地址:物理设备上真正的地址。
逻辑地址:是指计算机用户看到的地址,操作系统可以将不连续的物理地址隐射成连续的逻辑地址。
虚拟内存:把磁盘空间作为虚拟内存使用。不用把程序的全部加载到内存,可以把暂时不需要的放到虚拟内存中,在需要时进行数据交换
虚拟地址空间:虚拟地址空间是映射到物理内存和虚拟内存上
内存管理方式
分页存储管理
把进程分为大小固定的页,内存分为同样大小的块。优点:内存空间利用率高。(最后一页装不满也会有内存碎片)
页表:存储页号和内存块号的映射(页号从0开始,所以省略)。
地址转换:逻辑地址除以每页大小可以得到页号和页内偏移,根据页表可以找到页号对应的块号,块号加页内偏移就为物理内存地址。例子:如一页为1k,1023逻辑地址转物理地址步骤:1.1023除以1k 的0余1023,再通过页表查0对应的内存块号,最后内存块号乘以1024+1023就为物理地址。
多级页表作用:1. 可用于减少页表占用的连续空间。2. 可以节约内存(相同内存多级可以隐射更多的内存地址,可以不用把页表全部加载到内存)
分段存储管理
把进程按模块分为大小不固定段,逻辑地址为二维(段号和段内偏移组成) 。优点:方便按照逻辑模块实现信息的共享和保护。缺点:因为段内地址必须连续,所以容易产生内存碎片
段表:由存储段号,起始地址,和段长组成。
地址转换:根据段表找到段号对应的起始地址,起始地址加上段内偏移就为对应物理内存地址(段内偏移超过段长就会越界中断)。例子:如将逻辑地址(1,103)转物理地址步骤:先根据1找到段表中的段号,然后判断103是否小于段长,小于物理地址就为起始地址+103。大于就越界中断
段页式存储管理
将进程按逻辑模块分段,再将各段分页。
请求分页
请求分页是在分页的基础上实现。区别在于是否将作业的全部地址空间同时装入主存。请求分页存储管理只需把当前需要的页面装入内存。所以请求分页存储管理可以提供虚存。(请求分段同理)
地址映射及变换
全相联映射:主存任一一块都可以放到cache的任一一行中.
直接映射:将主存按cache大小分成若干区,区内的块与cache行一一对应
组相联映射:将cache分组,主存分区,每个区包含的块数等于cache组数。组内全相联映射,组间直接映射。
页面置换算法
最佳置换算法OPT:每次选择淘汰的页面都是在未来最长时间不在访问的页面。(拥有最好的性能,实际无法达到,可用于评价其他算法的优劣)
先进先出置换算法FIFO
最少使用置换算法LFU
最近最久未使用置换算法LRU
时钟置换算法CLOCK(每个页面设置一个访问位,访问:访问位设为1,需要淘汰时循环扫描访问位:1变为0,0淘汰)
抖动(颠簸现象)
页面在内存和外存间,短时间频繁调度称为抖动。主要原因是分配给进程的内存不够。
IO模型(网络编程)
IO模型
BIO:阻塞IO,如accept和recv都是阻塞的,要有连接或者数据才会继续执行。
NIO:非阻塞IO,如accept和recv都是非阻塞的,没有连接或数据直接返回-1。
AIO:异步IO,跟非阻塞IO不同的是,当有连接或数据时,会通过回调函数通知。(linux没用AIO是因为如果大量IO时,太多回调函数会造成触发不过来。AIO使用少量IO情况下,比如磁盘,文件可以采用AIO)
IO多路复用
产生
网络服务器可以与大量客户端建立tcp连接,如果每个连接都用一个线程,CPU上下文切换消耗大,所以引出单线程IO多路复用的方式(单线程如何在处理A客户时,接收其他客户请求,答:通过DMA实现,DMA控制器是独立CPU专门处理IO的)。
原始IO多路复用
while(true)中遍历fd,如果某个fd有数据就处理。这样就能处理每个网络连接。但如果需要程序来轮询判断fd是否有数据程序效率很低,所以可以考虑使用内核来轮询判断。(文件描述符fd:linux一切都是文件,每一个网络连接在内核中都是以文件描述符fd的形式存在。文件描述符存储的是一个数字)
三种IO多路复用
select
select函数过程:1. 将文件描述符集合拷贝到内核。2. 通过内核态监听哪些fd有数据(有数据对应bitmap置位)3.把文件描述符集合拷贝到用户态,对有数据的进行处理。
select五个参数:文件描述符最大值+1,读文件描述符集合,写文件描述符集合,异常文件描述符集合,超时时间。(读写文件描述符集合中为一个bitmap,共1024位,根据已建立连接的文件描述符fd决定哪位为1,其余为0。第一个参数的作用在于内核遍历bitmap的范围)
select函数流程:先接收连接,得到已建立连接的文件描述符数组集合fds,同时得到文件描述符最大值max,然后while循环(先通过fds初始化读写文件描述符集合set,然后执行select(max+1,rset,wset,null,null)函数,最后处理有数据的fd )
优点:通过内核态监听哪个fd有数据,速度快
缺点:1. bitmap有1024的限制,fd大于1024就不能用select了。2. bitmap不能重用,下次while循环需要重新初始化。3. 执行select函数会用用户态到内核态的数据拷贝开销。4. select返回时,并不知道哪个fd有数据,需要通过遍历bitmap得知,遍历时间复杂度o(n)。
poll
特点:与select不同在于没有用bitmap,而是把fd封装成一个结构体pollfd,在结构体中包含fd,events(表示读还是写),revents(标志位,表示是否有数据),poll函数传输结构体数组。
优点:自定义数组,没有fd大小限制。使用结构体所以能解决select中bitmap不可重用的问题。
缺点:依然存在select中3.4问题(数据拷贝和轮询判断)。
epoll
优点
1. 不用把fd从用户态拷贝到内核态,而是通过内存映射(mmap)来实现与内核消息传递。(mmap:可以将普通文件映射到内存中,对内存修改直接映射到文件本身。传统读文件时,需要先把数据从磁盘拷贝到内核态,内核态再拷贝到用户态,非常影响性能,)
2. epoll不采用轮询的方式判断是否有数据,而采用事件通知的形式(有数据就通过回调函数通知)
缺点
当事件触发比较频繁时,回调函数也会被频繁触发
三个重要方法
epoll_create 在内核空间中创建一个红黑树根节点。
epoll_ctl 每个连接建立时(accept),就把fd加入根节点。(添加删除节点)
epoll_wait 有数据的fd会通知内核,然后会把它放到就绪链表中,最后返回有数据fd的个数。
两种触发方式
水平触发:有数据但没有处理的fd,会在下一次调用epoll_wait的时候再次放到就绪链表中(缺点就是如果一直不处理就重复返回,耗费性能)
边缘触发:只放一次,不处理就不会通知了
场景选择
select场景:适用连接数量少或者活跃的连接数量多的情况。
epoll场景:适用连接数量多,但活跃的连接数量少的情况。
三种方式如果传入超时时间为0就是非阻塞。否则就是阻塞
reactor
reactor模型:通俗讲就是将对io处理转换为对事件的处理,通过reactor来进行管理。它对epoll进行封装,然后根据epoll_wait返回的fd判断可读还是可写,然后进行相应处理。(它由非阻塞IO和IO多路复用组成)
单reactor模型:网络处理(比如判断哪些fd可读)跟具体的业务逻辑处理(处理可读fd)都在一个线程中(缺点就是不适用高并发情况)。例子:redis采用的就是单reactor模型,因为redis是内存数据库,操作其中数据非常快,所以可以采用这种方式。
单reactor模型+任务队列+线程池:与单reactor相比,它把具体的业务逻辑处理交给其他线程去处理。(缺点很大流量情况下不适用,就需要采用多reactor模型)例子:skynet采用单reactor模型+任务队列+线程池
多reactor模型:一个reactor专门负责接收网络连接,然后把连接分配给多个reactor。每个reactor都是一个线程或进程。例子:nginx采用多reactor模型,每个reactor是一个进程。(通过master接收网络连接,然后fork出多个子进程worker,子进程都监听一个端口,共享一块内存,跟据锁机制来判断那个worker处理连接)
多reactor+消息队列+线程池:对于大流量且业务密集的情况下可以采用这种模式。
linux常用命令
java 操作相关
后台启动jar:nohup java -jar 文件名 & (nohup:不关服务器就一直运行,&:后台运行。输入日志可以在文件名后加:>日志名(不写生成到当前目录))
根据Jar包查询进程号:ps -ef|grep jar包名
杀掉进程:kill 进程号
查看jvm内存命令:jstat -gc 进程号
打开文件
vi:打开文件,如果没有文件,会创建一个,如果没有保存退出,则不会创建(输入:i。搜索:esc后/(n下一个,N上一个)。不保存退出:esc后:q!。保存退出:esc后:wq!)
cat 文件名:一次性显示文件内容。
more 文件名:一页页显示文件内容(空格翻页,q退出)
tail -f 文件名:显示文件最后几行,文件内容刷新就实时增加。
操作文件和文件夹
创建文件:vi 文件名(打开文件,如果没有文件,会创建一个,保存退出才会创建)
创建文件夹:mkdir 文件夹
删除文件:rm 文件名(参数-f:不用确认,参数-r:删文件夹 )
复制文件:cp 文件名 目标文件名(也可改名,加上参数-r可以复制目录)
移动文件或文件夹:mv 文件名 目标文件名(可以改名)
打包文件:tar zcvf 生成的文件名 打包哪些文件(在打包还有zip和unzip命令也可以)
解压文件:tar zxvf 文件名 (压缩到指定文件夹后面加: -C 文件夹名)
搜索文件:find 目录名 -name 搜索文件名 -print (目录名就是在那个目录下搜索,省略就是当前目录下)
查看当前目录:pwd
列出当前目录文件:ls (参数-l:查看详细信息。参数-t:按时间排序)
搜索文件中的内容:grep 搜索内容 文件名
统计文件行数、字数和大小:wc 文件名
文件追加命令:echo 内容 >> 文件名(>是删除原来的内容,然后添加内容)
系统相关
终止命令:ctrl+c
清屏:clear
查看端口占用情况:lsof -i:端口
正则表达式:?匹配一个字符,*匹配任意个字符
查看本机ip地址:ip addr
显示磁盘使用情况:df(参数-h:方便阅读)
判断网络是否连通:ping ip地址或域名(可以加参数指定包的个数:-c 包的个数)