-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathlinux-process-stop.html
656 lines (442 loc) · 83.1 KB
/
linux-process-stop.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
<!DOCTYPE html>
<html lang="en">
<!-- Head tag -->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
<meta name="baidu-site-verification" content="093lY4ziMu" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="description" content="一个有内涵的技术分享平台">
<meta name="keyword" content="meizu,kernel,魅族">
<link rel="shortcut icon" href="/img/ironman-draw.png">
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
<!--<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>-->
<title>
Linux 进程中 Stop, Park, Freeze - 魅族内核团队
</title>
<link rel="canonical" href="https://kernel.meizu.com//linux-process-stop.html">
<!-- Bootstrap Core CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="css/dusign-light.css">
<link rel="stylesheet" href="css/dusign-common-light.css">
<link rel="stylesheet" href="css/font-awesome.css">
<link rel="stylesheet" href="css/toc.css">
<!-- background effects end -->
<!-- Pygments Highlight CSS -->
<link rel="stylesheet" href="css/highlight.css">
<link rel="stylesheet" href="css/widget.css">
<link rel="stylesheet" href="css/rocket.css">
<link rel="stylesheet" href="css/signature.css">
<link rel="stylesheet" href="css/fonts.googleapis.css">
<link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css">
<!-- photography -->
<link rel="stylesheet" href="css/photography.css">
<!-- ga & ba script hoook -->
<script></script>
<meta name="generator" content="Hexo 7.3.0"></head>
<!-- hack iOS CSS :active style -->
<body ontouchstart="">
<!-- background effects start -->
<!-- background effects end -->
<!-- Modified by Yu-Hsuan Yen -->
<!-- Post Header -->
<style type="text/css">
header.intro-header{
background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('')
/*post*/
}
</style>
<header class="intro-header" >
<!-- Signature -->
<div id="signature">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="post-heading">
<div class="tags">
<a class="tag" href="/tags/#kthread_stop" title="kthread_stop">kthread_stop</a>
<a class="tag" href="/tags/#kthread_park" title="kthread_park">kthread_park</a>
<a class="tag" href="/tags/#freeze_processes" title="freeze_processes">freeze_processes</a>
</div>
<h1>Linux 进程中 Stop, Park, Freeze</h1>
<h2 class="subheading"></h2>
<span class="meta">
Posted by Peng Weilin on
2016-07-11
</span>
</div>
</div>
</div>
</div>
</div>
<div class="waveWrapper">
<div class="wave wave_before" style="background-image: url('/img/wave-light.png')"></div>
<div class="wave wave_after" style="background-image: url('/img/wave-light.png')"></div>
</div>
</header>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">魅族内核团队</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<!-- Known Issue, found by Hux:
<nav>'s height woule be hold on by its content.
so, when navbar scale out, the <nav> will cover tags.
also mask any touch event of tags, unfortunately.
-->
<div id="huxblog_navbar">
<div class="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about/">About</a>
</li>
<li>
<a href="/archive/">Archives</a>
</li>
<li>
<a href="/categories/">Categories</a>
</li>
<li>
<a href="/tags/">Tags</a>
</li>
</ul>
</div>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<script>
// Drop Bootstarp low-performance Navbar
// Use customize navbar with high-quality material design animation
// in high-perf jank-free CSS3 implementation
var $body = document.body;
var $toggle = document.querySelector('.navbar-toggle');
var $navbar = document.querySelector('#huxblog_navbar');
var $collapse = document.querySelector('.navbar-collapse');
$toggle.addEventListener('click', handleMagic)
function handleMagic(e){
if ($navbar.className.indexOf('in') > 0) {
// CLOSE
$navbar.className = " ";
// wait until animation end.
setTimeout(function(){
// prevent frequently toggle
if($navbar.className.indexOf('in') < 0) {
$collapse.style.height = "0px"
}
},400)
}else{
// OPEN
$collapse.style.height = "auto"
$navbar.className += " in";
}
}
</script>
<!-- Main Content -->
<!-- Post Content -->
<article>
<div class="container">
<div class="row">
<!-- Post Container -->
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
post-container">
<p>在调试内核的时候,经常会碰到几个相近的概念:进程 stop、进程 park、进程 freeze。这几个名词看起来都是停止进程,那么他们之间的区别和应用场景在分别是什么呢?下面就来分析一番。</p>
<blockquote>
<p>本文的代码分析基于 Linux kernel 3.18.22,最好的学习方法还是 “RTFSC”</p>
</blockquote>
<h2 id="1-进程-stop"><a href="#1-进程-stop" class="headerlink" title="1. 进程 stop"></a>1. 进程 stop</h2><p>进程 stop 分成两种:用户进程 stop 和内核进程 stop。</p>
<p>用户进程 stop 可以通过给进程发送 STOP 信号来实现,可以参考“Linux Signal”这一篇的描述。但是对内核进程来说不会响应信号,如果碰到需要 stop 内核进程的场景怎么处理?比如:我们在设备打开的时候创建了内核处理进程,在设备关闭的时候需要 stop 内核进程。</p>
<p>Linux 实现了一套 <code>kthread_stop()</code> 的机制来实现内核进程 stop。</p>
<h3 id="1-1-内核进程的创建"><a href="#1-1-内核进程的创建" class="headerlink" title="1.1 内核进程的创建"></a>1.1 内核进程的创建</h3><p>内核进程创建过程,是理解本篇的基础。</p>
<p>可以看到 <code>kthread_create()</code> 并不是自己去创建内核进程,而是把创建任务推送给 <code>kthreadd()</code> 进程执行。</p>
<p><code>kthreadd()</code> -> <code>create_kthread()</code> -> <code>kernel_thread()</code> 创建的新进程也不是直接使用用户的函数 <code>threadfn()</code>,而是创建通用函数 <code>kthread()</code>,<code>kthread()</code> 再来调用 <code>threadfn()</code>。</p>
<ul>
<li>kernel/kthread.c:</li>
</ul>
<p><img src="stop_kthread_create.png" alt="kthread_create"></p>
<h3 id="1-2-内核进程的-stop"><a href="#1-2-内核进程的-stop" class="headerlink" title="1.2 内核进程的 stop"></a>1.2 内核进程的 stop</h3><p>如果内核进程需要支持 <code>kthread_stop()</code>,需要根据以下框架来写代码。用户在主循环中调用 <code>kthread_should_stop()</code> 来判断当前 kthread 是否需要 stop,如果被 stop 则退出循环。</p>
<p>这种代码为什么不做到通用代码 <code>kthread()</code> 中?这应该是和 Linux 的设计思想相关的。Linux 运行内核态的策略比较灵活,而对用户态的策略更加严格统一。</p>
<p><img src="stop_kthread_should_stop.png" alt="kthread_should_stop"></p>
<p><code>kthread_should_stop()</code> 和 <code>kthread_stop()</code> 的代码实现:</p>
<ul>
<li>kernel/kthread.c:</li>
<li><code>kthread_should_stop()</code>/<code>kthread_stop()</code></li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">kthread_should_stop</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// (1) 判断进程所在 kthread 结构中的 KTHREAD_SHOULD_STOP 是否被置位</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">test_bit</span>(KTHREAD_SHOULD_STOP, &<span class="built_in">to_kthread</span>(current)->flags);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kthread_stop</span><span class="params">(<span class="keyword">struct</span> task_struct *k)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">kthread</span> *kthread;</span><br><span class="line"> <span class="type">int</span> ret;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">trace_sched_kthread_stop</span>(k);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">get_task_struct</span>(k);</span><br><span class="line"> kthread = <span class="built_in">to_live_kthread</span>(k);</span><br><span class="line"> <span class="keyword">if</span> (kthread) {</span><br><span class="line"> <span class="comment">// (2) 置位进程所在 kthread 结构中的 KTHREAD_SHOULD_STOP</span></span><br><span class="line"> <span class="built_in">set_bit</span>(KTHREAD_SHOULD_STOP, &kthread->flags);</span><br><span class="line"> <span class="comment">// (3) unpark & wake_up 进程来响应 stop 信号</span></span><br><span class="line"> __kthread_unpark(k, kthread);</span><br><span class="line"> <span class="built_in">wake_up_process</span>(k);</span><br><span class="line"> <span class="built_in">wait_for_completion</span>(&kthread->exited);</span><br><span class="line"> }</span><br><span class="line"> ret = k->exit_code;</span><br><span class="line"> <span class="built_in">put_task_struct</span>(k);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">trace_sched_kthread_stop_ret</span>(ret);</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="2-进程-park"><a href="#2-进程-park" class="headerlink" title="2. 进程 park"></a>2. 进程 park</h2><p><code>smpboot_register_percpu_thread()</code> 用来创建 per_cpu 内核进程,所谓的 per_cpu 进程是指需要在每个 online cpu 上创建线程。比如执行 <code>stop_machine()</code> 中 cpu 同步操作的 migration 进程:</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">shell</span>@:/ $ ps | grep migration</span><br><span class="line"><span class="attribute">root</span> <span class="number">10</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> smpboot_th <span class="number">0000000000</span> S migration/<span class="number">0</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">11</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> smpboot_th <span class="number">0000000000</span> S migration/<span class="number">1</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">15</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">2</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">19</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">3</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">207</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">8</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">247</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">4</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">251</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">5</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">265</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">6</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">356</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">7</span></span><br><span class="line"><span class="attribute">root</span> <span class="number">2165</span> <span class="number">2</span> <span class="number">0</span> <span class="number">0</span> __kthread_ <span class="number">0000000000</span> R migration/<span class="number">9</span></span><br></pre></td></tr></table></figure>
<p>问题来了,既然 per_cpu 进程是和 cpu 绑定的,那么在 cpu hotplug 的时候,进程需要相应的 disable 和 enable。实现的方法可以有多种:</p>
<ul>
<li>动态的销毁和创建线程。缺点是开销比较大。</li>
<li>设置进程的 cpu 亲和力 <code>set_cpus_allowed_ptr()</code>。缺点是进程绑定的 cpu 如果被 down 掉,进程会迁移到其他 cpu 继续执行。</li>
</ul>
<p>为了克服上述方案的缺点,适配 per_cpu 进程的 cpu hotplug 操作,设计了 <code>kthread_park()</code>/<code>kthread_unpark()</code> 机制。</p>
<h3 id="2-1-smpboot-register-percpu-thread"><a href="#2-1-smpboot-register-percpu-thread" class="headerlink" title="2.1 smpboot_register_percpu_thread()"></a>2.1 <code>smpboot_register_percpu_thread()</code></h3><p>per_cpu 进程从代码上看,实际也是调用 <code>kthread_create()</code> 来创建的。</p>
<ul>
<li>kernel/smpboot.c:</li>
<li>kernel/kthread.c:</li>
</ul>
<p><img src="stop_smpboot_register.png" alt="smpboot_register_percpu_thread"></p>
<p>我们可以看到 smpboot_register 又增加了一层封装:<code>kthread()</code> -> <code>smpboot_thread_fn()</code> -> <code>ht->thread_fn()</code>,这种封装的使用可以参考 cpu_stop_threads。</p>
<ul>
<li>kernel/stop_machine.c:</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">struct</span> <span class="title class_">smp_hotplug_thread</span> cpu_stop_threads = {</span><br><span class="line"> .store = &cpu_stopper_task,</span><br><span class="line"> .thread_should_run = cpu_stop_should_run,</span><br><span class="line"> .thread_fn = cpu_stopper_thread,</span><br><span class="line"> .thread_comm = <span class="string">"migration/%u"</span>,</span><br><span class="line"> .create = cpu_stop_create,</span><br><span class="line"> .setup = cpu_stop_unpark,</span><br><span class="line"> .park = cpu_stop_park,</span><br><span class="line"> .pre_unpark = cpu_stop_unpark,</span><br><span class="line"> .selfparking = <span class="literal">true</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">int</span> __init <span class="title">cpu_stop_init</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> cpu;</span><br><span class="line"></span><br><span class="line"> for_each_possible_cpu(cpu) {</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">cpu_stopper</span> *stopper = &<span class="built_in">per_cpu</span>(cpu_stopper, cpu);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">spin_lock_init</span>(&stopper->lock);</span><br><span class="line"> <span class="built_in">INIT_LIST_HEAD</span>(&stopper->works);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">BUG_ON</span>(<span class="built_in">smpboot_register_percpu_thread</span>(&cpu_stop_threads));</span><br><span class="line"> stop_machine_initialized = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们可以看到 <code>smpboot_thread_fn()</code> 循环中实现了对 park 的支持,具体实现 <code>kthread_should_park()</code>、<code>kthread_parkme()</code>、<code>kthread_park()</code>、<code>kthread_unpark()</code> 的代码分析:</p>
<ul>
<li>kernel/kthread.c:</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">kthread_should_park</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// (1) 判断进程所在 kthread 结构中的 KTHREAD_SHOULD_PARK 是否被置位</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">test_bit</span>(KTHREAD_SHOULD_PARK, &<span class="built_in">to_kthread</span>(current)->flags);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">kthread_parkme</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> __kthread_parkme(<span class="built_in">to_kthread</span>(current));</span><br><span class="line">}</span><br><span class="line">| →</span><br><span class="line"><span class="type">static</span> <span class="type">void</span> __kthread_parkme(<span class="keyword">struct</span> kthread *self)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// (2) 如果当前进程的 KTHREAD_SHOULD_PARK 标志被置位 ,</span></span><br><span class="line"> <span class="comment">// 将当前进程进入 TASK_PARKED 的阻塞状态。</span></span><br><span class="line"> <span class="comment">// 如果 KTHREAD_SHOULD_PARK 不清除,</span></span><br><span class="line"> <span class="comment">// 就算被 wake_up 唤醒还是会循环进入 TASK_PARKED 的阻塞状态。</span></span><br><span class="line"> __set_current_state(TASK_PARKED);</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">test_bit</span>(KTHREAD_SHOULD_PARK, &self->flags)) {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">test_and_set_bit</span>(KTHREAD_IS_PARKED, &self->flags))</span><br><span class="line"> <span class="built_in">complete</span>(&self->parked);</span><br><span class="line"> <span class="built_in">schedule</span>();</span><br><span class="line"> __set_current_state(TASK_PARKED);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">clear_bit</span>(KTHREAD_IS_PARKED, &self->flags);</span><br><span class="line"> __set_current_state(TASK_RUNNING);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kthread_park</span><span class="params">(<span class="keyword">struct</span> task_struct *k)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">kthread</span> *kthread = <span class="built_in">to_live_kthread</span>(k);</span><br><span class="line"> <span class="type">int</span> ret = -ENOSYS;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (kthread) {</span><br><span class="line"> <span class="comment">// (3) 设置 KTHREAD_IS_PARKED 标志位,并且唤醒进程进入 park 状态</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">test_bit</span>(KTHREAD_IS_PARKED, &kthread->flags)) {</span><br><span class="line"> <span class="built_in">set_bit</span>(KTHREAD_SHOULD_PARK, &kthread->flags);</span><br><span class="line"> <span class="keyword">if</span> (k != current) {</span><br><span class="line"> <span class="built_in">wake_up_process</span>(k);</span><br><span class="line"> <span class="built_in">wait_for_completion</span>(&kthread->parked);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ret = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">kthread_unpark</span><span class="params">(<span class="keyword">struct</span> task_struct *k)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">kthread</span> *kthread = <span class="built_in">to_live_kthread</span>(k);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (kthread)</span><br><span class="line"> __kthread_unpark(k, kthread);</span><br><span class="line">}</span><br><span class="line">| →</span><br><span class="line"><span class="type">static</span> <span class="type">void</span> __kthread_unpark(<span class="keyword">struct</span> task_struct *k, <span class="keyword">struct</span> kthread *kthread)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">// (4) 清除 KTHREAD_IS_PARKED 标志位</span></span><br><span class="line"> <span class="built_in">clear_bit</span>(KTHREAD_SHOULD_PARK, &kthread->flags);</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * We clear the IS_PARKED bit here as we don't wait</span></span><br><span class="line"><span class="comment"> * until the task has left the park code. So if we'd</span></span><br><span class="line"><span class="comment"> * park before that happens we'd see the IS_PARKED bit</span></span><br><span class="line"><span class="comment"> * which might be about to be cleared.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// 如果进程已经被 park,并且 wake_up 唤醒进程</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">test_and_clear_bit</span>(KTHREAD_IS_PARKED, &kthread->flags)) {</span><br><span class="line"> <span class="comment">// 如果是 per_cpu 进程,重新绑定进程 cpu</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">test_bit</span>(KTHREAD_IS_PER_CPU, &kthread->flags))</span><br><span class="line"> __kthread_bind(k, kthread->cpu, TASK_PARKED);</span><br><span class="line"> <span class="built_in">wake_up_state</span>(k, TASK_PARKED);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="2-2-cpu-hotplug-支持"><a href="#2-2-cpu-hotplug-支持" class="headerlink" title="2.2 cpu hotplug 支持"></a>2.2 cpu hotplug 支持</h3><p>我们前面说到 park 机制的主要目的是为了 per_cpu 进程支持 cpu hotplug,具体怎么响应热插拔事件呢?</p>
<ul>
<li>kernel/smpboot.c:</li>
</ul>
<p><img src="stop_park_hotplug.png" alt="park_hotplug"></p>
<h2 id="3-进程-freeze"><a href="#3-进程-freeze" class="headerlink" title="3. 进程 freeze"></a>3. 进程 freeze</h2><p>在系统进入 suspend 的时候,会尝试冻住一些进程,以避免一些进程无关操作影响系统的 suspend 状态。主要的流程如下:</p>
<ul>
<li>kernel/power/suspend.c:</li>
</ul>
<p><img src="stop_pm_suspend.png" alt="suspend_freeze_processes"></p>
<p>这 suspend_freeze 里面判断当前在那个阶段,有 3 个重要的变量:</p>
<ul>
<li>system_freezing_cnt - >0 表示系统全局的 freeze 开始;</li>
<li>pm_freezing - =true 表示用户进程 freeze 开始;</li>
<li>pm_nosig_freezing - =true 表示内核进程 freeze 开始;</li>
</ul>
<p>具体代码分析如下:</p>
<ul>
<li>kernel/power/process.c:</li>
<li>kernel/freezer.c:</li>
<li><code>suspend_freeze_processes()</code> -> <code>freeze_processes()</code> -> <code>try_to_freeze_tasks()</code> -> <code>freeze_task()</code></li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">freeze_processes</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">int</span> error;</span><br><span class="line"> <span class="type">int</span> oom_kills_saved;</span><br><span class="line"></span><br><span class="line"> error = __usermodehelper_disable(UMH_FREEZING);</span><br><span class="line"> <span class="keyword">if</span> (error)</span><br><span class="line"> <span class="keyword">return</span> error;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (1) 置位 PF_SUSPEND_TASK,确保当前进程不会被 freeze</span></span><br><span class="line"> <span class="comment">/* Make sure this task doesn't get frozen */</span></span><br><span class="line"> current->flags |= PF_SUSPEND_TASK;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (2) 使用全局 freeze 标志 system_freezing_cnt</span></span><br><span class="line"> <span class="keyword">if</span> (!pm_freezing)</span><br><span class="line"> <span class="built_in">atomic_inc</span>(&system_freezing_cnt);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">pm_wakeup_clear</span>();</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"Freezing user space processes ... "</span>);</span><br><span class="line"> <span class="comment">// (3) 使用用户进程 freeze 标志 pm_freezing</span></span><br><span class="line"> pm_freezing = <span class="literal">true</span>;</span><br><span class="line"> oom_kills_saved = <span class="built_in">oom_kills_count</span>();</span><br><span class="line"> <span class="comment">// (4) freeze user_only 进程</span></span><br><span class="line"> <span class="comment">// 判断进程是否可以被 freeze,唤醒进程 freeze 自己</span></span><br><span class="line"> error = <span class="built_in">try_to_freeze_tasks</span>(<span class="literal">true</span>);</span><br><span class="line"> <span class="keyword">if</span> (!error) {</span><br><span class="line"> __usermodehelper_set_disable_depth(UMH_DISABLED);</span><br><span class="line"> <span class="built_in">oom_killer_disable</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * There might have been an OOM kill while we were</span></span><br><span class="line"><span class="comment"> * freezing tasks and the killed task might be still</span></span><br><span class="line"><span class="comment"> * on the way out so we have to double check for race.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">oom_kills_count</span>() != oom_kills_saved &&</span><br><span class="line"> !<span class="built_in">check_frozen_processes</span>()) {</span><br><span class="line"> __usermodehelper_set_disable_depth(UMH_ENABLED);</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"OOM in progress."</span>);</span><br><span class="line"> error = -EBUSY;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"done."</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"\n"</span>);</span><br><span class="line"> <span class="built_in">BUG_ON</span>(<span class="built_in">in_atomic</span>());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (error)</span><br><span class="line"> <span class="built_in">thaw_processes</span>();</span><br><span class="line"> <span class="keyword">return</span> error;</span><br><span class="line">}</span><br><span class="line">| →</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">try_to_freeze_tasks</span><span class="params">(<span class="type">bool</span> user_only)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">task_struct</span> *g, *p;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> end_time;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> todo;</span><br><span class="line"> <span class="type">bool</span> wq_busy = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timeval</span> start, end;</span><br><span class="line"> u64 elapsed_msecs64;</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">int</span> elapsed_msecs;</span><br><span class="line"> <span class="type">bool</span> wakeup = <span class="literal">false</span>;</span><br><span class="line"> <span class="type">int</span> sleep_usecs = USEC_PER_MSEC;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_PM_SLEEP</span></span><br><span class="line"> <span class="type">char</span> suspend_abort[MAX_SUSPEND_ABORT_LEN];</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">do_gettimeofday</span>(&start);</span><br><span class="line"></span><br><span class="line"> end_time = jiffies + <span class="built_in">msecs_to_jiffies</span>(freeze_timeout_msecs);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (4.1) 如果是 kernel freeze,</span></span><br><span class="line"> <span class="comment">// 停工有 WQ_FREEZABLE 标志的 workqueue</span></span><br><span class="line"> <span class="comment">// 将 wq 的 pwq->max_active 设置成 0,新的 work 不能被执行</span></span><br><span class="line"> <span class="keyword">if</span> (!user_only)</span><br><span class="line"> <span class="built_in">freeze_workqueues_begin</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> todo = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">read_lock</span>(&tasklist_lock);</span><br><span class="line"> <span class="comment">// (4.2) 对每个进程执行 freeze_task()</span></span><br><span class="line"> for_each_process_thread(g, p) {</span><br><span class="line"> <span class="keyword">if</span> (p == current || !<span class="built_in">freeze_task</span>(p))</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">freezer_should_skip</span>(p))</span><br><span class="line"> todo++;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">read_unlock</span>(&tasklist_lock);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (4.3) 如果是 kernel freeze,</span></span><br><span class="line"> <span class="comment">// 判断停工的 workqueue 中残留的 work 有没有执行完</span></span><br><span class="line"> <span class="keyword">if</span> (!user_only) {</span><br><span class="line"> wq_busy = <span class="built_in">freeze_workqueues_busy</span>();</span><br><span class="line"> todo += wq_busy;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!todo || <span class="built_in">time_after</span>(jiffies, end_time))</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">pm_wakeup_pending</span>()) {</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_PM_SLEEP</span></span><br><span class="line"> <span class="built_in">pm_get_active_wakeup_sources</span>(suspend_abort,</span><br><span class="line"> MAX_SUSPEND_ABORT_LEN);</span><br><span class="line"> <span class="built_in">log_suspend_abort_reason</span>(suspend_abort);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"> wakeup = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * We need to retry, but first give the freezing tasks some</span></span><br><span class="line"><span class="comment"> * time to enter the refrigerator. Start with an initial</span></span><br><span class="line"><span class="comment"> * 1 ms sleep followed by exponential backoff until 8 ms.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">usleep_range</span>(sleep_usecs / <span class="number">2</span>, sleep_usecs);</span><br><span class="line"> <span class="keyword">if</span> (sleep_usecs < <span class="number">8</span> * USEC_PER_MSEC)</span><br><span class="line"> sleep_usecs *= <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">do_gettimeofday</span>(&end);</span><br><span class="line"> elapsed_msecs64 = <span class="built_in">timeval_to_ns</span>(&end) - <span class="built_in">timeval_to_ns</span>(&start);</span><br><span class="line"> <span class="built_in">do_div</span>(elapsed_msecs64, NSEC_PER_MSEC);</span><br><span class="line"> elapsed_msecs = elapsed_msecs64;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (wakeup) {</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"\n"</span>);</span><br><span class="line"> <span class="built_in">printk</span>(KERN_ERR <span class="string">"Freezing of tasks aborted after %d.%03d seconds"</span>,</span><br><span class="line"> elapsed_msecs / <span class="number">1000</span>, elapsed_msecs % <span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (todo) {</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"\n"</span>);</span><br><span class="line"> <span class="built_in">printk</span>(KERN_ERR <span class="string">"Freezing of tasks failed after %d.%03d seconds"</span></span><br><span class="line"> <span class="string">" (%d tasks refusing to freeze, wq_busy=%d):\n"</span>,</span><br><span class="line"> elapsed_msecs / <span class="number">1000</span>, elapsed_msecs % <span class="number">1000</span>,</span><br><span class="line"> todo - wq_busy, wq_busy);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">read_lock</span>(&tasklist_lock);</span><br><span class="line"> for_each_process_thread(g, p) {</span><br><span class="line"> <span class="keyword">if</span> (p != current && !<span class="built_in">freezer_should_skip</span>(p)</span><br><span class="line"> && <span class="built_in">freezing</span>(p) && !<span class="built_in">frozen</span>(p))</span><br><span class="line"> <span class="built_in">sched_show_task</span>(p);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">read_unlock</span>(&tasklist_lock);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">printk</span>(<span class="string">"(elapsed %d.%03d seconds) "</span>, elapsed_msecs / <span class="number">1000</span>,</span><br><span class="line"> elapsed_msecs % <span class="number">1000</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> todo ? -EBUSY : <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line">|| →</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">freeze_task</span><span class="params">(<span class="keyword">struct</span> task_struct *p)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="type">unsigned</span> <span class="type">long</span> flags;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * This check can race with freezer_do_not_count, but worst case that</span></span><br><span class="line"><span class="comment"> * will result in an extra wakeup being sent to the task. It does not</span></span><br><span class="line"><span class="comment"> * race with freezer_count(), the barriers in freezer_count() and</span></span><br><span class="line"><span class="comment"> * freezer_should_skip() ensure that either freezer_count() sees</span></span><br><span class="line"><span class="comment"> * freezing == true in try_to_freeze() and freezes, or</span></span><br><span class="line"><span class="comment"> * freezer_should_skip() sees !PF_FREEZE_SKIP and freezes the task</span></span><br><span class="line"><span class="comment"> * normally.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">freezer_should_skip</span>(p))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">spin_lock_irqsave</span>(&freezer_lock, flags);</span><br><span class="line"> <span class="comment">// (4.2.1) 检查当前进程是否可以被 freeze,</span></span><br><span class="line"> <span class="comment">// 或者是否已经被 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">freezing</span>(p) || <span class="built_in">frozen</span>(p)) {</span><br><span class="line"> <span class="built_in">spin_unlock_irqrestore</span>(&freezer_lock, flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (4.2.2) 如果是用户进程,伪造一个 signal 发送给进程</span></span><br><span class="line"> <span class="keyword">if</span> (!(p->flags & PF_KTHREAD))</span><br><span class="line"> <span class="built_in">fake_signal_wake_up</span>(p);</span><br><span class="line"> <span class="comment">// (4.2.3) 如果是内核进程,wake_up 内核进程</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">wake_up_state</span>(p, TASK_INTERRUPTIBLE);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">spin_unlock_irqrestore</span>(&freezer_lock, flags);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line">||| →</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">bool</span> <span class="title">freezing</span><span class="params">(<span class="keyword">struct</span> task_struct *p)</span></span></span><br><span class="line"><span class="function"></span>{ 具体代码分析如下:</span><br><span class="line"></span><br><span class="line">- kernel/power/process.c:</span><br><span class="line">- kernel/freezer.c:</span><br><span class="line"> <span class="comment">// 如果 system_freezing_cnt 为 0,说明全局 freeze 还没有开始</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">likely</span>(!<span class="built_in">atomic_read</span>(&system_freezing_cnt)))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">freezing_slow_path</span>(p);</span><br><span class="line">}</span><br><span class="line">|||| →</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">freezing_slow_path</span><span class="params">(<span class="keyword">struct</span> task_struct *p)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// (PF_NOFREEZE | PF_SUSPEND_TASK) 当前进程不能被 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (p->flags & (PF_NOFREEZE | PF_SUSPEND_TASK))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">test_thread_flag</span>(TIF_MEMDIE))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果 pm_nosig_freezing 为 true,内核进程 freeze 已经开始,</span></span><br><span class="line"> <span class="comment">// 当前进程可以被 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (pm_nosig_freezing || <span class="built_in">cgroup_freezing</span>(p))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果 pm_freezing 为 true,且当前进程为用户进程</span></span><br><span class="line"> <span class="comment">// 当前进程可以被 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (pm_freezing && !(p->flags & PF_KTHREAD))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="3-1-用户进程-freeze"><a href="#3-1-用户进程-freeze" class="headerlink" title="3.1 用户进程 freeze"></a>3.1 用户进程 freeze</h3><p>freeze 用户态的进程利用了 signal 机制,系统 suspend 使能了 suspend 以后,调用 <code>fake_signal_wake_up()</code> 伪造一个信号唤醒进程,进程在 <code>ret_to_user()</code> -> <code>do_notify_resume()</code> -> <code>do_signal()</code> -> <code>get_signal()</code> -> <code>try_to_freeze()</code> 中 freeze 自己。</p>
<p>具体代码分析如下:</p>
<ul>
<li>kernel/freezer.c:</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">bool</span> <span class="title">try_to_freeze</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (!(current->flags & PF_NOFREEZE))</span><br><span class="line"> <span class="built_in">debug_check_no_locks_held</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">try_to_freeze_unsafe</span>();</span><br><span class="line">}</span><br><span class="line">| →</span><br><span class="line"><span class="function"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">bool</span> <span class="title">try_to_freeze_unsafe</span><span class="params">(<span class="type">void</span>)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">might_sleep</span>();</span><br><span class="line"> <span class="comment">// 当前进程是否可以被 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">likely</span>(!<span class="built_in">freezing</span>(current)))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">// 调用 __refrigerator() freeze 当前进程</span></span><br><span class="line"> <span class="keyword">return</span> __refrigerator(<span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line">|| →</span><br><span class="line"><span class="type">bool</span> __refrigerator(<span class="type">bool</span> check_kthr_stop)</span><br><span class="line">{</span><br><span class="line"> <span class="comment">/* Hmm, should we be allowed to suspend when there are realtime</span></span><br><span class="line"><span class="comment"> processes around? */</span></span><br><span class="line"> <span class="type">bool</span> was_frozen = <span class="literal">false</span>;</span><br><span class="line"> <span class="type">long</span> save = current->state;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">pr_debug</span>(<span class="string">"%s entered refrigerator\n"</span>, current->comm);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (;;) {</span><br><span class="line"> <span class="comment">// (1) 设置当前进程进入 TASK_UNINTERRUPTIBLE 阻塞状态</span></span><br><span class="line"> <span class="built_in">set_current_state</span>(TASK_UNINTERRUPTIBLE);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">spin_lock_irq</span>(&freezer_lock);</span><br><span class="line"> <span class="comment">// (2) 设置已经 freeze 标志 PF_FROZEN</span></span><br><span class="line"> current->flags |= PF_FROZEN;</span><br><span class="line"> <span class="comment">// (3) 如果当前进程已经不是 freeze 状态,</span></span><br><span class="line"> <span class="comment">// 退出 freeze</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">freezing</span>(current) ||</span><br><span class="line"> (check_kthr_stop && <span class="built_in">kthread_should_stop</span>()))</span><br><span class="line"> current->flags &= ~PF_FROZEN;</span><br><span class="line"> <span class="built_in">spin_unlock_irq</span>(&freezer_lock);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!(current->flags & PF_FROZEN))</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> was_frozen = <span class="literal">true</span>;</span><br><span class="line"> <span class="built_in">schedule</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">pr_debug</span>(<span class="string">"%s left refrigerator\n"</span>, current->comm);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Restore saved task state before returning. The mb'd version</span></span><br><span class="line"><span class="comment"> * needs to be used; otherwise, it might silently break</span></span><br><span class="line"><span class="comment"> * synchronization which depends on ordered task state change.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">set_current_state</span>(save);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> was_frozen;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="3-2-内核进程-freeze"><a href="#3-2-内核进程-freeze" class="headerlink" title="3.2 内核进程 freeze"></a>3.2 内核进程 freeze</h3><p>内核进程对 freeze 的响应,有两个问题:</p>
<ul>
<li>wake_up_state(p, TASK_INTERRUPTIBLE) 能唤醒哪些内核进程。</li>
<li>内核进程怎么样来响应 freeze 状态,怎么样来 freeze 自己。</li>
</ul>
<p>如果进程阻塞在信号量、mutex 等内核同步机制上,wake_up_state 并不能解除阻塞。因为这些机制都有 while(1) 循环来判断条件,是否成立,不成立只是简单的唤醒随即又会进入阻塞睡眠状态。</p>
<ul>
<li>kernel/locking/mutex.c:</li>
<li><code>mutex_lock()</code> -> <code>__mutex_lock_common()</code></li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">__mutex_lock_common(<span class="keyword">struct</span> mutex *lock, <span class="type">long</span> state, <span class="type">unsigned</span> <span class="type">int</span> subclass,</span><br><span class="line"> <span class="keyword">struct</span> lockdep_map *nest_lock, <span class="type">unsigned</span> <span class="type">long</span> ip,</span><br><span class="line"> <span class="keyword">struct</span> ww_acquire_ctx *ww_ctx, <span class="type">const</span> <span class="type">bool</span> use_ww_ctx)</span><br><span class="line">{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (;;) {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Lets try to take the lock again - this is needed even if</span></span><br><span class="line"><span class="comment"> * we get here for the first time (shortly after failing to</span></span><br><span class="line"><span class="comment"> * acquire the lock), to make sure that we get a wakeup once</span></span><br><span class="line"><span class="comment"> * it's unlocked. Later on, if we sleep, this is the</span></span><br><span class="line"><span class="comment"> * operation that gives us the lock. We xchg it to -1, so</span></span><br><span class="line"><span class="comment"> * that when we release the lock, we properly wake up the</span></span><br><span class="line"><span class="comment"> * other waiters. We only attempt the xchg if the count is</span></span><br><span class="line"><span class="comment"> * non-negative in order to avoid unnecessary xchg operations:</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// (1) 如果 mutex_lock 条件成立,才退出</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">atomic_read</span>(&lock->count) >= <span class="number">0</span> &&</span><br><span class="line"> (<span class="built_in">atomic_xchg</span>(&lock->count, <span class="number">-1</span>) == <span class="number">1</span>))</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (2) 如果如果有信号阻塞,也退出</span></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * got a signal? (This code gets eliminated in the</span></span><br><span class="line"><span class="comment"> * TASK_UNINTERRUPTIBLE case.)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">unlikely</span>(<span class="built_in">signal_pending_state</span>(state, task))) {</span><br><span class="line"> ret = -EINTR;</span><br><span class="line"> <span class="keyword">goto</span> err;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (use_ww_ctx && ww_ctx->acquired > <span class="number">0</span>) {</span><br><span class="line"> ret = __mutex_lock_check_stamp(lock, ww_ctx);</span><br><span class="line"> <span class="keyword">if</span> (ret)</span><br><span class="line"> <span class="keyword">goto</span> err;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// (3) 否则继续进入阻塞休眠状态</span></span><br><span class="line"> __set_task_state(task, state);</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* didn't get the lock, go to sleep: */</span></span><br><span class="line"> <span class="built_in">spin_unlock_mutex</span>(&lock->wait_lock, flags);</span><br><span class="line"> <span class="built_in">schedule_preempt_disabled</span>();</span><br><span class="line"> <span class="built_in">spin_lock_mutex</span>(&lock->wait_lock, flags);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>所以 <code>wake_up_state()</code> 只能唤醒这种简单阻塞的内核进程,而对于阻塞在内核同步机制上是无能无力的:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">user_thread</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">set_current_state</span>(TASK_UNINTERRUPTIBLE);</span><br><span class="line"> <span class="built_in">schedule</span>();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>内核进程响应 freeze 操作,也必须显式的调用 <code>try_to_freeze()</code> 或者 <code>kthread_freezable_should_stop()</code> 来 freeze 自己:</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">user_thread</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">while</span> (!<span class="built_in">kthread_should_stop</span>()) {</span><br><span class="line"></span><br><span class="line"> <span class="built_in">try_to_freeze</span>();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>所以从代码逻辑上看内核进程 freeze,并不会 freeze 所有内核进程,只 freeze 了 2 部分:一部分是设置了 WQ_FREEZABLE 标志的 workqueue,另一部分是内核进程主动调用 <code>try_to_freeze()</code> 并且在架构上设计的可以响应 freeze。</p>
<hr>
<!-- Pager -->
<ul class="pager">
<li class="previous">
<a href="//linux-thermal-framework-intro.html" data-toggle="tooltip" data-placement="top" title="Linux Thermal 框架解析 ">← Previous Post</a>
</li>
<li class="next">
<a href="//linux-signal.html" data-toggle="tooltip" data-placement="top" title="Linux Signal">Next Post →</a>
</li>
</ul>
<!-- tip start -->
<div class="comment_notes">
<p>
This is copyright.
</p>
</div>
<!-- tip end -->
<!-- Music start-->
<!-- Music end -->
<!-- Sharing -->
<div class="social-share" data-wechat-qrcode-helper="" align="center"></div>
<!-- css & js -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/css/share.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/social-share.js/1.0.16/js/social-share.min.js"></script>
<!-- Sharing -->
<!-- gitment start -->
<!-- gitment end -->
<!-- 来必力City版安装代码 -->
<!-- City版安装代码已完成 -->
<!-- disqus comment start -->
<!-- disqus comment end -->
</div>
<!-- Tabe of Content -->
<!-- Table of Contents -->
<!-- Sidebar Container -->
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
sidebar-container">
<!-- Featured Tags -->
<section>
<!-- no hr -->
<h5><a href="/tags/">FEATURED TAGS</a></h5>
<div class="tags">
<a class="tag" href="/tags/#kthread_stop" title="kthread_stop">kthread_stop</a>
<a class="tag" href="/tags/#kthread_park" title="kthread_park">kthread_park</a>
<a class="tag" href="/tags/#freeze_processes" title="freeze_processes">freeze_processes</a>
</div>
</section>
<!-- Friends Blog -->
<hr>
<h5>FRIENDS</h5>
<ul class="list-inline">
<li><a href="#" target="_blank">Other</a></li>
</ul>
</div>
</div>
</div>
</article>
<!-- async load function -->
<script>
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
</script>
<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
<script>
async("https://cdn.bootcss.com/anchor-js/1.1.1/anchor.min.js",function(){
anchors.options = {
visible: 'hover',
placement: 'left',
icon: 'ℬ'
};
anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
})
</script>
<style type="text/css">
/* place left on bigger screen */
@media all and (min-width: 800px) {
.anchorjs-link{
position: absolute;
left: -0.75em;
font-size: 1.1em;
margin-top : -0.1em;
}
}
</style>
<!-- Footer -->
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
</ul>
<p class="copyright text-muted">
Copyright © meizu 2024
<br>
Powered by
<a href="https://github.com/dusign/hexo-theme-snail">
<i>hexo-theme-snail</i>
</a> |
<iframe name="star" style="margin-left: 2px; margin-bottom:-5px;" frameborder="0" scrolling="0"
width="100px" height="20px"
src="https://ghbtns.com/github-btn.html?user=dusign&repo=hexo-theme-snail&type=star&count=true">
</iframe>
</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="js/jquery.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/bootstrap.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/hux-blog.min.js"></script>
<!-- Search -->
<script src="js/search.js"></script>
<!-- async load function -->
<script>
function async(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
</script>
<!-- jquery.tagcloud.js -->
<script>
// only load tagcloud.js in tag.html
if($('#tag_cloud').length !== 0){
async("https://kernel.meizu.com/js/jquery.tagcloud.js",function(){
$.fn.tagcloud.defaults = {
//size: {start: 1, end: 1, unit: 'em'},
color: {start: '#bbbbee', end: '#0085a1'},
};
$('#tag_cloud a').tagcloud();
})
}
</script>
<!--fastClick.js -->
<script>
async("https://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
var $nav = document.querySelector("nav");
if($nav) FastClick.attach($nav);
})
</script>
<!-- Google Analytics -->
<script>
// dynamic User by Hux
var _gaId = 'UA-XXXXXXXX-X';
var _gaDomain = 'yoursite';
// Originial
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', _gaId, _gaDomain);
ga('send', 'pageview');
</script>
<!-- Baidu Tongji -->
<!-- Search -->
<script type="text/javascript">
var search_path = "search.xml";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "/" + search_path;
searchFunc(path, 'local-search-input', 'local-search-result');
</script>
<!-- busuanzi -->
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<a id="rocket" href="#top" class=""></a>
<script type="text/javascript" src="/js/totop.js?v=1.0.0" async=""></script>
<script type="text/javascript" src="/js/toc.js?v=1.0.0" async=""></script>
<!-- background effects line -->
<script type="text/javascript" src="/js/mouse-click.js" content='["🌱","just do it","🍀"]' color='["rgb(121,93,179)" ,"rgb(76,180,231)" ,"rgb(184,90,154)"]'></script>
<!-- background effects end -->
<!--<script size="50" alpha='0.3' zIndex="-999" src="/js/ribbonStatic.js"></script>-->
<script src="/js/ribbonDynamic.js"></script>
</body>
</html>