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$ M2. 同步的方法-加锁' 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: J1 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 [: W4 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 I7 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