UNIX系统的特点:
1. 简洁:仅有几百个系统调用,其他操作系统往往几千个
2. 所有的东西都被当做文件对待,这种抽象可以将数据和对设备的操作通过一套相同
的系统调用接口来进行:open(),read(),write(),lseek(),close()
3. Unix内核和相关系统工具软件是由C语言编写===>在各种硬件系统架构中有很好
的移植能力
4. unix进程创建十分迅速,提供了一个十分独特的fork()系统调用
5. 提供了一套十分简单又十分稳定的进程间通信元语
6. 采用了策略与机制分离的理念,unix系统具备清晰的层次化结构
linux系统:
基础:内核、c库、工具集、如登录程序、shell等系统的基本工具
内核的功能:管理硬件设备、分配系统资源等
内核由以下几大部分组成:
名称
中断服务程序
调度程序
内存管理程序
系统服务程序
功能
负责响应中断
负责管理多个进程并分配处理器时间
负责管理进程地址空间
网络、进程间通信等
用户空间:普通应用程序所在的空间
内核空间:系统态和被保护起来的内存空间
内核空间由内核子系统、设备驱动程序和系统调用接口组成。
与内核通信:
应用程序与内核通信:应用程序通过系统调用陷入内核
硬件设备与内核通信:中断机制
处理器活动状态:
state1
运行于用户空间,执行用户进程
state2
运行于内核空间,处于进程上下文,代表某个特定的进程执行
state3
运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断
linux内核:单内核,但是支持动态加载内核模块、支持抢占(微内核的优点)
linux内核与unix内核的比较:
linux支持动态的加载内核模块,在需要时可以动态卸载和加载内核代码
linux支持对称多处理机制(SMP)
linux内核可以抢占,允许任务优先级
linux内核不区分线程与其他一般进程
linux提供具有设备类的面向对象的设备模型、热插拔事件以及用户空间的设备文件系统
26.
1
修订版本号 稳定版本号(可选)
linux内核版本:比如2.6.26.1
2.
主版本号
6.
从版本号:
偶数代表稳定版本,奇数代表开发版本
内核开发的特点:
feature1
内核编程时不能访问C库与标准的头文件
feature2
内核编程必须使用GUN C
feature3
内核编程无用户空间那样的内存保护机制
feature4
内核编程时很难执行浮点运算
feature5
内核给每个进程只有很小的内核堆栈
feature6
注意同步与并发
进程管理:
进程
线程
处于执行期的程序以及相关资源的总称,由唯一的进程标识符PID来标识
进程提供的虚拟机制
虚拟内存 and 虚拟处理器
进程描述符
包含一个具体进程的所有信息:
打开的文件、进程的地址空间、挂起的信号、进程的状态等
任务队列
一个双向循环链表,进程描述符存放在其中
进程的状态
进程上下文
运行;准备;等待---睡眠;停止;
state域的5种取值:
运行;可中断;不可中断;被其他进程跟踪;停止;
一个程序调用了系统调用或者触发了某个异常,陷入内核空间,称内核代
表进程执行并处于进程上下文
解释:
一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈
上的内容,当内核需要切换到另一个进程时,它需要保存当前进程的所有
状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复
切换时的状态,继续执行
推迟甚至免除copy的技术,资源的复制只在需要写入的时候才进行,之前
就只是以只读的方式共享
fork()函数通过拷贝当前进程创建一个子进程,然后exec()函数产生新的地
址空间,然后读取可执行文件并将其载入地址空间开始运行
fork()函数在调用clone系统调用时需要传递一些参数标志来指明需要共享
的资源
写时拷贝
进程创建的方法
线程创建的方法
进程调度:
I/O消耗型进程
进程大部分时间用来提交I/O请求或等待I/O请求-------例如GUI--I/O密集型
处理器消耗型进程 进程把大部分时间用来执行代码
调度策略平衡
进程响应迅速与最大系统利用率之间寻求平衡
进程优先级
CFS调度算法
CFS使用RB-tree
两种优先级范围:
nice值:-20--19,nice值越大优先级越低
实时优先级:0--99,值越大,优先级越高
允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进
程,而不再采用给每个进程分配时间片的做法。
解释:
CFS调度器的抢占时机取决于新的可运行程序消耗了多少处理器使用比,如果消
耗的使用比比当前进程小,则新进程立刻投入运行,抢占当前进程,否则推迟运
行。
CFS使用红黑树来组织可运行进程队列,利用红黑树这种自平衡二叉搜索树的性
质来迅速找到最小vruntime的进程
方式:
运行RB-tree中最左边叶子结点所代表的那个进程
系统调用:
系统调用的作用 1、为用户空间提供了一种硬件的抽象接口
系统调用号
系统调用的实现
调用系统调用
2、系统调用保证了系统的稳定和安全
系统调用号独一无二,已经分配,不能变更,即使系统调用被删除,系统调用号
也不允许被回收
1、编写系统调用函数
2、将其函数声明添加到系统调用头文件中
3、在系统调用表中加入其的系统调用号
4、将新建的系统调用编译进内核
1、编写调用的系统调用函数,添加上标准头文件、C库链接
2、编译运行调用函数
当
一
个
进
程
在
执
行
时
,
C
P
U
的
所
有
寄
存
器
中
的
值
、
进
程
的
状
态
以
及
堆
栈
中
的
内
容
被
称
为
该
进
程
的
上
下
文
调用系统调用
2、编译运行调用函数
3、使用dmesg查看日志
内核数据结构:
链表
队列
映射
红黑树
如何选择合适
的数据结构
1、存放和操作可变数量元素(节点)的数据结构
2、无须连续内存区域,从链表中增加或删除节点只需要调整一下指向下一节点的指针
即可
3、节点由数据域和指针域组成
1、实现生产者和消费者模型的数据结构
2、数据元素先入先出,主要有2个操作:入队列和出队列
1、关联数组,一个由唯一键组成的集合,每个键必然关联一个特定的值
2、主要是映射一个唯一的标识数(UID)到一个指针
1、一种自平衡二叉搜索树
2、最深的叶子结点的深度不会大于最浅叶子结点深度的2倍
3、在插入和删除节点的时候永远维持半平衡树
链表:对数据集合的主要操作是遍历数据;存储较少数据项;与内核中其他使用链表
的代码交互时;
队列:符合生产者消费者模式;定长缓冲;
映射:映射一个UID到对象;
红黑树:存储大量数据,并且需要检索迅速
算法的复杂度 大O:上限
大Q:既是上限也是下限
中断和中断处理程序:
中断
设备向处理器发送异步的电信号,打断处理器的执行,进而打断内核的执行,从而与
系统通信
中断响应过程 设备产生中断,将中断发送给中断控制器,如果中断线是激活的,中断会被发送到处
理器,如果处理器未禁止该中断,处理器会中断内核,然后开始执行中断处理程序
中断上下文 硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件
的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理
中断会打断内核中进程的正常调度运行,所以要求中断服务程序尽可能的短小精悍既
由于我们既希望中断处理程序运行快,又希望中断处理程序完成的任务量多,出现了
上下半部机制。
上下半部的来
源
中断上半部 处理紧急功能,关中断执行
中断下半部 处理允许稍后完成的功能,开中断执行
中断注册过程
1、使用request_irq()函数注册中断处理程序,激活中断线
2、irq -分配中断号
3、handler-指向处理中断的实际中断处理程序
4、设置中断处理程序标志
下半部与退后执行的工作:
下半部的任务 执行与中断处理密切相关但中断处理程序本身不执行的工作
软中断
tasklet
一组静态定义的下半部接口,32个,可以在所有处理器上同时执行,两个类型一样的
软中断也行
允许响应中断,不能休眠
软中断之间不会相互抢占,只有中断处理程序会抢占软中断
基于软中断实现的灵活性强、动态创建的下半部实现机制
两个不同类型的tasklet可以在不同的处理器上执行,相同类型的tasklet不可以
由内核线程实现,一个用户创建内核线程的接口,通过它创建的进程负责执行由内核
其他部分排到队列里的任务
允许重新调度甚至睡眠
将工作推后交给内核线程去执行——整个过程是在进程上下文中执行
软中断:代码本身多线索化工作做得很好,完全使用单处理器变量
tasklet:不需要睡眠,代码对线索化工作考虑不充分
工作队列:易于使用;需要睡眠;
工作队列
下半部机制的
选择
3种下半部机制的比较:
下半部
软中断
tasklet
工作队列
内核同步:
临界资源的定
义
上下文
中断
中断
进程
顺序执行保障
没有
同类型不能同时执行
没有,和上下文一样被调度
一次仅允许一个进程使用的共享资源
临界区的定义 每个进程中访问和操作临界资源的那段代码
竞争条件
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程进行的顺序
时,我们认为发生了竞争条件
避免并发和防止竞争条件
同步(加锁)
的意义
死锁的概念 一个或多个执行线程和一个或多个资源,每个线程都在等待其中的一个资源,但是所
有资源都已经被占用
内核同步的方法:
原子操作
自旋锁
保证指令以原子的方式执行,执行过程不被打断
原子整数操作 and 原子位操作
1、一个自旋锁只能被一个线程持有,如果有其他线程想要获得一个被持有的自旋
锁,那么该线程将会一直进行忙循环-旋转,等待锁重新可用
2、自旋:十分浪费处理器时间,所以自旋锁不应该被持有,一般自旋时间小于两次
上下文切换用时
3、自旋锁不可以递归
4、不对临界区进行区分
5、应用:加锁时间不长且代码不会睡眠
读写自旋锁
顺序锁
1、读者锁:一个或多个读任务可以并发持有读者锁
2、写者锁:只能被一个写任务持有,且不能有并发的读操作
3、对临界区进程读写区分
4、适用于:读或写/消费者与生产者类型;读进程多于写进程
对某一个共享数据读取的时候不加锁,写的时候加锁。同时为了保证读取的过程中因
为写进程修改了共享区的数据,导致读进程读取数据错误。在读取者和写入者之间引
入了一个整形变量sequence,读取者在读取之前读取sequence, 读取之后再次读取
此值,如果不相同,则说明本次读取操作过程中数据发生了更新,需要重新读取。而
对于写进程在写入数据的时候就需要更新sequence的值。
应用场景:
1、数据读取多,写入少
2、希望写进程优先于读进程,而且不允许读进程让写进程饥饿
3、数据结构简单
1、一种睡眠锁,一个任务试图获得一个不可用的信号量时,信号量会将其推进到一
个等待队列,然后让其睡眠
2、特点:适用于锁被长时间持有的情况;只能在进程上下文中获得信号量锁;占用
信号量的同时不能占用自旋锁
3、计数信号量(允许多个任务持有锁) and 二值信号量(仅允许一个任务持有锁,
类似自旋锁)
1、实现互斥的特定睡眠锁,是一种互斥信号
2、上锁者自己负责解锁
3、不允许递归上锁和解锁
4、进程持有一个mutex不允许退出
5、mutex不可以被copy、手动初始化或重复初始化
自旋锁:低开销加锁;短期锁定;中断上下文加锁;
互斥体:长期加锁;持有锁需要睡眠
信号量
互斥体mutex
自旋锁与互斥
体的区别与应
用