因为闩的种类过多,以后分别在各自领域详细介绍.今天先总的介绍闩的概念.
闩不象锁,锁的结构非常复杂,在晶晶实验五中,我们就要讨论锁的结构.闩的实现相对与锁来说就非常简单了,大多数闩没
有等待者,持有者等等这些队列,且大部分闩没有共享,独占等模式.(当然有部分闩例外).闩是内存中的一些位,使用CPU的硬
指令(test and set 或swap)将其值设置为0或非0,表示是否被持有,他的实现代码极其简单,因此,闩的获取和释放都是非常
快的.闩的持有过程也应该非常短暂.比如:有些闩是保护lock和 pin的释放获取过程,当lock 和pin 获取完毕,闩就可以释放
了,真正的操作在lock和 pin的保护下完成.闩不会伴随整个操作.
闩分三种 1,长闩;2,短闩;3,共享闩
长闩:当请求闩而没有立即获得.请求闩的进程会被放进一个等待者队列.当闩的持有者释放时,会通知队列中第一个等待者
.
短闩:当请求闩而没有立即获得,多CPU下会先自旋,如果自旋仍然得不到闩会进入睡眠状态,N厘秒后醒来再次自旋,如果得
不到,再次睡眠...依次循环直到获得闩.睡眠的时间是1,4,4,8,8,16,16(单位厘秒)...每两次倍增.每睡眠一次都对应一次
latch free等待事件.
自旋的伪码大概如下:
定义全局变量 latch=0
读取(资源ID)
{
int bz=1:
while(bz==1)
{
Swap(latch,bz);
}
访问资源;
latch=0;
}
在X86系统中,Swap函数就是XCHG指令.他是一条CPU的硬指令,他的执行不会被打断.
因为自旋的过程是一个忙测试循环,在此期间进程会一直占有CPU的时间片,通常还会使用指令禁止进程切换,单CPU下在自
旋期间,持有闩的进程不可能得到执行,也不可能释放他所持有的闩,所以在单CPU下自旋是没有意义的.
共享闩: 具文档中介绍,共享闩只有一个,就是Cache Buffer Chain闩(简称CBC闩)。
编写了个小脚本,反复读取某个表的同一个块.看此共享闩是否有争用:
create or replace procedure my_cursor is
cursor aa is select id from jj_2 where rowid='AAAMvgAAHAAAAGwAAA';
x number(5);
begin
for i in 1..300000 loop
open aa;
fetch aa into x;
close aa;
end loop;
end;
/
在执行前先观察CBC闩的情况:
SQL> select sid,event,time_waited,time_waited_micro,total_timeouts,total_waits from v$session_event where
(sid=190 or sid=203) and event='latch: cache buffers chains';
SID EVENT TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
190 latch: cache buffers chains 0 39 0 4
203 latch: cache buffers chains 0 391 0 9
在一个会话中执行上面的脚本,查看V$session_event视图,可以发现没有新的相关CBC闩的等待,分别在两个会话中执行上
面的脚本,执行一次后,查看V$session_event中的等待事件:
SID EVENT TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
190 latch: cache buffers chains 0 39 0 4
203 latch: cache buffers chains 0 574 0 13
再试一次:
SID EVENT TIME_WAITED TIME_WAITED_MICRO TOTAL_TIMEOUTS TOTAL_WAITS
---------- ------------------------------ ----------- ----------------- -------------- -----------
190 latch: cache buffers chains 0 75 0 5
203 latch: cache buffers chains 0 682 0 17
发现第一次190号会话的CBC闩没有增加,而203号会话增加了,第二次执行后两个绘画都增加了。再进一步了解一下闩的相关
视图:
SQL> select misses,gets,sleeps,spin_gets from v$latch where name='cache buffers chains';
原值:
MISSES GETS SLEEPS SPIN_GETS
---------- ---------- ---------- ----------
1289 14632424 32 1260
只一个会话执行语句:
MISSES GETS SLEEPS SPIN_GETS
---------- ---------- ---------- ----------
1289 15232803 32 1260
可以发现,MISSES没有增加。只是GEST增加了60多万次。
两个会话都执行脚本:
MISSES GETS SLEEPS SPIN_GETS
---------- ---------- ---------- ----------
1479 16433137 42 1441
只一个会话执行的时候MISSES 并没增加,两个都执行的时候有少量增加,增加了将近200次。两个会话,几乎同时60多万次的
进行CBC请求,只有近200次的MISSES,因此这里CBC闩只是有限共享。
父闩; 子闩;
有些闩只能有一个,但有些闩可以有众多的子闩,这是为什么呢?只要是闩所保护的资源是成组出现的,且互相之间互不影
响,那么就可以拥有众多子闩,例如:Cache Buffer Chain闩(简称CBC闩).他主要是保护buffer cache 中 hash表的hash链,因
为hash链不止一条,且各个HASH链没什么影响,所以可以为每个HASH链都准备一个闩,这样CBC闩就拥有了众多的子闩,在8I中
就是这样的,但9I改变了算法,减少了每个HASH链中buffer head的数量.因此,9I中一个CBC子闩会对应多条HASH链,象CBC这样
有众多子闩的闩就可以称为非独立闩.或组闩.还有一种情况,并非所保护的资源是成组出现但也有多个子闩,如: redo copy
闩,子闩的个数,oracle默认为CPU个数的两倍,oracle是为了限制同时向log buffer复制日志缓存的进程的数量. 从而限制取
得此闩进程的数量.因此,此闩也是非独立闩,但子闩数量不多.
相关视图:
观察闩的情况是用v$latch 和v$latch_children两个视图, v$latch视图基本上是v$latch_children分组求合的结果.以
前见坛友问过此视图的列意义,在此简单说明几列:
gets 以愿意等待模式请求闩,并获得的次数.
misses 以愿意等待模式请求闩,自旋或睡眠后获得的次数.
sleeps 以愿意等待模式请求闩,睡眠的次数.
spin_gets 第一次自旋即成功获得闩的次数.
sleep1 睡眠一次后获得闩的次数
sleep2 睡眠两次后获得闩的次数
sleep3 睡眠三次后获得闩的次数
sleep4 睡眠四次以上获得闩的次数
sleeps 和 sleep1,2,3,4的关系:
sleeps=sleep1*1+sleep2*2+sleep*3+sleep4*N...
在多CPU环境中每次睡眠醒来都要自旋,假如:第三次睡眠后醒来,自旋获得闩了,spin_gets不再增加.他只反映第一次自旋即
获得闩的次数.sleep3加1.sleeps加3.文档中有个相关自旋的公式: sleeps/(misses-spin_gets) 这个公式的结果反映了几
次睡眠才会获得闩,这个值是个平均值.大家可以根据这个值的高低来判断自旋的效率.
自旋到底有什么样的作用呢?? 自旋可以减少进程切换所带来的负担,如果一个进程刚刚获得CPU时间片,但是只因为无法
获得闩而让出了时间片,浪费了刚刚到手的时间片,如果有很多进程都是因为无法理解获得闩而被切换走时间片,众多的进程
切换操作也会带来一些系统的负担.
补充一下进程的切换,它到底有多大的负担呢??我们都知道每个进程是运行在他的上下文中,从A进程切换到B进程,主要完
成的任务就是把A进程的上下文保存起来,而把B进程的上下文调出,进程切换的主要负担就是上下文的切换,上下文到底有多
少内容呢??参考LINUX的原码,在arch/i386/kernel/entry.S中,我们可以看到上下文主要是指CPU中的所有寄存器,保护A进程
的上下文就是当A进程运行时,把所有的CPU中寄存器的当前值压入堆栈.在32位系统中也就是100个字节左右,简单点说,上下
文切换的负担就是将CPU中的寄存器的值转入内存。 |
|