这是一个一直没有下定想法要做写的东西 真不敢说能把它搞通 只能说尽力去去考虑它了 大家也看得出之前几篇博客也写得有些乱 但这次重新整理
真的是想再把知识点扎扎实实登梳理清楚
那么看出来在考虑完进程的时候我们说过要为之后的一个难点树立起清楚节奏 那就是锁 只有了解进程机制才能好好的了解锁的概念
锁是一个容易很浅淡的理解后就认为搞清楚的东西 但是事实上锁不仅仅和它的解释那好好理解 说实话之前有大牛学长教我锁这个概念的时候 我总是怀揣
一颗敬畏的心 认为它巨难 所以有很大的包袱 在刚刚接触的时候非常的谨慎 总是想了再想才认定锁的某一些概念 在一段时间的学习后 认为自己看清了锁
甚至怀疑学长是不是夸大其词了 认为锁不过如此 结果可想而知毕竟是大牛说过的不能小视。锁的概念好理解但是 但是我知道这些事是都是一个慢慢理解
的过程。
之前说过进程切换是调度算法执行的 虽然说有算法调度 但是对于用户来说确是不可控的 即进程的执行对用户来说是无序的 那么从进程的角度来开 调度
是有无线可能的 那么问题来了 如果两个进程或多个进程都对同一变量进行操作 那么各个进程的结果最终可能是不唯一的 这违背了相通输入输出结果唯一性
这里面有一个经典的喂鱼问题 讲的是两个人一起养金鱼给金鱼喂食,金鱼不能多吃,多了就撑死;金鱼不能少食,少了就饿死。因此两个人喂食很有讲究。
他们基本不会天天都在一起,因此什么时候适合给金鱼喂食就很重要。喂食之前先要判断对方是否已经给金鱼喂食,要是没有喂,就得喂食;要是已经喂了
,就不再继续喂食。两个或者多个进程争相执行同一段代码或访问同一资源的现象称为竞争。这个可能造成竞争的共享代码段或资源称为临界区。
两个进程不可能在同一时刻执行,但是有可能在同一时刻两个进程在同一段代码上
上面的方法对人来讲看上去没有任何问题 但是从计算机的角度确可能出现问题 从进程的角度来看 检查是否已经喂鱼 是 则喂鱼 否则不喂鱼 两个人都会
执行这样的代码 问题就出在 检查鱼是否喂鱼之后做出判断 然后再3 执行是否喂鱼的步骤 是可以被中断的 在第一个人检查完鱼没有被喂 那他下一步则进行
喂鱼指令 这时他被中断了 第二个人进来检查鱼 发现鱼没有进食则喂鱼 结束进程 这时第一个人回来 此时他只会进行喂鱼的指令 因为他之前已经判断过了
所以 鱼被喂了两次 出现了鱼撑死的情况 这就是没有同步机制的执行可能的结果 按照这个故事的发展线路是简单的同步就是留下纸条 防止两个人同时进入
临界区 只要看见纸条这个人就不会再进行检查喂鱼的动作 而留下纸条的人一段会喂鱼 这个看上去也很合理 但是从第一个方案的弊端在我们仍旧可以看出
问题 那就是检查首先是检查纸条 其次是放下纸条 者两步是分开执行的 如果中断在这两步之间产生 仍就会出现都喂鱼的情况 即第一个人检查没有纸条
中断 第二个人检查没有纸条 放下纸条 中断或者喂鱼 第一个人 放下纸条(只要人留下纸条那么这个人就会去喂鱼 后面i的顺序就无关了)这样还是
出现了鱼撑死的情况。 这种同步机制还是没有解决问题 问题是先检查纸条 在放纸条之间 那么有方法就说把顺序调换过来及时先放纸条再检查有不是本人
放的纸条就拿走我的纸条 没有我就可以喂鱼了 是吧这样就不会出现同时喂鱼的情况 这样是不会出现同时喂鱼的情况 但是这样会出现同时都不会喂鱼的情况
造成鱼饿死 还是在检查纸条后 是否拿走纸条 会出都拿走纸条 从而 没人喂鱼的情况 当然后来也出现了用循环等待来解决这个问题 这个方案鱼是没什么问题
关键是人 首先是代码的不对称造成的编程困难 在就是这个不断的轮询造成了极大的CPU浪费。
其实仔细看看 上面 我们可以看出问题出在 我怎么让对方知道或者我要做的时候另一个人不会再做,即 当一个人拿到喂鱼的条件是另一个人不会在访问到
鱼缸了 这里就要引入锁了 首先锁是不是中断的 它是原子操作 就是说一旦或得首先上锁 一但将临界资源锁上 除非我执行完毕释放锁 否则其他人都不能访问
这个资源
这里提出锁的两种状态 锁 闭锁 等待锁达到打开状态
获得锁并锁上
开锁 打开锁
锁的特性 : 锁的初始状态都是打开状态
进入临界区必须获得锁
出临界区必须打开锁
如果别人持有锁则必须等待
这里也有一个问题就是循环等待 在这个小背景下可能看不出CPU的消耗 但是在大的代码中消耗是惊人的 为了避免循环等待 有了睡觉和叫醒机制 即睡眠
等信号 信号是变量count 但是count没有被保护 所以可能会发生数据竞争 当然这里解决问题的方式还是锁
这里还有一个问题就死当一个人发了信号后睡眠 此时却无人等信号 信号丢失 这是又来了一个人 没有信号则睡眠 则两个人都睡眠 也是死锁
解决方法(信号量) 不仅是同步原语,还是通信原语,还可以当锁
信号量实际是一个计数器 其取值为当前和累积的信号数量
支持两种操作 加法操作up 和减法操作down
up 加法操作 down减法操作
1: 将信号量的值增加1 1: 判断信号量的取值是否大于等于1
(此操作将叫醒一个在该信号量上面等待的线程) 2:如果是 将信号量减1 继续执行
2:此线程继续往下执行 3:否则在该信号量上等待(线程将被挂起)
锁解决来同步,带来了循环等待,为了消除循环等待发明来睡觉和叫醒,但是睡觉和叫醒会带来死锁 因此使用信号量
锁的两种状态 等待锁变为闲置状态 获得锁 锁的闭合步骤间存在别人插入的可能 因为不是同步原语登原子性
操作系统之所以能构建锁登同步原语 因为硬件已经提供来一定的原子操作
中断禁止和启用(interrupt enable /disable)
内存加载和存入(load/store)
测量和设置(test&set)
这些操作是硬件步骤 中间无法插入别登操作 早先是为了用于硬件制造者测试CPU而设计的 在这些硬件原子操作的基础上,我们可以构建
软件原子操作:锁,睡觉,叫醒,信号量。
那么锁登实现是用什么呢 从中断启用与禁止来实现锁
当然系统不会将原语操作直接交给用户 而是设计来一个锁登用户使用 (系统安全 防黑客什么的 开玩笑这可是硬件层次)
lock(){ unlock(){
disable interrapts; disable interrapts;
while ( value !=FREE){ value = FREE;
enable interrapts;; enable interrapts;
} }
valube = BUSY;
enable intertapts;
}
锁远远不止这些 这只是讲了锁 关键还是锁的性质决定了 它的关键是死锁 在之后会很详细的讲死锁。
这里讨论的是同步 和锁 有异议的地方 忘大牛多踩踩 我也好重新梳理 以免误导大家
再次 惭愧感谢