-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
949 lines (750 loc) · 66.2 KB
/
atom.xml
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
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[技术男儿裆自强]]></title>
<link href="http://geeksavetheworld.com/atom.xml" rel="self"/>
<link href="http://geeksavetheworld.com/"/>
<updated>2013-10-10T17:22:47+08:00</updated>
<id>http://geeksavetheworld.com/</id>
<author>
<name><![CDATA[wenbin wang]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[在php中设置cookie为httponly来减轻xss攻击的危害]]></title>
<link href="http://geeksavetheworld.com/blog/2013/10/09/php-set-cookie-httponly-to-prevent-xss-attack/"/>
<updated>2013-10-09T16:10:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2013/10/09/php-set-cookie-httponly-to-prevent-xss-attack</id>
<content type="html"><![CDATA[<p>目前站点一般通过cookie保存登陆、认证等敏感信息,而通过xss窃取cookie,以实现窃取敏感信息、伪造用户登陆等,是一种常见的攻击方式。</p>
<p>防止xss的主要方法是对用户输入内容进行转义等等,可参见此雄文:
<a href="https://www.golemtechnologies.com/articles/prevent-xss">Cross Site Scripting (XSS) Attacks: Methodology and Prevention</a>。
本文主要讨论在php下如何设置cookie为httponly,来最大限度的减轻xss攻击的危害,提升网站的安全性。</p>
<p>httponly是微软在Internet Explorer 6 SP1中为cookie引入的一个新特性,客户端脚本无法访问具有改属性的cookie值。
敏感cookie都应设置为httponly,减轻xss攻击的危害。</p>
<p>目前所有主流浏览器都支持该特性,php从5.2.0开始引入该特性。
只需在调用<code>setcookie</code>函数时,传入相关参数即可,如:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nx">setcookie</span><span class="p">(</span><span class="s2">"key"</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="nb">time</span><span class="p">()</span><span class="o">+</span><span class="mi">60</span> <span class="o">*</span> <span class="mi">60</span> <span class="o">*</span> <span class="mi">24</span> <span class="o">*</span> <span class="mi">10</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="k">FALSE</span><span class="p">,</span> <span class="k">TRUE</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>具体可见<a href="http://php.net/manual/en/function.setcookie.php">php文档</a>。</p>
<p>需要注意的是,销毁cookie时,<code>setcookie</code>后四个参数必须与设置cookie时严格相同,否则无法正确销毁。
例如销毁上面设置的cookie,需调用:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nx">setcookie</span><span class="p">(</span><span class="s2">"key"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="nb">time</span><span class="p">()</span><span class="o">-</span><span class="mi">3600</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="k">FALSE</span><span class="p">,</span> <span class="k">TRUE</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>还有一点特别需要注意,目前php的默认配置中,都会使用cookie来储存session id,见如下配置:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nx">session</span><span class="o">.</span><span class="nx">use_cookies</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'><span class="nx">session</span><span class="o">.</span><span class="nx">use_only_cookies</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class='line'><span class="nx">session</span><span class="o">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">PHPSESSID</span>
</span></code></pre></td></tr></table></div></figure>
<p>因此,有被截获cookie中的PHPSESSID,来劫持当前登陆用户会话的风险。
设置保存session id的PHPSESSID为httponly有两种方法。</p>
<ol>
<li>修改php.ini,设置<code>session.cookie_httponly = 1</code>;</li>
<li>在调用<code>start_session()</code>之前调用<code>session_set_cookie_params()</code>,设置相关参数。如:</li>
</ol>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nb">session_set_cookie_params</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s2">"/"</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="k">FALSE</span><span class="p">,</span> <span class="k">TRUE</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>具体可见<a href="http://php.net/manual/en/function.session-set-cookie-params.php">php文档</a>。</p>
<p>两种方法各有优缺点。方法1必须改动php.ini,在某些虚拟主机无法实现,但方便快捷,修改一次全局生效。
方法2需要在每个<code>start_session()</code>调用,可能容易漏掉,但灵活性更强,也不用依赖配置环境。</p>
<p>等用户退出等需要主动销毁session的时候,除了<code>session_destroy()</code>以外,还需要销毁cookie中的PHPSESSID。</p>
<p>一般资料中介绍的方法如下</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_COOKIE</span><span class="p">[</span><span class="nb">session_name</span><span class="p">()]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">setcookie</span><span class="p">(</span><span class="nb">session_name</span><span class="p">(),</span> <span class="s1">''</span><span class="p">,</span> <span class="nb">time</span><span class="p">()</span> <span class="o">-</span> <span class="mi">3600</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>但如前所述,如果销毁时<code>setcookie</code>参数跟创建时不同,将无法正确销毁,用改方法基本都会失败。
正确的做法为:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="nv">$sessionName</span> <span class="o">=</span> <span class="nb">session_name</span><span class="p">();</span>
</span><span class='line'><span class="nv">$sessionCookie</span> <span class="o">=</span> <span class="nb">session_get_cookie_params</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'><span class="nb">session_destroy</span><span class="p">();</span>
</span><span class='line'><span class="nv">$_SESSION</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_COOKIE</span><span class="p">[</span><span class="nv">$sessionName</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">setcookie</span><span class="p">(</span><span class="nv">$sessionName</span><span class="p">,</span> <span class="k">false</span><span class="p">,</span> <span class="nv">$sessionCookie</span><span class="p">[</span><span class="s1">'lifetime'</span><span class="p">],</span> <span class="nv">$sessionCookie</span><span class="p">[</span><span class="s1">'path'</span><span class="p">],</span> <span class="nv">$sessionCookie</span><span class="p">[</span><span class="s1">'domain'</span><span class="p">],</span> <span class="nv">$sessionCookie</span><span class="p">[</span><span class="s1">'secure'</span><span class="p">]);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[gentoo下使用nginx+fastcgi部署trac]]></title>
<link href="http://geeksavetheworld.com/blog/2013/10/07/trac-plus-nginx-plus-fastcgi-plus-gentoo/"/>
<updated>2013-10-07T23:14:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2013/10/07/trac-plus-nginx-plus-fastcgi-plus-gentoo</id>
<content type="html"><![CDATA[<p>首先,运行</p>
<pre><code>emerge --ask nginx trac
</code></pre>
<p>安装所需应用。</p>
<p>需注意确保trac的USES标记中含有fastcgi与vhosts。
如果没有vhosts,trac安装完成后会自动调用webapp-config在localhost虚拟目录下创建trac应用,而本人更倾向于根据需要手动安装。</p>
<p>运行</p>
<pre><code>webapp-config -I -h trac trac 1.0.1
</code></pre>
<p>将trac安装在名为trac的虚拟目录下。
再运行</p>
<pre><code>trac-admin /path/to/project initenv
</code></pre>
<p>按提示创建trac环境。
详细介绍及trac环境的目录结构见<a href="http://trac.edgewall.org/wiki/TracEnvironment">wiki</a>。</p>
<p>发布静态资源,以便通过nginx直接提供,提高效率:</p>
<pre><code>trac-admin /path/to/project deploy /var/www/trac
</code></pre>
<p>如配置为多项目,则需将<code>/var/www/trac/htdocs</code>下<code>site</code>与<code>common</code>目录移至<code>/var/www/trac/htdocs/project</code>下。</p>
<p>以nginx+fastcgi方式部署trac时,前端nginx做转发,后端fastcgi运行方式有两种:</p>
<ul>
<li>利用spawn-fcgi等服务,管理运行trac虚拟目录下cgi-bin/trac.fcgi脚本。</li>
<li>以fcgi协议运行<a href="http://trac.edgewall.org/wiki/TracStandalone">tracd</a>服务。</li>
</ul>
<p>由于使用tracd配置最为便捷,其他人提及的又相对较少,所以本文主要介绍后者。</p>
<!-- more -->
<p>运行</p>
<pre><code>tracd --protocol=fcgi /path/to/project
</code></pre>
<p>进行测试,出现以下错误:</p>
<pre><code>ImportError: No module named flup.server.fcgi
</code></pre>
<p>查看错误代码,发现是因为当tracd运行协议为fcgi、scgi等时,实现依赖flup,而emeger安装trac时并没有自动安装该依赖。
运行</p>
<pre><code>emerge --ask flup
</code></pre>
<p>安装flup后,tracd正常运行,无错误发生。</p>
<p>编辑tracd配置文件(<code>/etc/conf.d/tracd</code>),修改<code>TRACD_OPTS</code>段落为:</p>
<pre><code>TRACD_OPTS="--protocol=fcgi --env-parent-dir /path/to" (多项目)
</code></pre>
<p>或</p>
<pre><code>TRACD_OPTS="--protocol=fcgi -s /path/to/project" (单项目)
</code></pre>
<p>更多tracd选项请运行<code>tracd --help</code>查看。</p>
<p>启动tracd服务:</p>
<pre><code>/etc/init.d/tracd start
</code></pre>
<p>设置tracd开机自动运行:</p>
<pre><code>rc-update add tracd default
</code></pre>
<p>最后,配置nginx虚拟目录:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>server {
</span><span class='line'> listen 80; ## listen for ipv4; this line is default and implied
</span><span class='line'> server_name trac.example.com;
</span><span class='line'>
</span><span class='line'> access_log /var/log/nginx/trac.access_log main;
</span><span class='line'> error_log /var/log/nginx/trac.error_log notice;
</span><span class='line'>
</span><span class='line'> # 单项目静态资源配置,多项目环境只需下一段
</span><span class='line'> # it makes sense to serve static resources through Nginx
</span><span class='line'> location ~ /chrome/ {
</span><span class='line'> rewrite /chrome/(.*) /$1 break;
</span><span class='line'> root /var/www/trac/htdocs;
</span><span class='line'> access_log off;
</span><span class='line'> expires max;
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> # 多项目静态资源配置,单项目环境只需上一段
</span><span class='line'> # it makes sense to serve static resources through Nginx
</span><span class='line'> location ~ /(.*?)/chrome/ {
</span><span class='line'> rewrite /(.*?)/chrome/(.*) /$1/$2 break;
</span><span class='line'> root /var/www/trac/htdocs;
</span><span class='line'> access_log off;
</span><span class='line'> expires max;
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> location / {
</span><span class='line'> fastcgi_pass localhost:8000;
</span><span class='line'> include fastcgi_params;
</span><span class='line'> fastcgi_param SCRIPT_NAME "";
</span><span class='line'> fastcgi_param PATH_INFO $fastcgi_script_name;
</span><span class='line'> }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>
<p>注意,trac官方wiki中的配置方法为:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>server {
</span><span class='line'> ...
</span><span class='line'> # (Or ``^/some/prefix/(.*)``.
</span><span class='line'> if ($uri ~ ^/(.*)) {
</span><span class='line'> set $path_info /$1;
</span><span class='line'> }
</span><span class='line'> ...
</span><span class='line'> location / {
</span><span class='line'> fastcgi_pass localhost:8000;
</span><span class='line'> include fastcgi_params;
</span><span class='line'> fastcgi_param SCRIPT_NAME "";
</span><span class='line'> fastcgi_param PATH_INFO $path_info;
</span><span class='line'> }
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>
<p>正常情况下nginx在传递请求参数时会做url encode,但该段配置用正则表达式匹配请求参数,从而使变量$1、即$path_info不会被encode。
这种情况下,请求中存在中文的资源会无法正确解析,PATH_INFO设置为$fastcgi_script_name则表现正常。</p>
<p>重启nginx,部署完成。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[intel主板raid1灾难恢复小计]]></title>
<link href="http://geeksavetheworld.com/blog/2013/10/04/intel-fake-raid1-disaster-recovery/"/>
<updated>2013-10-04T10:32:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2013/10/04/intel-fake-raid1-disaster-recovery</id>
<content type="html"><![CDATA[<p>本人电脑用intel主板raid1来备份重要数据,昨天发现其中一块盘挂了。
二话不说换上一块新盘,开机Ctrl+I进入intel raid管理界面,设置好之后进入操作系统进行raid重建。</p>
<p>一是偷懒,二是想打会游戏,所以重建工作选择进入windows进行。
打开最新版Intel Matrix Storage Manager程序,会自动检测并重建raid1阵列。
也可以在linux下使用mdadm进行重建。</p>
<p>重建完成后,重启进入linux,运行<code>mount /dev/md126 /mnt</code>进行挂载,却出现以下错误:</p>
<blockquote><p>mount: wrong fs type, bad option, bad superblock on /dev/md126,
missing codepage or helper program, or other error
In some cases useful info is found in syslog – try
dmesg | tail or so</p></blockquote>
<p>用testdisk查看分区表:</p>
<pre><code>testdisk /list /dev/md126
</code></pre>
<p>与原始分区相符,目测分区表没有问题,应该是文件系统受损。</p>
<p>用fsck进行检测:</p>
<pre><code>fsck /dev/md126
</code></pre>
<p>出现以下提示:</p>
<blockquote><p>The filesystem size (according to the superblock) is 244189980 blocks
The physical size of the device is 244189952 blocks
Either the superblock or the partition table is likely to be corrupt!
Abort <y>?</p></blockquote>
<p>猜测是新换的硬盘跟原来的型号不一样(一个希捷一个西数),造成的上述问题。</p>
<p>输入y退出fsck检测,google一番后,找到如下解决方案:</p>
<pre><code>resize2fs -f /dev/md126 244189952
</code></pre>
<p>运行该命令将filesystem size重设为244189952(fsck检测时提示的physical size)。
重新运行<code>fsck /dev/md126</code>进行检测,无任何问题。挂载后查看数据,完好如初。</p>
<p>谨以此记。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[linux之主板raid(fake raid): dmraid or mdadm]]></title>
<link href="http://geeksavetheworld.com/blog/2012/12/19/fake-raid-on-linux-dmraid-or-mdadm/"/>
<updated>2012-12-19T16:40:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2012/12/19/fake-raid-on-linux-dmraid-or-mdadm</id>
<content type="html"><![CDATA[<p>当系统有多块硬盘时,通过组建<a href="http://en.wikipedia.org/wiki/RAID">raid</a>以提升磁盘性能或提供磁盘冗余,往往成为人们的首选考量。
当今主流raid实现方案大致可分为三种:</p>
<ul>
<li>硬件raid(hardware raid),通过购买昂贵的raid卡实现,我等平民不考虑。</li>
<li>软件raid(software raid),通过操作系统内软件创建阵列,raid处理开销由CPU负责。</li>
<li>主板raid(fake raid),通过主板内建raid控制器创建阵列,由操作系统驱动识别。</li>
</ul>
<p>需要注意的是,fake raid仅提供廉价的控制器,raid处理开销仍由CPU负责,因此性能与CPU占用基本与software raid持平。
如果只有单个linux系统,使用software raid一般比fake raid更健壮,但是,在多启动环境中(例如windows与linux双系统),为了使各个系统都能正确操作相同的raid分区,就必须使用fake raid了。
可参考<a href="https://help.ubuntu.com/community/FakeRaidHowto">FakeRaidHowto @ Community Ubuntu Documentation</a>以获得更多相关信息。</p>
<p>linux下最重要的raid管理程序为<a href="http://linux.die.net/man/8/dmraid">dmraid</a>与<a href="http://linux.die.net/man/8/mdadm">mdadm</a>。
本人印象中,前者用于识别rake raid,后者用于管理software raid,但最近更新系统时突然发现,openSUSE及rhel等发行版都改用mdadm替代dmraid来处理rake raid。
放狗搜索一番后,发现大家都有相似的疑问,便想到简单考察总结一番,对linux下rake raid应该使用dmraid还是mdadm进行管理做一个评判。</p>
<!--more-->
<p><em>说明:本人操作系统为openSUSE 12.2 x86_64,主板南桥为ICH10R,两块硬盘通过Intel Matrix Storage Manager Option Rom工具程序(开机自检时按Ctrl + I进入)设定为raid 0。</em></p>
<h2>起源与发展</h2>
<p>dmraid从linux kernel version 2.6.18起引入,由Red Hat维护,主要目的为管理fake raid,通过libdevmapper与device-mapper kernel runtime实现。</p>
<p>mdadm从linux kernel version 2.6.27起引入,由SUSE维护,主要目的为管理software raid,通过md (Multiple Devices) device driver实现。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>/sbin/dmraid --version
</span><span class='line'>dmraid version: 1.0.0.rc16-3 <span class="o">(</span>2010.01.12<span class="o">)</span>
</span><span class='line'>dmraid library version: 1.0.0.rc16-3 <span class="o">(</span>2010.01.12<span class="o">)</span>
</span><span class='line'>device-mapper version: unknown
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'><span class="nv">$ </span>/sbin/mdadm --version
</span><span class='line'>mdadm - v3.2.5 - 18th May 2012
</span></code></pre></td></tr></table></div></figure>
<p>由以上对比可以看出,dmraid自2010年初就没有更新过了,mdadm则一直更新频繁。
特别是,mdadm 3.0版本加入对<a href="https://raid.wiki.kernel.org/index.php/RAID_setup#External_Metadata">external metadata</a>的支持;mdadm 3.2.1版本的release说明中有如下内容:</p>
<blockquote><p>Secondly, the support for Intel Matrix Storage Manager (IMSM) arrays has been substantially enhanced. Spare migration is now possible as is level migration and OLCE (OnLine Capacity Expansion). This support is not quite complete yet and requires MDADM_EXPERIMENTAL=1 in the environment to ensure people only use it with care. In particular if you start a reshape in Linux and then shutdown and boot into Window, the Windows driver may not correctly restart the reshape. And vice-versa.</p></blockquote>
<p>自此,mdadm正式踏入管理fake raid的舞台。</p>
<h2>功能比较</h2>
<p>下图为<a href="http://download.intel.com/design/intarch/papers/326024.pdf">Intel文档</a>中对dmraid与mdadm的简单比较,以供参考:</p>
<p><img src="https://lh5.googleusercontent.com/-ZgSuXbQSn5E/UNK-0CcKq-I/AAAAAAAAAJE/uZAQg1Epow0/s1229/comparison_of_dm_and_md.png" alt="comparison of dmraid and mdadm" /></p>
<p>另外已知的区别有:</p>
<ul>
<li>mdadm可以使用其他电脑的磁盘进行恢复。</li>
<li>dmraid有时无法正确识别大硬盘(3TB imsm raid1被识别为746GB)。</li>
<li><em>不断添加中…</em></li>
</ul>
<h2>实战比较</h2>
<p>使用openSUSE 12.2 x86_64 DVD安装系统,dmraid与mdadm均能正确识别已经设置好的raid分区,正常安装、引导启动系统。唯一不同的是,dmraid需要设置单独的/boot分区,mdadm则无此限制。</p>
<p>安装完成后,用dd测试硬盘读写速度并监控CPU使用率,二者无任何差别。</p>
<h2>结论</h2>
<p>鉴于mdadm的各种优点与不断的更新完善,以及dmraid似乎停止开发,管理fake raid与software raid的首选均为mdadm。
另外,mdadm也得到了intel的极力推荐。</p>
<p>有关mdadm的具体使用,本文不再讨论,可参考<a href="http://download.intel.com/design/intarch/papers/326024.pdf">Intel® Rapid Storage Technology in Linux</a>。</p>
<p><strong><em>欢迎大家帮助补充更多相关资料。</em></strong></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Chartboost-x新鲜出炉: C++ wrapper of Chartboost for cocos2d-x]]></title>
<link href="http://geeksavetheworld.com/blog/2012/11/09/chartboost-x-new-released/"/>
<updated>2012-11-09T19:54:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2012/11/09/chartboost-x-new-released</id>
<content type="html"><![CDATA[<p>前几日团队的手游项目到了交付阶段,运营商要求在游戏中加入<a href="http://chartboost.com/">Chartboost</a>支持,以实现交叉推广。</p>
<p>该平台的最大特点就是:全屏广告!让玩家欲罢不能,横竖都得点一下-_–! 虽然对用户体验有一定影响,但据说效果很好,点击率超高,捷报频频。Backflip,Crowdstar, Disney, Gameloft, Get Set Games, GREE, Kabam, Kiloo, Playfirst, Pocket Gems, TinyCo等等大厂都在使用,实乃交叉推广之必备良药。</p>
<p>我们的游戏使用cocos2d-x引擎,逻辑代码由C++编写。为了在C++代码内直接调用Chartboost相关接口,本人对Chartboost提供的iOS和Android SDK做了一个简单的C++封装,方便使用。
最近闲来无事,对相关代码加以完善整理,取名为Chartboost-x,现正式放出,以造福大众!源代码与详细说明请见:</p>
<p><a href="https://github.com/wenbin1989/Chartboost-x">Chartboost-x GitHub页</a></p>
<p>该封装基本覆盖了Chartboost SDK的所有接口,实现了包括Delegate等功能,运行良好,使用简单。如果您碰巧也使用cocos2d-x,碰巧也需要Chartboost,那一定要clone下来看看,或许能节省您不少宝贵时间。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[向Octopress添加JiaThis分享工具及冲突解决]]></title>
<link href="http://geeksavetheworld.com/blog/2012/11/05/add-jiathis-to-octopress-blog/"/>
<updated>2012-11-05T19:08:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2012/11/05/add-jiathis-to-octopress-blog</id>
<content type="html"><![CDATA[<p>Octopress默认集成twitter、google +1、facebook like等分享工具,只需补全配置文件<code>_config.yml</code>中的相关信息即可。</p>
<p>但是,但是,作为水深火热的墙内人民,这些工具大部分状况下都处于瘫痪状态,甚至会拖慢整个站点的加载速度。
权衡起见,只得在<code>_config.yml</code>中注释掉twitter、google +1、facebook like相关选项,
并添加对<a href="http://www.jiathis.com/">JiaThis</a>等分享工具的支持。</p>
<p>此外,JiaThis与Octopress的Video模块有一点小冲突,会导致分享工具条左下角被一个小白框遮盖,像一块烦人的膏药,需要做一定处理。</p>
<h2>1. 向_config.yml中添加JiaThis配置开关</h2>
<p>仿照原始风格,在<code>_config.yml</code>底部添加如下配置:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># JiaThis
</span><span class='line'>jiathis: true
</span></code></pre></td></tr></table></div></figure>
<p>以后,想要开启或关闭JiaThis,只需修改该配置值即可。</p>
<h2>2. 集成JiaThis代码</h2>
<p>修改<code>source/_include/post/sharing.html</code>,在最后一行<code></div></code>前添加如下代码:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'>{% if site.jiathis %}
</span><span class='line'> <span class="c"><!-- JiaThis Button BEGIN --></span>
</span><span class='line'> <span class="nt"><div</span> <span class="na">class=</span><span class="s">"jiathis_style"</span><span class="nt">></span>
</span><span class='line'> <span class="nt"><span</span> <span class="na">class=</span><span class="s">"jiathis_txt"</span><span class="nt">></span>分享到:<span class="nt"></span></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_button_tsina"</span><span class="nt">></span>新浪微博<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_button_googleplus"</span><span class="nt">></span>Google+<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_button_renren"</span><span class="nt">></span>人人网<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_button_qzone"</span><span class="nt">></span>QQ空间<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_button_tqq"</span><span class="nt">></span>腾讯微博<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">href=</span><span class="s">"http://www.jiathis.com/share"</span> <span class="na">class=</span><span class="s">"jiathis jiathis_txt jiathis_separator jtico jtico_jiathis"</span> <span class="na">target=</span><span class="s">"_blank"</span><span class="nt">></span>更多<span class="nt"></a></span>
</span><span class='line'> <span class="nt"><a</span> <span class="na">class=</span><span class="s">"jiathis_counter_style"</span><span class="nt">></a></span>
</span><span class='line'> <span class="nt"></div></span>
</span><span class='line'> <span class="nt"><script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"http://v2.jiathis.com/code/jia.js?uid=1334720487296344"</span> <span class="na">charset=</span><span class="s">"utf-8"</span><span class="nt">></script></span>
</span><span class='line'> <span class="c"><!-- JiaThis Button END --></span>
</span><span class='line'>{% endif %}
</span></code></pre></td></tr></table></div></figure>
<p>其中2到14行为从<a href="http://www.jiathis.com/">http://www.jiathis.com/</a>获取的分享代码,可自行定制。</p>
<!-- more -->
<h2>3. 解决JiaThis与Octopress冲突</h2>
<p>Octopress会对所有有<code>movie</code>属性的<code>object</code>标签添加一层形如<code><div class="flash-video"><div></code>的wrapper,用于视频回放。
实现代码在<code>source/javascripts/octopress.js</code>中的<code>wrapFlashVideos()</code>函数:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">function</span> <span class="nx">wrapFlashVideos</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'object'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">object</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">object</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">object</span><span class="p">);</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'param[name=movie]'</span><span class="p">,</span> <span class="nx">object</span><span class="p">).</span><span class="nx">length</span> <span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">wrapper</span> <span class="o">=</span> <span class="nx">object</span><span class="p">.</span><span class="nx">before</span><span class="p">(</span><span class="s1">'<div class="flash-video"><div>'</span><span class="p">).</span><span class="nx">previous</span><span class="p">();</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="nx">wrapper</span><span class="p">).</span><span class="nx">children</span><span class="p">().</span><span class="nx">append</span><span class="p">(</span><span class="nx">object</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'iframe[src*=vimeo],iframe[src*=youtube]'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">iframe</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">iframe</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">iframe</span><span class="p">);</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">wrapper</span> <span class="o">=</span> <span class="nx">iframe</span><span class="p">.</span><span class="nx">before</span><span class="p">(</span><span class="s1">'<div class="flash-video"><div>'</span><span class="p">).</span><span class="nx">previous</span><span class="p">();</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="nx">wrapper</span><span class="p">).</span><span class="nx">children</span><span class="p">().</span><span class="nx">append</span><span class="p">(</span><span class="nx">iframe</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>这层wrapper即是形成JiaThis分享工具条左下角小白框的原因。我们需要对<code>object</code>做一个判断,过滤掉JiaThis的object。
将<code>wrapFlashVideos()</code>函数改为以下代码即可:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="kd">function</span> <span class="nx">wrapFlashVideos</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'object'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">object</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">object</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">object</span><span class="p">);</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">object</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s1">'id'</span><span class="p">)</span> <span class="o">!=</span> <span class="s2">"JIATHISSWF"</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span> <span class="nx">$</span><span class="p">(</span><span class="s1">'param[name=movie]'</span><span class="p">,</span> <span class="nx">object</span><span class="p">).</span><span class="nx">length</span> <span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">wrapper</span> <span class="o">=</span> <span class="nx">object</span><span class="p">.</span><span class="nx">before</span><span class="p">(</span><span class="s1">'<div class="flash-video"><div>'</span><span class="p">).</span><span class="nx">previous</span><span class="p">();</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="nx">wrapper</span><span class="p">).</span><span class="nx">children</span><span class="p">().</span><span class="nx">append</span><span class="p">(</span><span class="nx">object</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="s1">'iframe[src*=vimeo],iframe[src*=youtube]'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">iframe</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">iframe</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="nx">iframe</span><span class="p">);</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">wrapper</span> <span class="o">=</span> <span class="nx">iframe</span><span class="p">.</span><span class="nx">before</span><span class="p">(</span><span class="s1">'<div class="flash-video"><div>'</span><span class="p">).</span><span class="nx">previous</span><span class="p">();</span>
</span><span class='line'> <span class="nx">$</span><span class="p">(</span><span class="nx">wrapper</span><span class="p">).</span><span class="nx">children</span><span class="p">().</span><span class="nx">append</span><span class="p">(</span><span class="nx">iframe</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MacVim处理中文时卡顿、CPU占用过高问题]]></title>
<link href="http://geeksavetheworld.com/blog/2012/11/02/macvim-cpu-stuck/"/>
<updated>2012-11-02T16:34:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2012/11/02/macvim-cpu-stuck</id>
<content type="html"><![CDATA[<p>问题出现在<a href="https://github.com/downloads/b4winckler/macvim/MacVim-snapshot-65.tbz">MacVim-snapshot-65</a>版本</p>
<p>本人一般用MacVim编辑文档。近期发现编辑含有大段中文的markdown文档时,出现卡顿、MacVim进程CPU占用严重过高问题。
google搜索无果,自行研究,过程如下:</p>
<ul>
<li>打开纯英文markdown文档,不管多长都不会出现卡顿问题。</li>
<li>打开大段中文txt文件,未出现卡顿问题。</li>
<li>考虑可能是语法高亮造成卡顿问题,关闭语法高亮,大段中文的markdown文档未出现卡顿问题。</li>
</ul>
<p>虽然关闭语法高亮可以解决卡顿问题,但是。。。这个必须得有啊!!!</p>
<p>继续研究,偶然发现,在<em>Preferences</em>对话框中选择<em>Advanced</em>选项卡,去掉<em>Use Core Text renderer</em>选项的勾勾,
则打开语法高亮后,大段中文也不会造成卡顿,至此问题解决。</p>
<p>虽然去掉<em>Use Core Text renderer</em>选项后,渲染效果变差一点点,无法使用透明效果,偶尔出现残像,但总比没有语法高亮强。
Sigh…</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[小议libjpeg解压损坏文件时的错误处理]]></title>
<link href="http://geeksavetheworld.com/blog/2012/11/01/libjpeg-errors-when-decompress-broken-image/"/>
<updated>2012-11-01T22:48:00+08:00</updated>
<id>http://geeksavetheworld.com/blog/2012/11/01/libjpeg-errors-when-decompress-broken-image</id>
<content type="html"><![CDATA[<p>本文主要介绍libjpeg解压损坏文件时,可能的一些错误与处理方法。libjpeg的常规使用请参阅文档或其他文章。</p>
<h2>问题1:错误处理</h2>
<p>使用libjpeg解压文件时难免产生错误,原因可能是图片文件损坏、io错误、内存不足等等。
默认的错误处理函数会调用exit()函数,导致整个进程结束,这对用户来说是非常不友好的。我们需要注册自定义错误处理函数,改变此行为。</p>
<p>libjpeg采用c语言的setjmp/longjmp机制实现错误处理,首先需要包含以下头文件:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="cp">#include <setjmp.h></span>
</span></code></pre></td></tr></table></div></figure>
<p>定义错误处理结构体:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="k">struct</span> <span class="n">my_error_mgr</span> <span class="p">{</span>
</span><span class='line'> <span class="k">struct</span> <span class="n">jpeg_error_mgr</span> <span class="n">pub</span><span class="p">;</span> <span class="cm">/* "public" fields */</span>
</span><span class='line'>
</span><span class='line'> <span class="kt">jmp_buf</span> <span class="n">setjmp_buffer</span><span class="p">;</span> <span class="cm">/* for return to caller */</span>
</span><span class='line'><span class="p">};</span>
</span><span class='line'>
</span><span class='line'><span class="k">typedef</span> <span class="k">struct</span> <span class="n">my_error_mgr</span> <span class="o">*</span> <span class="n">my_error_ptr</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>
<p>实现错误处理函数:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">METHODDEF</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span><span class='line'><span class="n">my_error_exit</span> <span class="p">(</span><span class="n">j_common_ptr</span> <span class="n">cinfo</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="cm">/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */</span>
</span><span class='line'> <span class="n">my_error_ptr</span> <span class="n">myerr</span> <span class="o">=</span> <span class="p">(</span><span class="n">my_error_ptr</span><span class="p">)</span> <span class="n">cinfo</span><span class="o">-></span><span class="n">err</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* Always display the message. */</span>
</span><span class='line'> <span class="cm">/* We could postpone this until after returning, if we chose. */</span>
</span><span class='line'> <span class="p">(</span><span class="o">*</span><span class="n">cinfo</span><span class="o">-></span><span class="n">err</span><span class="o">-></span><span class="n">output_message</span><span class="p">)</span> <span class="p">(</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* Return control to the setjmp point */</span>
</span><span class='line'> <span class="n">longjmp</span><span class="p">(</span><span class="n">myerr</span><span class="o">-></span><span class="n">setjmp_buffer</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>以上代码只是简单的打印错误信息,之后调用longjmp,使控制流跳转到setjmp设置的代码块。
如果需要,还可通过cinfo->err->msg_code判断错误类型,进行进一步处理。</p>
<!-- more -->
<p>实现解压函数:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">GLOBAL</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
</span><span class='line'><span class="n">read_JPEG_file</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span> <span class="n">filename</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">struct</span> <span class="n">jpeg_decompress_struct</span> <span class="n">cinfo</span><span class="p">;</span>
</span><span class='line'> <span class="k">struct</span> <span class="n">my_error_mgr</span> <span class="n">jerr</span><span class="p">;</span>
</span><span class='line'> <span class="cm">/* More stuff */</span>
</span><span class='line'> <span class="kt">FILE</span> <span class="o">*</span> <span class="n">infile</span><span class="p">;</span> <span class="cm">/* source file */</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* In this example we want to open the input file before doing anything else,</span>
</span><span class='line'><span class="cm"> * so that the setjmp() error recovery below can assume the file is open.</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'> <span class="k">if</span> <span class="p">((</span><span class="n">infile</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s">"rb"</span><span class="p">))</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"can't open %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">filename</span><span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* We set up the normal JPEG error routines, then override error_exit. */</span>
</span><span class='line'> <span class="n">cinfo</span><span class="p">.</span><span class="n">err</span> <span class="o">=</span> <span class="n">jpeg_std_error</span><span class="p">(</span><span class="o">&</span><span class="n">jerr</span><span class="p">.</span><span class="n">pub</span><span class="p">);</span>
</span><span class='line'> <span class="n">jerr</span><span class="p">.</span><span class="n">pub</span><span class="p">.</span><span class="n">error_exit</span> <span class="o">=</span> <span class="n">my_error_exit</span><span class="p">;</span>
</span><span class='line'> <span class="cm">/* Establish the setjmp return context for my_error_exit to use. */</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">setjmp</span><span class="p">(</span><span class="n">jerr</span><span class="p">.</span><span class="n">setjmp_buffer</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'> <span class="cm">/* If we get here, the JPEG code has signaled an error.</span>
</span><span class='line'><span class="cm"> * We need to clean up the JPEG object, close the input file, and return.</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'> <span class="n">jpeg_destroy_decompress</span><span class="p">(</span><span class="o">&</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'> <span class="n">fclose</span><span class="p">(</span><span class="n">infile</span><span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* ...</span>
</span><span class='line'><span class="cm"> * 读取数据</span>
</span><span class='line'><span class="cm"> * ...</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* This is an important step since it will release a good deal of memory. */</span>
</span><span class='line'> <span class="n">jpeg_destroy_decompress</span><span class="p">(</span><span class="o">&</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="cm">/* After finish_decompress, we can close the input file.</span>
</span><span class='line'><span class="cm"> * Here we postpone it until after no more JPEG errors are possible,</span>
</span><span class='line'><span class="cm"> * so as to simplify the setjmp error logic above.</span>
</span><span class='line'><span class="cm"> */</span>
</span><span class='line'> <span class="n">fclose</span><span class="p">(</span><span class="n">infile</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>以上代码重点在于,注册my_error_exit回调函数,之后调用setjmp函数设置返回代码块。
发生错误时,回调my_error_exit函数,在my_error_exit函数末尾,控制流跳转到setjmp设置的代码块,在此代码块完成释放资源等操作。
这样,即使发生错误,也不至于进程退出,造成用户困扰。</p>
<h2>问题2:关于jpeg_finish_decompress()函数</h2>
<p>网上几乎所有使用libjpeg解压图片的代码,在解压完成后都会调用以下代码:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="cm">/* Finish decompression */</span>
</span><span class='line'><span class="n">jpeg_finish_decompress</span><span class="p">(</span><span class="o">&</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'><span class="cm">/* Release JPEG decompression object */</span>
</span><span class='line'><span class="n">jpeg_destroy_decompress</span><span class="p">(</span><span class="o">&</span><span class="n">cinfo</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>其中,jpeg_finish_decompress()函数的作用为:</p>
<ul>
<li>检查jpeg对象状态是否正常,并将其设为解压完成状态</li>
<li>读取输入数据源中剩余数据,直至EOI标记(0xFF,0xD9,标记jpeg图片结束)</li>
<li>释放jpeg对象分配的工作内存</li>
</ul>
<p>实现如下:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="n">GLOBAL</span><span class="p">(</span><span class="n">boolean</span><span class="p">)</span>
</span><span class='line'><span class="n">jpeg_finish_decompress</span> <span class="p">(</span><span class="n">j_decompress_ptr</span> <span class="n">cinfo</span><span class="p">)</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">((</span><span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">==</span> <span class="n">DSTATE_SCANNING</span> <span class="o">||</span>
</span><span class='line'> <span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">==</span> <span class="n">DSTATE_RAW_OK</span><span class="p">)</span> <span class="o">&&</span> <span class="o">!</span> <span class="n">cinfo</span><span class="o">-></span><span class="n">buffered_image</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="cm">/* Terminate final pass of non-buffered mode */</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="n">cinfo</span><span class="o">-></span><span class="n">output_scanline</span> <span class="o"><</span> <span class="n">cinfo</span><span class="o">-></span><span class="n">output_height</span><span class="p">)</span>
</span><span class='line'> <span class="n">ERREXIT</span><span class="p">(</span><span class="n">cinfo</span><span class="p">,</span> <span class="n">JERR_TOO_LITTLE_DATA</span><span class="p">);</span>
</span><span class='line'> <span class="p">(</span><span class="o">*</span><span class="n">cinfo</span><span class="o">-></span><span class="n">master</span><span class="o">-></span><span class="n">finish_output_pass</span><span class="p">)</span> <span class="p">(</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'> <span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">=</span> <span class="n">DSTATE_STOPPING</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">==</span> <span class="n">DSTATE_BUFIMAGE</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="cm">/* Finishing after a buffered-image operation */</span>
</span><span class='line'> <span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">=</span> <span class="n">DSTATE_STOPPING</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span> <span class="o">!=</span> <span class="n">DSTATE_STOPPING</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="cm">/* STOPPING = repeat call after a suspension, anything else is error */</span>
</span><span class='line'> <span class="n">ERREXIT1</span><span class="p">(</span><span class="n">cinfo</span><span class="p">,</span> <span class="n">JERR_BAD_STATE</span><span class="p">,</span> <span class="n">cinfo</span><span class="o">-></span><span class="n">global_state</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="cm">/* Read until EOI */</span>
</span><span class='line'> <span class="k">while</span> <span class="p">(</span><span class="o">!</span> <span class="n">cinfo</span><span class="o">-></span><span class="n">inputctl</span><span class="o">-></span><span class="n">eoi_reached</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">((</span><span class="o">*</span><span class="n">cinfo</span><span class="o">-></span><span class="n">inputctl</span><span class="o">-></span><span class="n">consume_input</span><span class="p">)</span> <span class="p">(</span><span class="n">cinfo</span><span class="p">)</span> <span class="o">==</span> <span class="n">JPEG_SUSPENDED</span><span class="p">)</span>
</span><span class='line'> <span class="k">return</span> <span class="n">FALSE</span><span class="p">;</span> <span class="cm">/* Suspend, come back later */</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="cm">/* Do final cleanup */</span>
</span><span class='line'> <span class="p">(</span><span class="o">*</span><span class="n">cinfo</span><span class="o">-></span><span class="n">src</span><span class="o">-></span><span class="n">term_source</span><span class="p">)</span> <span class="p">(</span><span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'> <span class="cm">/* We can use jpeg_abort to release memory and reset global_state */</span>
</span><span class='line'> <span class="n">jpeg_abort</span><span class="p">((</span><span class="n">j_common_ptr</span><span class="p">)</span> <span class="n">cinfo</span><span class="p">);</span>
</span><span class='line'> <span class="k">return</span> <span class="n">TRUE</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>从以上代码可以看到,jpeg_finish_decompress()函数是利用jpeg_abort()函数进行释放工作内存操作的。</p>
<p>解压损坏图片时,即使在调用jpeg_finish_decompress()之前的解压过程完全正常,调用jpeg_finish_decompress()时仍可能产生错误。
错误原因可能数据源结构损坏,导致丢失EOI标记,或有多个SOI标记等。
例如,解压下图时,调用jpeg_finish_decompress()之前完全正常,但调用jpeg_finish_decompress()时会产生错误:
<em>Invalid JPEG file structure: two SOI markers</em></p>
<p><img src="https://lh5.googleusercontent.com/-2fC4tAPLYzU/UJKDhHuaEOI/AAAAAAAAAHE/kHoVUrhC2-w/s800/3.jpg"></p>
<p>直觉上,如果在调用jpeg_finish_decompress()之前未发生错误,则直接利用所读取的图片数据用于显示等操作即可,
不必理会jpeg_finish_decompress()中可能产生的错误,进一步,甚至可能不必调用jpeg_finish_decompress()函数。
由此引出下一个问题:</p>
<h2>问题3:调用jpeg_destroy_decompress()之前是否有必要调用jpeg_finish_decompress()?</h2>
<p>由上文所述,调用jpeg_finish_decompress()的目的为:</p>
<ol>
<li>检查jpeg对象状态是否正常,并将其设为解压完成状态</li>
<li>读取输入数据源中剩余数据,直至EOI标记(0xFF,0xD9,标记jpeg图片结束)</li>
<li>释放jpeg对象分配的工作内存</li>
</ol>
<p>对于目的1,如果不需检测jpeg对象状态,或者不再继续使用该对象,可直接忽略。</p>
<p>对于目的2,如果不需读取并消耗剩余输入数据源数据,可直接忽略。</p>
<p>对于目的3,即使不调用jpeg_finish_decompress(),jpeg对象分配的工作内存也会在调用jpeg_destroy_decompress()时被释放,不会造成内存泄露。
此外,如果还要继续使用jpeg对象,也可调用jpeg_abort()函数进行释放工作内存操作。</p>
<p>可简单做出如下结论:</p>
<ul>
<li>如果只需得到解压数据,不再使用jpeg对象,则完全可以不必调用jpeg_finish_decompress()函数,直接调用jpeg_destroy_decompress()函数释放内存即可。</li>
<li>如果还需使用jpeg对象,但不在意对象状态,且忽略输入数据源剩余数据,可用jpeg_abort()代替jpeg_finish_decompress(),达到释放工作内存的目的。</li>
<li>如需检查jpeg对象状态,或者需要读取并消耗剩余输入数据源数据,则需调用jpeg_finish_decompress()函数。
此时需要注意jpeg_finish_decompress()可能产生的错误。</li>
</ul>
]]></content>
</entry>
</feed>