EDA365电子论坛网
标题:
Linux内核设计与实现之内核同步介绍
[打印本页]
作者:
pulbieup
时间:
2020-12-8 15:33
标题:
Linux内核设计与实现之内核同步介绍
- z4 _) v8 C$ [/ q: i5 e
存在共享资源(共享一个文件,一块内存等等)的时候,为了防止并发访问时共享资源的数据不一致,引入了同步机制。
/ N- {* r. V& x" @1 J
& l! j' z' x# c6 N
主要内容:
, n' c H N: ~. j# Z1 u- F
/ ]* Z) C0 `7 T4 F
同步的概念
; h, P! X* W$ s$ c" T* G
同步的方法-加锁
5 X) ^' f- b# b" N2 Z
死锁
; D% m1 L3 J) @ w; U
锁的粒度
; F$ l7 n- w/ i5 W( c8 k& E
* Z$ b9 B% ^! r7 k- k Q
& ]) O# T8 Q; n" n2 o7 U
1. 同步的概念
6 V9 Y; N" s2 i
了解同步之前,先了解另外2个概念:
! @4 I6 [) C J9 t6 w* ]
9 [" ?$ H4 z( x& B9 _
临界区 - 也称为临界段,就是访问和操作共享数据的代码段。
4 W8 |6 \ C2 e# ^' l
竞争条件 - 2个或2个以上线程在临界区里同时执行的时候,就构成了竞争条件。
8 M7 M" U* z+ [& f' ~
. m& h2 m f9 N. c1 Z4 U/ Q* F5 e
+ e" K& n! W0 a$ e3 D. d
所谓同步,其实防止在临界区中形成竞争条件。
. e$ H. w+ f5 T. s2 V
# y: y3 d1 s# }
如果临界区里是原子操作(即整个操作完成前不会被打断),那么自然就不会出竞争条件。
1 S R. w" R% @& W% U
/ z* [+ a" F; L! h% @4 L; Y( z
但在实际应用中,临界区中的代码往往不会那么简单,所以为了保持同步,引入了锁机制。
9 Z6 |" I6 w) J6 }4 Z
+ W) `" T. P, m J: K3 h
3 e8 t! b9 B q9 G/ ]& j( ?2 i
% Y, M( ^( L4 k% Q2 V$ M
2. 同步的方法-加锁
' w- p7 Q4 A! Y: l- n3 {' V0 ?$ x
为了给临界区加锁,保证临界区数据的同步,首先了解一下内核中哪些情况下会产生并发。
0 |! B0 \1 M/ W6 ?1 G2 P
' `2 E) _: j/ o- u2 p4 Q0 o
- Z: t6 Q$ {. }, ~/ u
; L9 H# E) x# j% _ s" F
内核中造成竞争条件的原因:
3 N$ {- m9 t4 Z: S! o
) B7 ~( J- ?2 `( S$ R$ u8 n
竞争原因
9 P+ \- p8 ]/ ?' k n. N: R6 n4 V9 L! D4 C
D- w9 e- c4 K) Z8 K
说明
2 _; \- i+ F, A& K. |6 E9 T, M. t# n
& j0 y, @+ w1 q+ U, G+ M
中断 中断随时会发生,也就会随时打断当前执行的代码。如果中断和被打断的代码在相同的临界区,就产生了竞争条件
; m; v5 S( j5 i- H0 h3 T
软中断和tasklet 软中断和tasklet也会随时被内核唤醒执行,也会像中断一样打断正在执行的代码
; F9 B' r6 ~8 Y9 Q. o! s9 S" I
内核抢占 内核具有抢占性,发生抢占时,如果抢占的线程和被抢占的线程在相同的临界区,就产生了竞争条件
: i( @1 Q0 M5 [7 }8 M r
睡眠及用户空间的同步 用户进程睡眠后,调度程序会唤醒一个新的用户进程,新的用户进程和睡眠的进程可能在同一个临界区中
" {+ w0 O# [/ `! X9 }/ [
对称多处理 2个或多个处理器可以同时执行相同的代码
" H( s! W4 A; p( \
2 G6 d3 f* w c+ Y0 S
# t6 x1 C9 P0 | n9 a5 K) i
为了在编写内核代码时避免出现竞争条件,在编写代码之前就要考虑好临界区在哪,以及怎么加锁。
6 t2 I: R) s, Y7 ^
8 p" r% U: B; D0 l4 {% _! o
在编写完代码后再加锁是非常困难的,很可能还会导致部分代码重写。
: }% L% l" O+ i6 O A" U
! X; d2 s& \/ O$ K! T) L& @8 `
& F% [3 R0 V5 z" g" k
3 |( `8 {' C4 Y+ J0 l
编写内核代码时,时时记着下面这些问题:
, T% h1 r, g2 j* k
) T3 N6 ]( C% z2 s/ ]+ M
这个数据是不是全局的?除了当前线程以外,其他线程能不能访问它?
% E! F& ?& v/ H+ p/ x" a
这个数据会不会在进程上下文或者中断上下文中共享?它是不是要在两个不同的中断处理程序中共享?
: \3 b% H2 @% U. m( k+ v) r
进程在访问数据时可不可能被抢占?被调度的新程序会不会访问同一数据?
. e' I0 ^3 y# I$ C$ P
当前进程会不会睡眠(或者阻塞)在某些资源上,如果是,它会让共享数据处于何种状态?
7 }7 p0 \( G- I( U( X
怎样防止数据失控?
" F) N1 J" T; i+ X, W, p2 J
如果这个函数又在另一个处理器上被调度将会发生什么?
( A% H6 ^, b# c V8 G7 y) W% G
9 o* C( b! d$ C ^" ?
" _( _% q& i2 K9 ^1 d' t! H6 ~
3. 死锁
3 L2 Z% o4 S9 j9 a& {! A- C r
死锁就是所有线程都在相互等待释放资源,导致谁也无法继续执行下去。
S% ?$ o& Z2 j4 m: e1 `, q. i& P
P- a. J$ k( C) J- X5 m) k3 r
下面一些简单的规则可以帮助我们避免死锁:
5 {! a6 x S. k8 i I$ A2 c
! I; ]9 H V- Y8 n* U
如果有多个锁的话,尽量确保每个线程都是按相同的顺序加锁,按加锁相反的顺序解锁。(即加锁a->b->c,解锁c->b->a)
' {, t* i1 L! R3 o# [
防止发生饥饿。即设置一个超时时间,防止一直等待下去。
7 G' y* G4 a' Q. U+ j
不要重复请求同一个锁。
" b( @2 v+ Z0 S& [& C, R
设计应力求简单。加锁的方案越复杂就越容易出现死锁。
" ~9 X+ Z' B- c) h
. [( v& O& T; Z2 @! S/ p
& K! {; u9 V3 j; U
4. 锁的粒度
( R( u% r( U+ J0 a- u- e
在加锁的时候,不仅要避免死锁,还需要考虑加锁的粒度。
* M9 c) H" A1 b: q M: J
1 g2 ]" J/ ?. \4 P- Q5 \. G2 g
锁的粒度对系统的可扩展性有很大影响,在加锁的时候,要考虑一下这个锁是否会被多个线程频繁的争用。
: Y$ C5 H$ l9 v3 C
$ c2 ~& n2 q, x
如果锁有可能会被频繁争用,就需要将锁的粒度细化。
, P) M9 q* Z* `, _
9 f9 ]- e$ G, x8 R$ W
细化后的锁在多处理器的情况下,性能会有所提升。
' f) e$ E! h, c4 S4 \
* z/ [4 h& w a9 H- `# u: M( M
3 D- Y J- F4 [+ h2 c y C- M: D
0 C9 z6 h$ ~) z
举个例子说明一下:比如给一个链表加锁,同时有A,B,C 3个线程频繁访问这个链表。
, V' ?: t8 ]. g
! E1 a+ q2 h) x; u! O
那么当A,B,C 3个线程同时访问这个链表时,如果A获得了锁,那么B,C线程只能等待A释放了锁后才能访问这个链表。
1 k9 P. o+ `' t7 R6 [: W
4 S* ^( o1 B2 e0 o/ \4 V$ @
7 ^7 G' Q0 M: e4 f' o
$ l& o7 q) V) }# t; d. E
如果A,B,C 3个线程访问的是这个链表的不同节点(比如A是修改节点listA,B是删除节点listB,C是追加节点listC),
& J' p3 Z: `. b$ J4 g* x& V
& [+ d- g t3 k8 G
并且这3个节点不是连续的,那么3个线程同时运行是不会有问题的。
: H8 }; \) F$ r5 J9 C- ? r
' `: H' X* ^6 L1 |- Q1 R5 i
' i2 u: E9 |% O& N4 I
7 s* c$ m, Q4 y6 ~+ w X9 l
这种情况下就可以细化这个锁,把加在链表上的锁去掉,改成把锁加在链表的每个节点上。(也就是锁粒度的细化)
8 ]6 f, b% W$ [; w
5 n" ~/ Z3 ]5 |$ ]4 [) o
那么,上述的情况下,A,B,C 3个线程就可以同时访问各自的节点,特别是在多处理器的情况下,性能会有显著提高。
) h7 ]% \) _+ F, D0 F
J9 c. H x' |
6 e B# u# a5 e
; F. ? H& C% m8 I. {1 Z" W
最后还有一点需要提醒的是,锁的粒度越细,系统开销越大,程序也越复杂,所以对于争用不是很频繁的锁,就没有必要细化了。
作者:
xiaogegepcb
时间:
2020-12-8 16:20
Linux内核设计与实现之内核同步介绍
欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/)
Powered by Discuz! X3.2