-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
591 lines (507 loc) · 113 KB
/
index.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
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>临风博客</title>
<link>https://jordonyang.github.io/</link>
<description>Recent content on 临风博客</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh_CN</language>
<copyright>All rights reserved</copyright>
<lastBuildDate>Sun, 10 Feb 2019 19:32:16 +0800</lastBuildDate>
<atom:link href="https://jordonyang.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>QuickSelect with BFPRT to Conquer Top-K Problem</title>
<link>https://jordonyang.github.io/post/alg/selecting/quickselect_bfprt/</link>
<pubDate>Sun, 10 Feb 2019 19:32:16 +0800</pubDate>
<guid>https://jordonyang.github.io/post/alg/selecting/quickselect_bfprt/</guid>
<description>0x01. 简介 top-K问题:在一堆数中求其前 k 大或前 k 小的问题,简称 TOP-K 问题,例如在LeetCode上就有这么一道题:215. Kth Largest Element in an Array。在首次接触 TOP-K 问题时,第一反应估计是用快速排序对所有数据进行一次排序,然后取其前 k 即可,但是这么做有两个问题:
快速排序的平均复杂度为 O(nlgn),但最坏时间复杂度为 O(n2),不能始终保证较好的复杂度; 我们只需要前 k 大的,而对其余不需要的数也进行了排序,浪费了大量排序时间。 除此之外,也可以选择堆排序,维护一个大小为 k 的堆,时间复杂度为 O(nlogk)。那是否还存在更有效的方法呢?目前用于解决TOP-K问题较为普遍的算法是带有BFPRT 算法的QuickSelect算法。
QuickSelect算法:用于在一个乱序的集合中找到第k小的元素,即用于解决top-K问题。与快速排序算法一样,作者也是Tony Hoare,同时它在实际应用中也十分高效,有着良好的平均性能,不过也有着较差的最坏性能。
大体上,quickselect算法使用了与快排一样的过程,选择一个集合元素作为中轴点,基于中轴点把数据分成相应地大于或小于中轴点的两块,然而不同于快排在两个分块中递归排序,quickselect算法在确定要查找的元素在哪一块后,会只在那一块中递归选择,这样就将平均复杂度从 O(log n) 降到了 O(n),尽管最坏复杂度还是 O(n2)。
与快排一样,quickselect算法通常也是被实现为原地算法,除了用于解决top-K问题,它还可以用于局部排序,本文不作讨论。
BFPRT 算法:又称为中位数的中位数算法(Median of medians),该算法由 Blum、Floyd、Pratt、Rivest、Tarjan 提出,最坏时间复杂度为O(n),是一种非精确的选择算法,主要用于为quickselect算法其他选择算法提供一个良好的中轴点,从而解决top-k问题。BFPRT 算法能在有限的线性级的时间内找到一个大致合适的中位数,当该中位数在quickselect算法中用作一个优化过的中轴点,quickselect算法在最坏情况下的复杂度将从平方级降到线性级。
0x02. 实现与解决 为了保证算法的线性运行时间,我们需要设计另一种方法,使所选的主元保证“接近”给定数字的中值。通过选择一个接近中值的主元,我们可以保证,在每次迭代中,我们将元素的搜索数量减少(几乎)一半。
将n个元素划为⌊n / 5⌋组,每组5个,至多只有一组由n mod 5个元素组成。 寻找这⌈n / 5⌉个组中每一个组的中位数,这个过程可以用插入排序。 对步骤2中的⌈n / 5⌉个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。 判断pivot的位置与k的大小,有选择的对左边或右边递归。 上面的步骤中1~3步为BFPRT 算法的过程,用于为quickselect算法选择一个好的中轴点,而4~5步则是quickselect算法的内容,其实BFPRT 算法就相当于是quickselect算法的一个辅助插件一般。下面先介绍QuickSelect算法的分区与选择过程。</description>
</item>
<item>
<title>Memory Semantic and Lock Optimizing of Synchronized</title>
<link>https://jordonyang.github.io/post/java/concurrency/sync/memory_semantic_optimizing/</link>
<pubDate>Sun, 20 Jan 2019 08:47:16 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/concurrency/sync/memory_semantic_optimizing/</guid>
<description>0x00. TOC 0x01.字节码分析 0x02.锁优化 1.自旋锁 2.锁消除 3.锁粗化 4.轻量级锁 4.1 加锁过程 4.2 解锁过程 5.偏向锁 6.锁的优缺点对比 0x03.参考 0x04.相关文章 0x01. 字节码分析 先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
普通同步方法:锁是当前实例对象。 静态同步方法:锁是当前类的Class对象。 同步方法块:锁是synchronized括号里配置的对象。 从JVM Spec中可以看到synchonized在 JVM 里的实现原理,JVM 基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。以下面代码为例:
package org.jordon.sync; import java.util.ArrayList; import java.util.List; public class SyncByteCodeTest { private static List&lt;String&gt; list = new ArrayList&lt;&gt;(); //当前实例的锁 public synchronized void add1(String s){ list.add(s); } //SynTest.class 锁 public static synchronized void add2(String s){ list.add(s); } //SynTest.class 锁 public void add3(String s){ synchronized(SyncByteCodeTest.</description>
</item>
<item>
<title>Summary of Sorting Algorithms</title>
<link>https://jordonyang.github.io/post/alg/sorting/summary/</link>
<pubDate>Fri, 18 Jan 2019 22:54:44 +0800</pubDate>
<guid>https://jordonyang.github.io/post/alg/sorting/summary/</guid>
<description>0x00. TOC 选择排序 冒泡排序 鸡尾酒排序 插入排序 希尔排序 归并排序 快速排序 堆排序 排序算法的复杂度 Happy Sorting 参考 选择排序 首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。因为它在不断地选择剩余元素之中的最小者,所以这种方法叫做选择排序。TOC
代码实现如下所示,选择排序的内循环只是在比较当前元素与目前已知的最小元素(以及将当前索引加 1 和检查是否代码越界),这已经简单到了极点。交换元素的代码写在内循环之外,每次交换都能排定一个元素,因此交换的总次数是 N。所以算法的时间效率取决于比较的次数。对于长度为 N 的数组,选择排序需要大约 N2/2 次比较和 N 次交换。
public static void sort(Comparable[] a) { int n = a.length; for (int i = 0; i &lt; n; i++) { int min = i; for (int j = i+1; j &lt; n; j++) { if (less(a[j], a[min])) min = j; } exchange(a, i, min); assert isSorted(a, 0, i); } assert isSorted(a); } 选择排序是一种很容易理解和实现的简单排序算法,它有两个很鲜明的特点。</description>
</item>
<item>
<title>Timeline</title>
<link>https://jordonyang.github.io/timeline/</link>
<pubDate>Tue, 15 Jan 2019 10:36:00 +0800</pubDate>
<guid>https://jordonyang.github.io/timeline/</guid>
<description>2019 【02.10】 使用QuickSelect结合BDPRT算法解决Top-K问题 【01.20】 深入理解synchronized内存语义及锁优化 【02.02】 基本排序算法总结 【01.15】 深入理解volatile的内存语义及其实现 【01.14】 如何正确使用volatile变量 【01.12】 自定义安全类加载器 【01.11】 Java虚拟机类加载过程 【01.10】 编程实现及理解SSL协议交互过程 2018 【12.28】 MySQL存储引擎基础 【12.23】 操作系统期末考试考点整理 【12.10】 使用Tomcat和OpenSSL自制证书和配置单向服务器身份验证 【12.06】 理解模板方法设计模式 【11.25】 理解比较并交换算法 【11.20】 从MESI理解缓存一致性协议 【11.12】 SSL - 握手协议详细过程 【11.12】 SSL - 协议架构和基本协议介绍 【11.10】 RSA公钥密码算法原理及实现过程 【11.07】 流密码原理及RC4算法的实现 【11.05】 理解素性测试及大素数生成过程 【10.18】 基于原理逐步实现AES加密算法 【10.11】 分组密码的工作模式 【10.09】 DES加密算法原理及实现 【10.01】 Java字节码结构及解析过程 【09.18】 基于二叉树数据结构的算术表达式生成器 【09.14】 基于责任链设计模式的代码文件分析统计器 【08.27】 基于JDK1.7的HashMap源码阅读 【08.04】 理解Java虚拟机垃圾收集机制 【06.25】 LinkedList源码阅读与分析 【06.23】 ArrayList源码阅读与分析 【05.07】 深入理解CPU高速缓存 【04.</description>
</item>
<item>
<title>Memory Semantics and Implementation of Volatile</title>
<link>https://jordonyang.github.io/post/java/concurrency/volatile/assembling_dive/</link>
<pubDate>Tue, 15 Jan 2019 09:18:23 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/concurrency/volatile/assembling_dive/</guid>
<description>0x01. volatile 特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。假设有多个线程分别调用下图左边程序的3个方法,这个程序在语义上和下图右边程序等价。
可见一个volatile变量的单个读/写操作,与一个普通变量的读/写操作都是使用同一个锁来同步,它们之间的执行效果相同。锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到任意线程对这个 volatile 变量最后的写入。
锁的语义决定了临界区代码的执行具有原子性。这意味着,即使是64位的 long 型和 double 型变量,只要它是volatile变量,对该变量的读/写就具有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。简而言之,volatile变量自身具有下列特性:
可见性。对一个 volatile 变量的读,总是能看到任意线程对这个 volatile 变量最后的写入。 原子性:对任意单个 volatile 变量的读/写具有原子性,但类似于 volatile++ 这种复合操作不具有原子性。 0x02. volatile 写-读内存语义 volatile写的内存语义:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。 volatile读的内存语义:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。 以下面示例程序VolatileExample为例,假设线程A首先执行writer()方法,随后线程B执行reader()方法,初始时两个线程的本地内存中的flag和a都是初始状态。下图左边是线程A执行volatile写后,共享变量的状态示意图。
class VolatileExample { int a = 0; volatile boolean flag = false; public void writer() { a = 1; // 1 flag = true; // 2 } public void reader() { if (flag) { // 3 int i = a; // 4 …… } } } 下面右边为线程B读同一个volatile变量后,共享变量的状态示意图。如图所示,在读flag变量后,本地内存B包含的值已经被置为无效。此时,线程B必须从主内存中读取共享变量。线程B的读取操作将导致本地内存B与主内存中的共享变量的值变成一致。如果我们把volatile写和volatile读两个步骤综合起来看的话,在读线程B读一个volatile变量后,写线程A在写这个volatile变量之前所有可见的共享变量的值都将立即变得对读线程B可见。</description>
</item>
<item>
<title>Guidelines for Using Volatile Variables</title>
<link>https://jordonyang.github.io/post/java/concurrency/volatile/guidelines/</link>
<pubDate>Mon, 14 Jan 2019 09:22:44 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/concurrency/volatile/guidelines/</guid>
<description>0x01. 简介 synchronized锁提供了两个主要特性:互斥和可见性。互斥意味着一次只能有一个线程持有给定的锁,该属性可用于实现协调对共享数据的访问的协议,以便一次只能有一个线程使用共享数据。可见性更为微妙,它与确保在释放锁之前对共享数据所做的更改对随后获取该锁的另一个线程可见有关,遵循happens-before规则,如果没有同步提供的可见性保证,线程可能会看到过时或不一致的值。
Java 语言中的volatile变量可以看作是synchronized lite,即synchronized的简化版,与synchronized块相比,它们需要更少的编码,运行时开销也更少,但是它们只能用于synchronized所能做的事情的一个子集。下面介绍一些有效使用volatile变量的模式,以及关于何时不使用它们的一些警告。
0x02. volatile 变量 volatile变量共享synchronized的可见性特性,但没有原子性特性。这意味着线程将自动看到volatile变量的最新值。它们可以用于提供线程安全,但仅在一组非常受限的情况下使用:不在多个变量之间或变量的当前值与其未来值之间施加约束的情况。因此,volatile本身不足以实现计数器、互斥锁或任何具有与多个变量相关的不变量的类(如start &lt;= end)。
出于两个主要原因之一,你可能更喜欢使用volatile变量而不是锁:简单性或可伸缩性。当使用volatile变量而不是锁时,一些习惯用法更容易编码和阅读。此外,volatile变量与锁不同,它们不会导致线程阻塞,因此它们不太可能导致可伸缩性问题。在读操作远远多于写操作的情况下,volatile变量还可能比锁提供性能优势。
1. 正确使用 volatile 的条件 只有在一组受限的情况下,才能使用volatile变量而不是锁。volatile变量必须满足以下两个条件才能提供所需的线程安全性:
对变量的写操作不依赖于它的当前值 变量不参与其他变量的不变条件,如start &lt;= end,若start为volatile的,则该条件语句为原子操作,先取值,再比较,有可能取完值后,在比较前发生变化。 基本上,这些条件表明可以写入volatile变量的有效值集独立于任何其他程序状态,包括该变量的当前状态。第一个条件使volatile变量不能用作线程安全的计数器。虽然x++的自增操作看起来像一个单独的操作,但它实际上是一个复合的读-修改-写操作序列,必须以原子方式执行,而volatile不提供必要的原子性。正确的操作要求x的值在操作期间保持不变,这是使用volatile变量无法实现的。但是,如果该值只从单线程写入,那么可以忽略第一个条件。
大多数编程情况都会与第一个或第二个条件发生冲突,这使得volatile变量在实现线程安全方面不如synchronized那么常用。下面代码清单显示了一个非线程安全的数字范围类。它包含的一个不变条件是下界lower总是小于或等于上界upper。
@NotThreadSafe public class NumberRange { private int lower, upper; public int getLower() { return lower; } public int getUpper() { return upper; } // 下溢出,设为最小 public void setLower(int value) { if (value &gt; upper) throw new IllegalArgumentException(...); lower = value; } // 上溢出,设为最大 public void setUpper(int value) { if (value &lt; lower) throw new IllegalArgumentException(.</description>
</item>
<item>
<title>Custom Secure Class Loader</title>
<link>https://jordonyang.github.io/post/java/vm/custom_secure_classloader/</link>
<pubDate>Sat, 12 Jan 2019 10:42:11 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/vm/custom_secure_classloader/</guid>
<description>0x00. 简介 近年来,初创企业和企业都在使用云服务。然而,它带来了安全问题。在这一点上,开发的代码与数据是不同的。关键数据应加密存储。另一方面,开发的代码大多是脆弱地安装在服务器上。大多数Java项目都是直接在服务器上运行后缀为jar或war的扩展文件。这些文件的层次结构中包含字节码文件。然而,有些反编译器可从字节码文件中提取原始Java代码,这对于专利性较强或者企业私有的代码而言是极为不利的。
对于这种情况,我们应该像加密关键数据一样加密重要的代码,将它们存储在云数据库中,并在运行时解密,以保护知识产权。这样,即使云系统被入侵,私密代码仍然是安全的,因为加密密钥不会存储在云系统中。
0x01. 编译方 假设存在非常重要的代码如下,使用IntelliJ IDEA或者javac.exe对其进行编译得到字节码文件,使用IntelliJ IDEA Decompiler或者其他反编译工具很容易就可以得到源码,下面将其放置到E:\\crypto中。
package org.jordon.vm; public class CoreClass { public static void main(String[] args) { System.out.println(&quot;hello world&quot;); } } 使用javax.crypto.Cipher工具包中的AES-128对编译后的CoreClass类的字节码文件进行加密,此处使用固定密钥进行测试,实际工程中应使用随机密钥。加密后的字节码文件放置到E:\crypto\org\jordon\vm中.
public class ClassEncryption { public static void main(String[] args) throws Exception{ String path = &quot;E:\\crypto&quot;; String classname = &quot;CoreClass&quot;; String algorithm = &quot;AES&quot;; // 16 x 8 = 128 位密钥 byte[] key = {75, 110, -45, -61, 101, -103, -26, -25, 55, -70, 19, 51, 66, -91, -35, 19}; System.</description>
</item>
<item>
<title>JVM - Class Loading Process</title>
<link>https://jordonyang.github.io/post/java/vm/class_loading/</link>
<pubDate>Fri, 11 Jan 2019 21:28:33 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/vm/class_loading/</guid>
<description>0x00. TOC 0x01.概述 0x02.类的生命周期 0x03.加载时机 0x04.加载过程 1.加载 2.连接 2.1.验证 2.2.准备 2.3.解析 3.初始化 0x05.载加载器 1.双亲委派模型 2.Tomcat类加载器 0x06.参考 0x01. 概述 类加载:虚拟机把描述类的数据从字节码文件加载待内存中,并对数据进行校验、转换解析和初始化,最终形成可被虚拟机直接使用的 Java 类型的过程。
0x02. 类的生命周期 其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持 Java 语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
0x03. 加载时机 JVMS 规定有且只有下面5种发生主动引用的场景才会触发类的初始化,这意味着在此之前的加载、验证和准备也会随之发生:TOC
遇到new、getstatic、putstatic或invokestatic这 4 条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这 4 条指令的最常见的 Java 代码场景是: 使用 new 关键字实例化对象 读取或设置一个类的静态字段(static 修饰,已在编译期把结果放人常量池的静态字段除外〉 调用一个类的静态方法 使用·java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。 当初始化一个类时,发现其父类未初始化,则需先初始化其父类。 虚拟机启动时先初始化用户指定的执行主类(main 方法所在类) 当使用 JDK 1.7 的动态语言支持时,若一个java.lang.invoke.MethodHandle实例最后解析为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且该方法对应的类未初始化,则先触发其初始化。 除了以上五种虚拟机限定的触发类初始化的场景外,其他的引用类的场景都不会触发类初始化,如:
尝试通过子类来引用父类中定义的静态变量,只会触发父类的初始化而不会触发子类的初始化。 数组对象的初始化由 JVM 实现,不会触发数组元素类的初始化,而且会做越界检查,防止缓冲区溢出。 每个类都有属于自己的运行时常量池,当类A尝试访问类B中的常量C时,javac会将常量C复制一份到类A的字节码文件中,到运行时再装入运行时常量池,所以访问某个类的常量时不会触发其初始化。 0x04 加载过程 1.</description>
</item>
<item>
<title>Touch MySQL Storage Engine</title>
<link>https://jordonyang.github.io/post/mysql/engine/base/</link>
<pubDate>Fri, 28 Dec 2018 00:40:04 +0800</pubDate>
<guid>https://jordonyang.github.io/post/mysql/engine/base/</guid>
<description>前言:和大多数数据库不同,MySQL 中有一个存储引擎的概念,针对不同的存储需求可以选择最优的存储引擎。
0x00. 概述 MySQL 数据库最重要的一个特性是支持插件式存储引擎,用户可以根据应用的需要选择如何存储和索引数据、是否使用事务等。MySQL 默认支持多种存储引擎,以适用于不同领域的数据库应用需要,用户可以通过选择使用不同的存储引擎提高应用的效率,提供灵活的存储,用户甚至可以按照自己的需要定制和使用自己的存储引擎,以实现最大程度的可定制性。 MySQL 5.0 支持的存储引擎包括 MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED 等,其中 InnoDB 和 BDB 提供事务安全表,其他存储引擎都是非事务安全表。
0x01. 简单的引擎相关命令 1. 查看引擎信息 可以通过show engines命令查询当前数据库支持的存储引擎,下面以MySQL 5.7(Ubuntu)进行试验。可以看到MySQL 5.7版本支持的存储引擎包括:MyISAM、CSV、MRG_MYISAM、BLACKHOLE、PERFORMANCE_SCHEMA、InnoDB、ARCHIVE、MEMORY、FEDERATED ,其中默认的存储引擎是InnoDB,其他几列给出了这些引擎的一些概要的特性说明。
第二种方法是采用show variables like '%storage_engine%'命令搜索与存储引擎相关的信息,如下面的右图所示。据《深入浅出MySQL》这本书说,使用show variables like 'have%'可以查看当前版本支持的引擎类型,看一下它的环境是MySQL 5.0,而在5.7版本中显然这些信息已被去除了(如下面左图所示),只能看到一些关于安全、压缩和缓存之类的信息。
在创建新表的时候,可以通过增加 ENGINE 关键字设置新建表的存储引擎,例如,在下面的例子中,表 user 因为没有指明而使用默认的 InnoDB 存储引擎,而 city 表则是指明为 MyISAM 存储引擎的。当然也可以通过alter table user engine=MyISAM修改表定义将 user 表的存储引擎改为 MyISAM,这样它就可以使用 MyISAM 引擎相关的存储特性了,实验证明可以,在此不贴图。
2. 修改引擎信息 默认情况下,创建新表不指定表的存储引擎,则新表是默认存储引擎的,如果需要修改默认的存储引擎,可以通过命令set default_storage_engine = engine_name设置
还可以修改 MySQL 的配置文件my.cnf,下面使用locate命令定位该文件,可以看到其中的default-storage-enine为InnoDB,将其修改后保存退出。
重启mysql服务器:使用mysqladmin -u root -p shutdown或者service mysqld restart。登录mysql数据库,使用show engines;命令默认引擎是否修改成功。</description>
</item>
<item>
<title>Notes for OS Exam</title>
<link>https://jordonyang.github.io/post/os/sc/</link>
<pubDate>Sun, 23 Dec 2018 21:05:05 +0800</pubDate>
<guid>https://jordonyang.github.io/post/os/sc/</guid>
<description>前言: 本来不打算发的,想想(再)还是(水)留(一)个记(篇)录吧。想到最后一道简单到爆却没时间做的移臂调度不禁心塞😭
0x00. 知识点 第一章. 引论 计算机操作系统的功能是控制、管理计算机系统的资源和程序的执行。 分时系统的特点 多路性:各用户可同时请求系统服务,分时原则 独立性:各用户的请求彼此独立,互不干扰 及时性:响应快 交互性:用户通过终端进行广泛的人机对话 多个用户是经过网络连接,同时使用计算机系统不是分时系统的特点。每个用户都在不同的时间片运行,只是时间片很小难以察觉。 在实时操作系统的控制下,计算机系统能及时处理由过程控制反馈的数据,并作出响应。 操作系统为用户程序完成与硬件相关和应用无关的工作。 分时操作系统的主要目的是计算机系统的交互性。 操作系统不进行软件管理。 从用户的观点看,操作系统是用户与计算机之间的接口 在操作系统的各功能组成部分中,进程调度不需要硬件的支持。 屏蔽所有中断命令应该只在核心态下执行。 多道批处理系统的主要缺点是缺少交互性。 配置了操作系统的计算机被称为虚拟计算机。 UNIX操作系统是一种多用户的、人机交互的分时系统。 实时操作系统必须在被控对象规定的时间内响应一个新任务。 操作系统提供给用户程序的接口是系统调用。 操作系统的最主要设计目标是方便性和有效性。 第二章 进程描述与控制 进程之间的制约关系可以归结为同步与互斥。 在进程状态变化中,等待→运行的变化是不可能发生的。 进程和程序的本质区别是动态和静态特征。 某进程所要求的一次打印输出结束,该进程被唤醒,其进程状态将从等待状态改到就绪状态。 临界区是指一段程序,用于控制进程/线程对临界资源的互斥访问。 进程是PCB结构、程序和数据的集合。 多道程序系统中的操作系统分配资源以进程为基本单位。 通常,用户进程被建立后,随着程序运行正常或异常结束而撤消。 同步机制应遵循的规则 空闲让进:当无进程进入临界区,应允许一个进程访问临界资源 忙则等待:当已有进程进入临界区访问临界资源,其他进程必须等待,以保证互斥访问 有限等待:保证进程在有限时间内进入临界区 让权等待:进程不能进入临界区时,应立即放弃CPU执行权 信号量同步机制:通过两个原子操作wait(s)和signal(s)来访问,这两个操作又分别被称为P、V操作 整型信号量:设置整型量,没有遵循让权等待原则,请求临界资源的进程出于“忙等”状态 记录型信号量:设置等待队列,实现让权等待。 AND型信号量:要么全分配,要么一个也不分配。避免系统死锁(静态资源分配法)。 信号量集:为提高效率而对AND信号的扩充。允许一次申请多种资源多个。 三大进程同步问题 生产者-消费者问题 哲学家进餐问题 读者-写者问题 当一个进程获得了所等待的资源(IO完成等),就要退出等待队列而进入就绪队列。 多道程序设计能充分发挥CPU与外设之间的并行工作能力。 在引入线程的操作系统中,把线程作为调度和分派的基本单位,而把进程作为资源拥有的基本单位。 S为死锁状态的充要条件是当且仅当S状态的资源分配图是不可完全简化的,该充要条件称为死锁定理。 第四章 进程的调度与死锁 在批处理系统中,周转时间是指作业等待时间和运行时间之和。 先来先服务调度:排队等待时间最长的作业被优先调度。 操作系统用于 作业调度的算法有:先来先服务、优先级调度、高响应比优先 用于进程调度的算法:时间片轮转、优先数优先、多级反馈队列 两个进程争夺同一个资源不一定死锁。 资源要求多的作业,其优先权应低于资源要求少的作业;系统进程的优先权应高于用户进程的优先权;计算型作业的优先权,应低于I/O型作业的优先权;在动态优先权时,随着进程运行时间的增加,其优先权降低。 产生死锁的原因: 资源分配策略不当 并发进程执行速度不当 多个进程由于竞争互斥使用的资源又互不相让而进入死锁。 资源分配图中有环路则系统可能存在死锁,也可能不存在死锁。 一般来说,对需经常启动外设的进程给一个较小的时间片比较合适(阻塞,不需CPU)。 最高响应比优先既有利于短小作业又兼顾到长作业的作业调度。 在单处理器的多进程系统中,进程什么时候占用处理器和能占用多长时间,取决于进程自身和进程调度策略。 设系统中有n个并发进程,竞争资源R,且每个进程都需要m个R类资源,为使该系统不会因竞争该类资源而死锁,资源R至少要有 (n-1) x m + 1个。 分时系统中进程调度算法通常采用时间片轮转法。 某单处理器多进程系统中有多个就绪进程,在进程处于临界区时仍能进行处理机调度。 第四章 存储器管理 存储管理方式: 连续分配存储 固定分区分配 可变式分区分配 首次适应:符合大小要求即分配 循环首次适应:从上次分区的下一分区起循环找,找到符合大小要求即分配 最佳适应:在空闲区表中以空闲区长度按从小到大排列 最坏适应:在空闲区表中以空闲区长度按从大到小排列 动态可重定位分区分配:相比于可变式分区分配多了紧凑。</description>
</item>
<item>
<title>Books</title>
<link>https://jordonyang.github.io/books/</link>
<pubDate>Sat, 22 Dec 2018 13:25:11 +0800</pubDate>
<guid>https://jordonyang.github.io/books/</guid>
<description>0x00. 技术书籍 1. Java 《Java核心技术卷》 《Java编程思想》 《Java8实战》 《设计模式之禅》 《深入理解Java虚拟机》 《Java性能优化权威指南》 《Java并发编程实战》 《Effective Java》 《代码整洁之道》 2. 网络 《计算机网络自顶向下》 《计算机网络》 3. 算法 《数据结构概论》 《算法导论》 《算法竞赛入门经典》 《数据结构与算法分析-Java语言描述》 《剑指Offer》 4. 安全 《密码学导论》 《信息安全原理与技术》 《网络安全基础》 《信息安全数学基础》 《黑客攻防技术宝典》 5. 计算机系统 《深入理解计算机系统》 《深入理解并行编程》 《计算机操作系统》 《计算机组成原理》 6. 数据库 《深入浅出MySQL》 《数据库系统概论》 《高性能MySQL》 7. Linux 《鸟哥的Linux私房菜》 0x01. 小说 《摆渡人》 《麦田的守望者》 《偷影子的人》 《局外人》 《分成两半的人》 《情人》 《2001太空漫游》 《树上的男爵》 《不存在的骑士》 《嫌疑人X的献身》 《乞力马扎罗的雪》 《岛上书店》 《童年》 《追风筝的人》 《百年孤独》 古龙系列小说 《一只特立独行的猪》 《三重门》 0x02.</description>
</item>
<item>
<title>Homemade Certificate and Unidirectional Authentication</title>
<link>https://jordonyang.github.io/post/security/app/ssl-config/</link>
<pubDate>Mon, 10 Dec 2018 23:42:48 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/app/ssl-config/</guid>
<description>0x00. OpenSSL使用 作为本地环境的实验,可以使用OpenSSL生成证书帮助理解证书认证的整个过程,事实上,在应用层面OpenSSL的可靠性是值得考量的,最著名的事件莫过于在2014年爆出的心跳漏洞(Heart Bleed),利用该漏洞,黑客坐在自己家里电脑前,就可以实时获取到约30%https开头网址的用户登录账号密码,包括大批网银、购物网站、电子邮件等。
1. 获取OpenSSL Github OpenSSL官网 Git安装目录下的/usr/bin/openssl.exe(win10,Linux类似) 比较折腾的方法是到Github或者OpenSSL官网的官网下载源码,然后自己编译,但我敲下nmake命令之后,发现这个坑不是一般的大,想想要在我这破电脑上装个VS突然心酸不已,那有没有什么easy way呢?有,而且或许在不知不觉中你已经装了,那就是Git,想想Git一套套的安全验证机制,当你在Git安装目录下的/usr/bin/中找到openssl.exe这个文件时真的不需要惊奇。
2. 创建私钥 下面命令会生成2048位的RSA密钥对, 并且将它存放在exp.key文件中。
OpenSSL&gt; genrsa -out out.key 2048 如果不指定参数则会创建默认2048位的RSA密钥对, 并且输出到stdout
OpenSSL&gt; genrsa Generating RSA private key, 2048 bit long modulus .......................................................................................+++ ........+++ e is 65537 (0x10001) -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAm5FLEgxENOYWzKxs/PNP2/UxmBQjdfURuz3k1QFVw76RMi8i mQPTdMSdZ3tuEG6r7qZDId4nefJRFCMSJOLbuVoLkLozWnoUAGbvDzQKiXEVE9Bi ypsm6fHi7zZKpBltHwFiuU0jMSlj1PIcKyd7JYr0dIXyZIYz8z9bfJf+JPefr6sq WsTl6FzxC+3S8Na6xzCNPl21v9AGsLOIT9roVenN7ewjdhOqpKOyvYwSAev0wOoc gMiz6qnI0a/2xlrgFP6qb7wofOpBbosDSbYsexg2kFfvlYPTtPFyAUudewtaZ2qm MzWl5f7vngfLLQ9F8C4cXFpFUVoboDOO+vvXxwIDAQABAoIBABqcDaU2p+wMd1Xn IHVcAQr0s9j5iN9CXhgTDDNLGSp0TE2zNk3KE79jajErMbZp18rocY83erUnN6sk HJRmoYLg3h2RW/tb6lPvR3DoS+0ahKpw+JUDWPKnR1P3i2jqWHn3OLXNdfmQxNzF 1P+krzHKxOmxh7aeJgCzk6iKLxSPamCQLSWW8sc3fu+6yBbtXOzifgETXqc5SEbk UKfn489d4vLU5foLiOLuSWc2FI+c1mDQfzUVo14v/CpnknCSfNIPlDePdvBLr5uG 8YWQ4hftlCQ6hZQodmnxw+HNCqOqJzMs3SMXsuuFu66VVzy48p8+zKzZJ7+uZUOA Bo1GCgECgYEAyenl8TP6yxW4sKc3pQwL2A9wUJBg3mdcpMOEvJoL66fL1HF2uSz9 /3H/aNJzJb5bfeJytZfgSIt+XwttuKxWzLfBOdpX972xA9iZ0ia3v62wD7oXkrho +oaETu41y6CIy5t9eTQpk7JeH/MzrXYPIXGPAuLq/DOPZ0aR32N5tYECgYEAxT09 Weee71Z+qKNWIcwBGnq+ko5CRWe1Btl75yeo/kABVH8/jDMJFlZU5otU8W6aPRmX 93nbNZ42u24GBo8xZcrY5/K1qRolMS40faDQqv28pWcRJAfDUyp+iuZ8nulV9fFU 5KaDR1Z2Tq2XX9lAX92itaPeiFrmUL0UO98FAUcCgYAodvWcJzyloo7G6FwgeY+O F1n861Jef/xzELPzUDP/YDtbMfoHFls47FGxYiutbr7LNayjc/KA12PWDGZK0k76 7ACPZZCnl554FNqO4S+F/HsCS9sZjleLjZXKc7bYPi3sEBdPSQLkFKUEZir/Il/Z 5fYxL+TD8llU6zGkwTzNAQKBgBrooO8cD23doggFyKFxwJDJQCikadwoKuVVVHcT zRVqzAOoHJZW/B1lB84wIhWSHk7JqBbmNAbbn+HwP7gzXuzhQLhcP3Zjj8Fm6Vhe UTmSxmLWbds5h+dXu83dZhEUCls0Yn2TTgO0gw1211kHPaYWibU4NwrpQ9SuBw2n /nnPAoGBAJsQn5SsS97e1D++wHXCduj8mP/oA3xUxVVySwiwh5+equGRMNl/ALfF v3ok5pLP/j2KbfXwnNAiImNUzY7KJhGrAGwWG9vPloeM3oXjeeBlDuVKcxcNzRat 9DrkTj9IH1YNcFV+z5hMpxNhMWIBEjtlL9YCRpla9W9dpFAwMYG8 -----END RSA PRIVATE KEY----- 下面是genrsa命令的一些参数 (usage: genrsa [args)][numbits])</description>
</item>
<item>
<title>What is Template Method Pattern</title>
<link>https://jordonyang.github.io/post/architecture/pattern/template-method-pattern/</link>
<pubDate>Thu, 06 Dec 2018 19:42:27 +0800</pubDate>
<guid>https://jordonyang.github.io/post/architecture/pattern/template-method-pattern/</guid>
<description> 0x00. 定义 定义一个操作中的算法的框架, 而将一些步骤延迟到子类中。 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 模板方法模式的通用类图如下所示 模板方法模式确实非常简单, 仅仅使用了Java的继承机制, 但它是一个应用非常广泛的模式。 其中, AbstractClass叫做抽象模板, 它的方法分为两类:
基本方法 基本方法也叫做基本操作, 是由子类实现的方法, 并且在模板方法被调用 模板方法 可以有一个或几个, 一般是一个具体方法, 也就是一个框架, 实现对基本方法的调度,完成固定的逻辑。 注意: 为了防止恶意的操作, 一般模板方法都加上final关键字,不允许被覆写;所有的基本方法一般都是用protected修饰的,因为具体算法的实现只有子类可以访问,对外是不开放的。
在类图中还有一个角色: 具体模板。 ConcreteClass1和ConcreteClass2属于具体模板, 实现父类所定义的一个或多个抽象方法, 也就是父类定义的基本方法在子类中得以实现。
其通用代码如下图所示
0x01. 优点 1. 封装不变部分, 扩展可变部分:把认为是不变部分的算法封装到父类实现, 而可变部分的则可以通过继承来继续扩展。 增加一个子类,只需实现父类的基本方法。
2. 提取公共部分代码,便于维护
3. 行为由父类控制, 子类实现:基本方法是由子类实现的, 因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。
0x02. 缺点 按照我们的设计习惯, 抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。
0x03. 应用 多个子类有公有的方法,并且逻辑基本相同时。 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见模板方法模式的扩展) 约束其行为。 0x04. 扩展 - 控制反转 模板方法设计模式的一个更加常见的形式是除了使用abstract的基本方法和final的不可变的模板方法外,还应用了钩子函数,所谓的钩子函数一般是指一系列定义在抽象父类中非抽象方法,子类可以选择性地重写这些方法(当然也可以使用父类的默认实现),通常地,它们拥有布尔型的返回值,在通过子类对象调用父类的模板方法时,模板方法会根据子类的钩子函数结果决定其执行逻辑。
不难发现,这种情况中的模板方法跟上面的的有所不同,在上面的通用代码中,父类直接决定整个模板的执行,而现在父类的模板方法的运行过程需要需要依赖于子类某个方法的执行结果,这种情况其实属于控制反转的一种,因为高层代码不再在运行前决定整个代码块的执行流程,而是在运行时动态地选择低层代码。
0x05. 实例 如果你看过AQS源码,那么很自然地你会发现它使用了模板方法设计模式。虽然AQS定义为抽象类,但是其中并没有抽象的基本方法,其中定义了5个钩子函数和10个模板方法,钩子函数的具体实现由AQS子类提供,而且这些返回值会影响模板方法的执行结果。
0x06. 参考 Wikipedia:IoC Wikipedia:Template_method_pattern 《设计模式之禅》(秦小波著,第二版,机械工业出版社) </description>
</item>
<item>
<title>Simply Understanding Compare-And-Swap</title>
<link>https://jordonyang.github.io/post/java/concurrency/cas/</link>
<pubDate>Sun, 25 Nov 2018 20:48:32 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/concurrency/cas/</guid>
<description>0x00. TOC 0x01.简介 0x02.应用 0x03.ABA问题 0x04.实现 0x05.Java中的CAS 0x06.参考 0x01. 简介 比较并交换(compare and swap, CAS),是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。
无锁(lock-free)的非阻塞算法 CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS无锁算法的C语言实现如下:
int cas(long *addr, long old, long new) { /* Executes atomically. */ ATOMIC(); if(*addr != old) return 0; *addr = new; END_ATOMIC(); return 1; } 在使用上,通常会记录下某块内存中的旧值,通过对旧值进行一系列的操作后得到新值,然后通过CAS操作将新值与旧值进行交换。如果这块内存的值在这期间内没被修改过,则旧值会与内存中的数据相同,这时CAS操作将会成功执行使内存中的数据变为新值。如果内存中的值在这期间内被修改过,则一般来说旧值会与内存中的数据不同,这时CAS操作将会失败,新值将不会被写入内存。
0x02. 应用 在应用中CAS可以用于实现无锁数据结构,常见的有
无锁队列(先入先出) 无锁堆(先入后出) 对于可在任意位置插入数据的链表以及双向链表,实现无锁操作的难度较大。 0x03. ABA问题 ABA问题是无锁结构实现中常见的一种问题,可基本表述为:
进程P1读取了一个数值A P1被挂起(时间片耗尽、中断等),进程P2开始执行 P2修改数值A为数值B,然后又修改回A P1被唤醒,比较后发现数值A没有变化,程序继续执行。 对于P1来说,数值A未发生过改变,但实际上A已经被变化过了,继续使用可能会出现问题。在CAS操作中,由于比较的多是指针,这个问题将会变得更加严重。试想如下情况:
有一个堆(先入后出)中有top和节点A,节点A目前位于堆顶top指针指向A,这时堆结构图1所示。现在有一个进程P1想要pop一个节点,因此按照如下无锁操作进行 pop() { do{ ptr = top; // ptr = top = NodeA next_prt = top-&gt;next; // next_ptr = NodeX } while(CAS(top, ptr, next_ptr) !</description>
</item>
<item>
<title>Dive into MESI Protocol</title>
<link>https://jordonyang.github.io/post/cache/mesi/</link>
<pubDate>Tue, 20 Nov 2018 10:46:53 +0800</pubDate>
<guid>https://jordonyang.github.io/post/cache/mesi/</guid>
<description>0x01. 概述 1. 缓存一致性 (Cache Coherence) 硬件层面的问题,指的是由于多核计算机中有多套缓存,各个缓存之间的数据不一致性问题。一致性如何理解,我觉得 CMU 的一位老师说得不错:All processors see the same view of memory at the same time。缓存一致性协议,如MESI解决是多个缓存副本之间的数据的一致性问题,这些协议可能十分复杂,可能有数十种状态。
2. MESI MESI协议是一个基于失效的缓存一致性协议,是支持回写(write-back)缓存的最常用协议。因为是在伊利诺伊大学厄巴纳-香槟分校被发明的,所以也被称作伊利诺伊协议 (Illinois protocol)。与写通过(write through)缓存相比,回写缓冲能节约大量带宽。总是有“脏”(dirty)状态表示缓存中的数据与主存中不同。MESI协议要求在缓存不命中(miss)且数据块在另一个缓存时,允许缓存到缓存的数据复制。
0x02. 状态 MESI 存在modified、exclusive、shared和invalid四种状态,协议可以在一个指定的缓存中应用这四种状态。因此,协议在每一个缓存行中维护一个两位的状态tag, 附着在缓存行的物理地址或者数据后。
modified:处于该状态的缓存行中的数据被当前CPU独占,不在其他CPU缓存中,且为最新的与主存不同的数据,因为数据已经更新,所以脏位为1,缓存最终有责任将数据写回到内存,并且也应当为其他缓存提供数据,必须在当前缓存缓存其他数据之前完成这些事情。该缓存行写回主存后,状态变为shared。 exclusive:非常类似于modified状态,唯一的区别是缓存行的数据还没有被相应的 CPU 修改,这表示缓存行中的数据及内存中的数据都是最新的,所以缓存行的脏位是0。但是,由于 CPU 独占该缓存行,所以能够在任何时刻将数据保存到该行,而不考虑其他 CPU。也就是说,由于内存中的值是最新的,该行可以被直接丢弃而不用回写到内存,也可以为其他缓存提供数据。 shared:处于该状态的缓存行可能被复制到至少一个其他 CPU 缓存中,这样在没有得到其他 CPU 的许可时,不能向缓存行存储数据。由于exclusive状态下,内存中的值是最新的,因此可以不用向内存回写值而直接丢弃缓存中的值,或者向其他 CPU 提供值。 invalid:处于该状态的行是空的,换句话说,它没有保存任何有效数据。当新数据进入缓存时,它替换一个处于invalid状态的缓存行。这个方法是比较好的,因为替换其他状态的缓存行将引起大量的缓存缺失。 对于任意给定的缓存对,给定缓存行的允许状态如下:
如当一个缓存中的某个块被标记为M(modified)时,其他缓存中的块副本只能被标记为I(Invalid)。
0x03. 协议消息 Read:该消息包含缓存行需读的物理地址。 Read Response:该消息包含较早前的read消息的数据。这个read response消息可能由内存或者其他缓存提供。例如,如果一个缓存请求一个处于modified状态的数据,则缓存必须提供read response消息。 Invalidate:包含要使无效的缓存行的物理地址。其他的缓存必须从它们的缓存中移除相应的数据并且响应此消息。 Invalidate Acknowledge:一个接收到invalidate消息的 CPU 必须在移除指定数据后响应一个invalidate acknowledge消息。 Read Invalidate:该消息包含要缓存行读取的物理地址。同时指示其他缓存移除数据。因此,它包含一个read和一个invalidate。read invalidate也需要read response以及invalidate acknowledge消息集。英文亦称read with intend to modify,可理解为读其他缓存的数据,并试图修改当前缓存数据,然后指示其他缓存的数据无效化。 Writeback:该消息包含要回写到内存的地址和数据。(并且也许会通过窥探器请求检查其他 CPUs 的缓存)。这个消息允许缓存在必要时换出modified状态的数据以腾出空间。 0x04.</description>
</item>
<item>
<title>SSL - Dive into Handshake protocol</title>
<link>https://jordonyang.github.io/post/security/ssl/handshake/</link>
<pubDate>Mon, 12 Nov 2018 12:04:11 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/ssl/handshake/</guid>
<description>0x00. 握手过程 SSL最复杂的部分是握手协议。这一协议允许客户端和服务器相互认证,并协商加密和MAC算法,以及用于保护数据使用的密钥通过SSL记录传送。握手协议在任何应用数据被传输之前使用。 握手协议由客户端和服务器之间的一系列消息交换组成。每一条消息都包括三个域:
类型(1字节):表示预定义的10种消息类型之一。 长度(3字节):以字节为单位的消息长度。 内容(&gt;0字节):和本条消息相关的参数。 消息类型 参数 hello_request 空 client_hello 版本,随机数,会话ID,密码套件,压缩方法 server_hello 版本,随机数,会话1D,密码套件,压缩方法 certificate X.509v3证书链 server_key_exchange 参数,签名 certificate_request 类型,授权 server_done 空 certificate_verify 签名 client key_exchange 参数,签名 finished 散列值 下图说明了为建立客户端和服务器之间的逻辑连接需要进行的初始交换。这些交换可分为4个阶段。(注意:加阴影的传输是可选的)
1. 第一阶段:客户端发起建立连接请求 这一阶段主要是发起逻辑连接并建立与之关联的安全能力。交换首先由客户端通过发送下列dient_hello消息启动。
版本(Client_version):客户端的SSL最高版本。 随机数(Random):由客户端产生的随机序列,由32比特时间戳以及安全随机数生成器产生的28字节随机数组成。这些数没有任何意义,主要用于SSL协议后面的密码学计算(会话密钥),防止密钥交换过程中的重放攻击。 会话ID(Session ID):可变长度的会话标识符。非零值表示客户端希望更新现有连接的参数,或为该会话创建一条新连接。零值表示客户端希望在新会话上建立一条新连接。 密码套件(Cipher_suite):按优先级的降序排列的、客户端支持的密码算法列表。列表中的每一行(即每一个密码套件)同时定义了密钥交换算法和密码规格(CipherSpec)。 压缩方法(Compression_methods):客户端支持的压缩方法列表。 同样,该列表也依赖客户端的偏爱来排序。尽管该域通常在SSLv3中不使用(空算法),但以后的TLS将要求支持它。 发送完dient_hello消息后,客户端将等待server_hello消息,该消息所包含的参数与client_hello消息包含的参数相同。</description>
</item>
<item>
<title>SSL - Architecture and Basic Protocols</title>
<link>https://jordonyang.github.io/post/security/ssl/base/</link>
<pubDate>Mon, 12 Nov 2018 12:03:51 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/ssl/base/</guid>
<description>0x00. 简介 安全套接字层(Secure Socket Layer, SSL),由网景(Netscape)公司于1994年提出的基于Web应用的安全协议。该协议的第三版是经过公开评论和工业界使用后成为互联网草案。1999年,Internet Engineering Task Force (IETF)标准化了一种名为TLS的新协议,它是SSL的更新版本。事实上,TLS与SSL非常相似,TLS 1.0使用的SSL协议版本号为3.1。乍一看,这可能令人困惑,但这是有意义的,因为TLS只是对SSL 3.0的一个小更新。TLS的后续版本遵循了这种模式。由于TLS是SSL协议的演变,人们仍然可以在某种程度上互换使用术语TLS和SSL。 SSL主要采用了公钥密码机制和X.509数字证书技术,其目标是保证两个应用间通信的保密性、完整性和可靠性,可在服务器和客户端两端同时实现支持。 SSL结余可靠的传输层协议和应用层协议之间,能为客户端和服务器之间的TCP/IP连接提供服务器认证、消息完整性、通信数据加密和可选的客户端认证服务。 SSL可以用于任何面向连接的安全通信,但通用于安全web应用的HTTP协议。 SSL提供一个安全的握手来初始化一个TCP/IP连接,完成客户端与服务端之间关于安全等级,密码算法、通信密钥的协商,以及执行对连接端的身份认证工作。在此之后SSL上的所传送的应用层数据都会被加密,从而保证通信的机密性。 0x01. 协议结构 1. 协议栈 SSL使用TCP提供一种可靠的端对端的安全服务。SSL不是单个协议,它由两层协议组成,如下图所示。SSL记录协议对各种更高层协议提供基本的安全服务。尤其是,HTTP是为Web客户端/服务器的交互提供传输服务的协议,它可以在SSL的顶层运行。SSL中定义的三个较高层协议分别是:握手协议、修改密码规范协议和警报协议。这些SSL协议规范用来管理SSL的交换,
2. 状态 SSL协议中的两个重要概念是SSL会话和SSL连接,按照规范文件,它们的定义如下。
连接:传输层概念,是一种能够提供合适服务类型(按照OSI分层模型定义)的传输。对SSL来说,这种连接是点对点的关系而且都是短暂的。每一条连接都与一个会话相关联。 会话:SSL会话是客户与服务器之间的一种关联。会话是通过握手协议来创建的。所有会话都定义了密码安全参数集合,这些参数可以在多个安全连接之间共享。会话通常用来减少每次连接建立安全参数的昂贵协商费用。 任何一对实体(例如客户和服务器上的HTTP应用)之间都可以有多个安全连接。理论上也允许一对实体之间同时有多个会话存在,但实际上并非如此。根据连接和会话状态,可以描述SSL的基本通信步骤:
建立TCP连接 SSL握手,建立SSL会话 通过会话传送加密数据报 释放连接,会话过期 实际上还有若干个状态与每个会话相关联。一旦建立起一个会话,对于读和写(即接收和发送)就存在一个当前操作状态。此外,在握手协议中,还会创建读挂起和写挂起状态。当握手协议结束后,挂起的状态又回到当前状态。
根据SSL规范文件,会话状态由下列参数定义:
会话标识符:由服务器产生的用于标识活动或可恢复的会话状态的一个任意字节序列 对等实体证书:对等实体(信道另一方)的X.509v3证书。会话状态的这一元素可以为空。 压缩方法:加密前用于压缩数据的算法。 密码规格:包括大块数据加密算法(例如空算法、 AES算法等)规格和用于计算MAC的散列算法(如MD5或SHA-1算法等)规格。它还定义了一些密码属性,例如散列值长度等。 主密钥:客户端和服务器共享的48字节的会话密钥。 可恢复性:表明会话是否可被用于初始化新连接的标志。 连接状态由下列参数定义:
服务器和客户端随机数:由服务器和客户端为每个连接选定的字节串。 服务器写MAC密钥:服务器发送数据时用于计算MAC值的密钥。 客户端写MAC密钥:客户端发送数据时用于计算MAC值的密钥。 服务器写密钥:服务器用于加密数据、客户端用于解密数据的加密密钥。 客户端写密钥:客户端用于加密数据、服务器用于解密数据的对称加密密钥。 初始化向量:在CBC模式中,需要为每个密钥配置一个初始化向S(IV)。最初的IV值由SSL的握手协议初始化。之后,每个记录的最后一个密码块被保存,以作为后续记录的IV。 序列号:建立连接的各方为每条连接发送和接收的消息维护单独的序列号。当一方发送或接收改变密码规格的消息时,相应的序列号应置零。序列号的值不能超过264-1。 3. 记录层协议 记录协议要传输应用消息时,先将数据分段成一些可操作的明文记录(Plaintext Records),然后选择压缩或不压缩数据,再生成MAC、加密、添加头并将最后的结果作为一个TCP分组送出。对接收到的数据,首先解密,然后做完整性验证、解压缩、重组,最后把数据递送到更高层用户。
SSL记录协议为SSL连接提供如下两种服务。
机密性:握手协议定义一个可以用于加密SSL载荷的传统加密共享密钥。 消息完整性:握手协议还定义一个用于产生消息认证码(MAC)的共享密钥。 3.</description>
</item>
<item>
<title>Rationale and Implementation of RSA Algorithm</title>
<link>https://jordonyang.github.io/post/security/crypto/asymmetry/rsa/</link>
<pubDate>Sat, 10 Nov 2018 14:23:26 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/asymmetry/rsa/</guid>
<description>0x00. TOC 0x01.简介 0x02.应用 0x03.原理 0x04.实现 1.素数的选择与判断 2.实现模逆算法 2.1.欧几里得算法 2.2.扩展欧几里得算法 2.3.求解同余方程算法 3.实现快速模指运算 4.编程实现RSA算法 5.利用RSA进行数据加解密 6.实现对大数据进行加密 7.实现简单的GUI界面 8.分析总结 0x05.攻击方法 0x06.密钥分发 0x07.参考 0x01. 简介 最初的公钥方案是在1977年由Ron Rivest、Adi Shamir和Len Adleman在MIT提出的,并且于1978年首次发表[RIVE78]。 RSA方案从那时起便占据了绝对的统治地位,成为最广泛接受和实现的通用公钥加密方法。 RSA是分组密码,对于某个m它的明文和密文是0〜n - 1之间的整数。TOC
0x02. 应用 用于在开放的网络环境(如Internet)上保护电子通信,而不依赖于隐藏或隐蔽的通道,甚至用于密钥交换。开放的网络环境容易受到各种通信安全问题的影响,比如中间人攻击和欺骗。通信安全通常包括要求通信不得在运输途中可读(保存保密),通信在运输途中不能修改(保证沟通完整性),沟通必须来自一个确定原(发送方真实性),和收件人必须不能否定或拒绝接收的通信。将非对称加密与Enveloped Public Key Encryption(EPKE)方法相结合,允许在开放的网络环境中安全发送通信。换句话说,即使密码分析者听了包括密钥交换在内的整个对话也无法解释对话。
公钥密码术中使用的区别技术是使用非对称密钥算法,其中一方用于执行加密的密钥与另一方用于解密的密钥不同。每个用户都有一对加密密钥——一个公共加密密钥和一个私有解密密钥。例如,用于数字签名的密钥对(包括一个私有签名密钥和一个公共验证密钥)。公钥可以广泛分发,而私钥只有其所有者知道。它们在数学上是相关的,但是选择参数是为了从公钥计算私钥是不可行的。
相反,对称密钥算法使用的是一个秘密密钥,它必须由发送方(用于加密)和接收方)用于解密)共享并保持私有。要使用对称加密方案,发送方和接收方必须事先安全地共享密钥。
由于对称密钥算法几乎总是比非对称密钥算法计算量小得多,所以通常使用密钥交换算法交换密钥,然后使用该密钥和对称密钥算法传输数据。PGP和SSL/TLS方案家族使用这个过程,因此称为混合加密系统。综上非对称算法适用于 TOC
密钥交换 数字签名 与对称加密算法混合使用(PGP, SSL, TLS) 0x03. 原理 对于某一明文块M和密文块C,加密和解密有如下的形式: $$C = M^e \ mod \ n$$ $$M = C^d \ mod \ n = (M^e)^d \ mod\ n = M^{ed} mod \ n$$</description>
</item>
<item>
<title>Understand Stream Cipher and Implement RC4</title>
<link>https://jordonyang.github.io/post/security/crypto/symmetry/rc4/</link>
<pubDate>Wed, 07 Nov 2018 10:01:04 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/symmetry/rc4/</guid>
<description>前言 分组密码每次处理一个输入分组,并为每个输入分组产生一个输出分组。流密码连续处理输入元素,在运行过程中,一次产生一个输出元素。尽管分组密码普遍得多,但对于一些特定的应用,使用流密码更合适。
0x00. 流密码结构 &nbsp;&nbsp;&nbsp;&nbsp;典型的流密码一次加密一个字节的明文,尽管流密码可能设计成一次操作一个比特或者比字节大的单位。图2.7是流密码结构的示意图。在这个结构里,密钥输入到一个伪随机字节生成器,产生一个表面随机的8比特数据流。如果不知道输入密钥,伪随机流就不可预测的,而且它具有表面上随机的性质。这个生成器的输出称为密钥流,使用位异或操作与明文流结合,一次一个字节。例如,如果生成器产生的下一字节是01101100,明文的下一字节是11001100,那么得到的密文字节是: $$11001100 ⊕ 01101100 \ = \ 10100000$$ 解密需要同一伪随机序列: $$10100000 ⊕ 01101100 \ = \ 11001100 $$
[KUMA97]列出了下列设计流密码时需要重要考虑的因素:
加密序列应该有一个长周期。伪随机数生成器使用一个函数产生一个实际上不断重复的确定比特流。这个重复的周期越长,密码破解就越困难。 密钥流应该尽可能地接近真随机数流的性质。例如, 1和0的数目应该近似相等。如果将密钥流视作字节流,那么字节的256种可能值出现的频率应该近似相等。密钥流表现得越随机,密文就越随机化,密码破译就越困难。 图2.6指出了伪随机数生成器的输出受输入密钥值控制。为了抵抗穷举攻击,这个密钥必须非常长。分组密码中的考虑因素在这里同样适用。因此,就当前的科技水平而言,需要至少128比特长度的密钥。 &nbsp;&nbsp;&nbsp;&nbsp;如果伪随机数生成器设计合理,对同样的密钥长度,流密码和分组密码一样安全。流密码的主要优点是流密码与分组密码相比几乎总是更快,使用更少的代码。本文中的示例RC4能用仅仅几行代码实现。最近几年,随着AES的引进,这个优势己经消失了,因为AES可以用软件方式高效实现。比如,Intel AES指令集含有一轮加解密和密钥产生过程使用的机器指令。使用硬件指令实现AES跟仅使用软件方式相比,速度提高了一个数M级。
&nbsp;&nbsp;&nbsp;&nbsp;分组密码的优点是可以重复使用密钥。但是如果两个明文使用同一密钥进行流密码加密,密码破译常常会非常容易[DAWS96]。如果将这两个密文流进行异或,结果就是原始明文的异或值。如果明文是文本字符串、信用卡号或者其他己知其性质的字节流,密码破解可能会成功。
&nbsp;&nbsp;&nbsp;&nbsp;对于需要加密/解密数据流的应用,比如在数据通信信道或者浏览器/网络链路上,流密码也许是更好的选择。对于处理数据分组的应用,比如文件传递、电子邮件和数据库,分组密码可能更合适。但是,这两种密码都可以在几乎所有的应用中使用。
0x01. RC4算法 &nbsp;&nbsp;&nbsp;&nbsp;RC4是Ron Rivest在1987年为RSA Security公司设计的流密码。它是密钥大小可变的流密码,使用面向字节的操作。这个算法基于随机交换的使用。通过分析指出,这个密码的周期完全可能大于10100[ROBS95a]。每输出一个字节需要8~16个机器操作,并且此密码用软件实现运行速度非常快。为网络浏览器和服务器之间的通信定义的SS17TLS (安全套接字层/传输层安全)标准中使用了RC4。它也被用于属于IEEE 802.11无线LAN标准一部分的WEP (有线等效保密)协议及更新的Wi-Fi保护访问(WPA) 协议 。 RC4原本被RSA Security公司当作商业秘密。1994年9月,RC4算法通过Cypherpunks匿名邮件转发列表匿名地公布在因特网上。
1. 算法细节 &nbsp;&nbsp;&nbsp;&nbsp;RC4算法非常简单,易于描述。用一个可变长度为1〜256字节(8-2048比特)的密钥来初始化256字节的状态向S,其元素为S[0], S[l] , …, S[255]。从始至终置换后的S包含从0到255的所有8位数。加密和解密时,字节k(见上图)是从S的255个元素中按一种系统的方式选出的。每次k值产生之后,要重新排列S的元素。
2. 初始化S &nbsp;&nbsp;&nbsp;&nbsp;开始时, S的元素按升序被置为0〜255;即S[0] = 0, S[l] = l, S[255] = 255。同时创建一个临时向量T。如果密钥K的长度为256字节,就把K直接赋给T,否则,对于keylen字节长度的密钥,将K赋值给T的前keylen个元素,并循环重复用K的值赋给T剩下的元素,直到T的所有元素都被赋值。这些预操作可被概括为
/* 初始化 */ for i = 0 to 255 do S[i] = i; T[i] = K[i mod keylen]; 然后用T产生S的初始置换,从S[0]〜S[255],对每个S[i],根据由T[i]确定的方案,并将S[i]置换为S的另一字节:</description>
</item>
<item>
<title>Big Prime Number Generation And Primality Testing</title>
<link>https://jordonyang.github.io/post/security/crypto/asymmetry/prime/</link>
<pubDate>Mon, 05 Nov 2018 21:06:29 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/asymmetry/prime/</guid>
<description>前言: RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难。那么也就是说要使用RSA算法,前提是必须有两个大素数才行,那么大素数是怎么生成的呢?考虑到RSA算法的高效性和安全性,怎样快速生成一个大素数呢?
0x01. 素性测试 (Primality Testing) 素数又称质数,是在大于1的整数中,只能被1和其自身整除的数(如2、3、5等)。素性测试是检验一个给定的整数是否为素数的测试。
1. 试除法 ( Trial Division ) 如果要判定n是否是素数,写一个循环从2到sqrt(n)判断其能否整除n,若都不能则n为素数。这个方法我们一般称之为试除法。
如果n比较小的话,采用试除法当然是非常高效快捷的。但是当n很大的时候,这个算法可就行不通了,以RSA1024为例,当公钥为
0x890e23101a542913da8a4350672c9ef8e7b34c2687ce8cd8db3fb34244a791d60c9dc0a53172a56dcc8a66f553c0ae51e9e2e2ce9486fa6b00a6c556bfed139001133cdfe5921c425eb8823b1bd0a4c00920d24bee2633256328502eadbfac1420f9a5f47139de6f14d8eb7c2b7c0cec42530c0a71dadb80c7214f5cd19a3f2f 时,两个质因数分别为
0xe5a111a219c64f841669400f51a54dd4e75184004f0f4d21c6ae182cfb528652a02d6d677a72b564c505b1ed42a0c648dbfe14eb66b04c0d60ba3872826c32e7 和
8002511426596424351829267099531651390448054153452321185350746845306277585856673898048740413439442356860630765545600353049345324913056448174487017235828857 这是一个155位数和一个154位数,都在2的511次方左右,肯定gg啦😒😒😒
2. 概率性检验算法 现在许多流行的质数检验是概率检验。这些测试除了使用被测试的数字n外,还使用从样本空间中随机选择的其他一些数字a。通常的随机素数检验从不将素数检测误判为合数,但合数被误判为素数是可能的。通过重复几个独立选取的a值进行试验,可以降低误差概率。
随机素数检验的基本结构如下:
随机选择一个数字a。 检查a与给定数n是否相等,如果相等不成立,则n为合数,a为合数的见证(witness),检验停止。 重复步骤1,直到达到所需的精度。 在一个或多个迭代之后,如果没有发现n是一个合数,那么可以说它可能是素数。
( 1 ) 费马小定理 (Fermat&rsquo;s little theorem) 它是数论中的一个定理:假如a是一个整数,p是一个素数,那么ap−a是p的倍数,可以表示为ap ≡ a (mod p) ,如果a不是p的倍数,这个定理也可以写成 ap − 1 ≡ 1 (mod p)
费马小定理的证明过程在这里不再赘述。需要注意的是,费马小定理是判定一个数是否为素数的必要条件,并非充分条件,因为存在着一些伪素数满足费马小定理却不是素数,如2340 ≡ 1 (mod 341),但是341 = 11×31,不为素数。
( 2 ) 费马概率性检验 (Fermat primality test) 更近一步考虑,一个合数可能在a=2时通过了测试,但a=3时的计算结果却排除了素数的可能。于是,人们扩展了伪素数的定义,称满足an−1 mod n = 1的合数n叫做以a为底的伪素数(pseudoprime to base a)。前10亿个自然数中同时以2和3为底的伪素数只有1272个,这个数目不到刚才的1/4。这说明如果同时验证a=2和a=3两种情况,算法出错的概率降到了0.</description>
</item>
<item>
<title>Theory-based Implementation of AES Algorithm</title>
<link>https://jordonyang.github.io/post/security/crypto/symmetry/aes/</link>
<pubDate>Thu, 18 Oct 2018 22:23:44 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/symmetry/aes/</guid>
<description>0x00. TOC 0x01.概述 0x02.AES算法的数学基础 1.有限域 2. AES在有限域上的表示 2. AES的字表示与运算 2.1. 字表示 2.2. 字运算 0x03.AES的加密过程 1.状态 2.加密过程概述 3.AES有限域上加加法 4.轮密钥的生成 4.1.密钥扩展 4.2.轮密钥的选择 4.3.代码实现 5.AES基本变换 5.1.S盒变换 5.2.行移位变换 5.3.列混合变换 6.对外加密接口 7.核心加密逻辑 0x04.解密过程 1.AES基本逆变换 2.对外解密接口 3.核心解密逻辑 0x05.测试 0x06.说明 0x07.参考 0x01. 概述 美国政府在1997年9月12日公开征集更高效更安全的替代DES加密算法,第一轮共有15种算法入选,其中5种算法入围了决赛,分别是MARS,RC6,Rijndael,Serpent和Twofish。又经过3年的验证、评测及公众讨论之后Rijndael算法最终入选。
Rijndael算法之所以最终能够被选为AES的原因是其安全、性能好、效率高、实用灵活。
Rijndael算法支持多种分组及密钥长度,介于128-256之间所有32的倍数均可,最小支持128位,最大256位,而AES标准支持的分组大小固定为128位,密钥长度有3种选择:128位、192位及256位。TOC
0x02. AES算法的数学基础 Rijndaels算法中的许多运算是按字节和4字节的字来定义的。把一个字节看成是在有限域GF(2^8)上的一个元素。有限域(Finite Field)又名伽罗瓦域(Galois field),简单言之就是一个满足特定规则的集合,集合中的元素可以进行加减乘除运算,且运算结果也是属于此集合。 TOC
1. 有限域 AES的基础域是有限域 GF(28) TOC
一个字节的全体256种取值构成一个GF(28) 一个GF(2)上的8次既约多项式可生成一个 GF(28)
GF(28)的全体元素构成加法交换群、线性空间。 GF(28)的非零元素构成乘法循环群。 2.</description>
</item>
<item>
<title>Block Cipher Mode of Operation</title>
<link>https://jordonyang.github.io/post/security/crypto/symmetry/working-mode/</link>
<pubDate>Thu, 11 Oct 2018 21:54:13 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/symmetry/working-mode/</guid>
<description>0x00. 简介 分组密码一次处理一个数据分组。在DES和3DES中,分组长度是b=64比特。在AES中,分组长度是b=128比特。对于较长的明文,需要将明文分解成b比特的分组(需要时还要填充最后一个分组)。针对不同应用使用一个分组密码时, NIST (在SP800-38A中)定义了五种工作模式。这五种情况,希望能够覆盖利用一个分组密码做加密的所有可能情况。这五种模式也希望能适用于任何分组密码算法,当然包括三重DES和AES。下面介绍几种最重要的工作模式。
0x01. 电子密码本模式 最简单的一种使用方式是所谓的电子密码本(Electronic Codebook, ECB)模式,在此模式下明文一次被处理b比特,而且明文的每一个分组都使用同一密钥加密。之所以使用术语密码本,是因为对于给定的密钥,每个b比特的明文分组对应唯一的密文。因此,可以想象一个庞大的密码本,它包含任何可能的b比特明文对应的密文。
在ECB中,如果同一个64比特的明文分组在消息中出现了不只一次,它总是产生相同的密文。因此,对于过长的消息, ECB模式可能不安全。如果消息高度结构化,密码破译者很有可能研究出这些规律。例如,如果已知消息总是开始于某一预定范围,那么密码破译者可能会拥有很多已知明文-密文对。如果消息有一些重复元素,重复周期为64比特的倍数,那么这些元素可能被破译者识别。这也许能帮助破译,或者可能给替换或者重排数据分组提供了机会。
为了克服ECB的安全不足,我们希望有一种技术,其中如果重复出现同一明文分组,则将产生不同的密文分组。
0x02. 密码分组链接模式 在密码分组链接(Cipher Block Chaining, CBC)模式中,加密算法的输入是当前明文分组与前一密文分组的异或;每个分组使用同一密钥。这就相当于将所有的明文组连接起来了。加密函数的每次输入和明文分组之间的关系不固定。因此,64比特的重复模式并不会被暴露。
解密时,用解密算法依次处理每个密文分组。将其结果与前一密文分组进行异或,产生明文分组。为了表示这个过程,可以写为 其中EK是对明文使用密钥K的加密, ㊉是异或操作符。同理得到其解密过程:
为了产生第一个密文分组,将一个初始向量(IV)和第一个明文分组进行异或。解密时,将IV和解密算法的输出进行异或来恢复第一个明文分组。
发送者和接收者都必须知道IV。为了提高安全性, IV需要像密钥一样进行保护。这可以通过使用ECB加密传送IV来完成。要保护IV的一个理由如下:如果攻击者成功欺骗接收者使其使用一个不同的IV值,接着攻击者就能把明文的第一个分组的某些位取反。为了解释这一点,考虑下列公式:
$$C_1\ = E(K,\ [IV \ ㊉\ P_1])$$
$$P_1\ =\ IV\ ㊉\ D(K,\ C_1)$$
现在使用符号 X[j] 表示 64 比特 X 的第 j 比特。则
$$P_1[i] \ =\ IV[i]\ ㊉\ D(K,\ C_1)[i]$$
那么,根据异或的性质,可以确定
$$P_1[i]&rsquo;\ =\ IV[i]&rsquo;\ ㊉\ D(K,\ C_1)[i]$$
其中单引号表示位的补码。这意味着如果攻击者能确定改变IV的某些比特,那么P1接收值的相应比特也会被改变。
0x03. 密码反馈模式 使用密码反馈(Cipher Feedback, CFB)模式能将任意分组密码转化为流密码。流密码不需要将消息填充为分组的整数倍。它还能实时操作。因此,如果传送字符流,使用面向字符的流密码,每个字符都能被及时地加密并传送。
流密码的一个特性是密文和明文长度相等。因此,如果传输8比特的字符,每个字符应该加密为8比特。如果使用超过8比特的字符进行加密,传输能力就被浪费了。下图描述了CFB方案。在该图中,假设传输单元为k比特;通常值是8。和CBC一样,明文单元被连接在一起,所以任意个明文单元的密文都是和之前所有的明文有关的函数。 首先,考虑加密。加密模块的输入是一个64比特的移位寄存器,初始值设定为某一初始向量IV。加密模块输出的最左边(最高) k比特和明文P1的第1个单元进行异或,产生密文C1的第1个单元,然后传输。接下来,移位寄存器的内容都左移k比特,同时将C1放在移位寄存器的最右边(最低) k比特。这个过程一直持续直到所有明文单元都已被加密。注意到途中将密文Ci放在移位寄存器的最右边(最低) k比特的过程标注为FEEDBACK,这就解释了为什么叫密码反馈模式了。</description>
</item>
<item>
<title>Raw Implementation of DES Algorithm</title>
<link>https://jordonyang.github.io/post/security/crypto/symmetry/des/</link>
<pubDate>Tue, 09 Oct 2018 22:06:42 +0800</pubDate>
<guid>https://jordonyang.github.io/post/security/crypto/symmetry/des/</guid>
<description>0x00. DES概述 数据加密标准(Data Encryption Standard),一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。
1. 几个重要的历史时间 1973年美国国家标准局(NBS)向社会公开征集加 密算法,以制定加密算法标准; 1974年第二次征集; 1975年选中IBM的算法,并公布征求意见; 1977年1月15日正式颁布; 1998年底以后停用; 1999年颁布3DES为新标准。 2. 标准加密算法的目标 用于加密保护政府机构和商业部门的非机密的敏感 数据。 用于加密保护静态存储和传输信道中的数据。 安全使用10 ~ 15年。
3.密码的整体特点 分组密码,明文、密文和密钥的分组长度都是64位。 面向二进制的密码算法,因而能够加解密任何形式的计算机数据。 对合运算:
f = f–1 加密和解密共用同一算法,使工程实现的工作量减半。
综合运用了置换、代替、代数等基本密码技术。 基本结构属于Feistel结构。
4. 应用 在全世界范围得到广泛应用。 许多国际组织采用为标准。 产品形式:软件(嵌入式,应用软件) 硬件(芯片,插卡) 5. 结论 用于其设计目标是安全的。 设计精巧、实现容易、使用方便,堪称典范。 为国际信息安全发挥了重要作用。
0x01. 加密过程 64位密钥经子密钥产生算法产生出16个子密钥: K1, K2, &hellip;, K16 , 分别供第一次, 第二次, &hellip;, 第十六次加密迭代使用。 64位明文经初始置换IP, 将数据打乱重排并分成左右两半。左边32位构成L0 , 右边2位构成R0 。 第一次加密迭代:由加密函数f实现子密钥k1对R0的加密,得到32位的f(R0, K1),然后L0⊕f(R0, K1),32位的结果作为第二次加密迭代的R1,以R0作为第二次加密迭代的L1。</description>
</item>
<item>
<title>Touch and Parse Java Class File</title>
<link>https://jordonyang.github.io/post/java/vm/bytecode_base/</link>
<pubDate>Mon, 01 Oct 2018 11:31:12 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/vm/bytecode_base/</guid>
<description>0x00. TOC 0x01.概述 0x02.字节码文件结构 1.魔数 2.版本号 3.常量池 4.访问标志 5.类索引、父类索引与结构索引集合 6.字段表集合 7.方法表集合 8.属性表集合 8.1 Code 8.2 Exception 8.3 LineNumberTable 8.4 LocalVariableTable 8.5 LocalVariableTypeTable 8.6 SourceFile 8.7 ConstantValue 0x03.解析 0x04.参考 0x01. 概述 为了实现在不同的操作系统和硬件体系结构下的平台无关性和语言无关性,Java 语言设计者定义了一套程序存储结构的格式规范,即把源程序编译成具有一定格式的字节码文件,各个平台上的虚拟机再按照相应的规范把该字节码文件解析成其平台下的运行代码。
这样不仅实现了一处编写,到处运行(Write Once, Run Anywhere)的平台无关性,还为在 Java 虚拟机上运行非 Java 语言代码提供了可能。时至今日,已经发展出有很多其他能够在 Java 虚拟机上运行的语言,如Clojure、Groovy、JRuby、Jython、Scala等。
0x02. 字节码文件结构 Class 文件是一组以8位字节为单位的二进制流,各个数据项严格按照顺序无间隙地紧凑排列在 Class 文件中。当遇到需要占用8位字节以上的空间的数据项时,则会将其按照大端(Big-Endian)方式分割成若干个8位字节进行存储(大端方式指数据的最高位字节存储在地址的最低位,最低位字节存储在地址的最高位)。
根据 JVM 规范,Class 文件格式采用包含无符号数和表的类结构体存储数据,无符号数以u1、u2、u4、u8分别代表1、2、4、8个字节的无符号数,用来描述数字、索引引用、UTF-8字符串值等;表是由多个无符号数或其他表作为数据项构成的符合数据类型,用于描述有层次关系的符合结构的数据,一般以_info结尾。另外,除了表和无符号数外,当需要表示一个长度不定的数据时,往往需要前置一个容量计数器。Class 文件格式具体如下图所示:
1. 魔数 如 gif 和 jpeg 等文件的存储标准一样,为了标识和验证其文件的身份,每个 Class 文件使用前四个字节作为魔数(Magic Number),用于确定该文件是否为一个能被虚拟机接受的文件,其值固定为oxCAFEBABE。</description>
</item>
<item>
<title>Arithmetic Expression Generator</title>
<link>https://jordonyang.github.io/post/projects/java/expression_generator/</link>
<pubDate>Tue, 18 Sep 2018 20:25:43 +0800</pubDate>
<guid>https://jordonyang.github.io/post/projects/java/expression_generator/</guid>
<description>前言 : 学校软件工程课程作业, 需求大概是根据参数随机产生n道算术表达式,将表达式及结果写到文件中,将用户提交的答案文件与正确答案进行比对,输出比对结果。
一. PSP2.1表格 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 20 25 · Estimate · 估计这个任务需要多少时间 20 25 Development 开发 1280 1740 · Analysis · 需求分析 (包括学习新技术) 40 55 · Design Spec · 生成设计文档 40 41 · Design Review · 设计复审 (和同事审核设计文档) 30 20 · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 31 · Design · 具体设计 40 66 · Coding · 具体编码 1000 1422 · Code Review · 代码复审 40 41 · Test · 测试(自我测试,修改代码,提交修改) 60 64 Reporting 报告 70 103 · Test Report · 测试报告 20 24 · Size Measurement · 计算工作量 20 21 · Postmortem &amp; Process Improvement Plan · 事后总结, 并提出过程改进计划 30 58 合计 1370 1868 二.</description>
</item>
<item>
<title>Code File Analyser and Counter</title>
<link>https://jordonyang.github.io/post/projects/java/word_counter/</link>
<pubDate>Fri, 14 Sep 2018 12:23:56 +0800</pubDate>
<guid>https://jordonyang.github.io/post/projects/java/word_counter/</guid>
<description>Github仓库地址
前言 : 该项目是学校软件工程课程的一个小作业,要求大概是分析统计代码文件中的一些数据(如行数、词数、注释行数、代码行数、字符数等),下面简单介绍下我的完成情况。
一. PSP2.1表格 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 20 35 · Estimate · 估计这个任务需要多少时间 20 35 Development 开发 460 842 · Analysis · 需求分析 (包括学习新技术) 100 221 · Design Spec · 生成设计文档 30 41 · Design Review · 设计复审 (和同事审核设计文档) 20 38 · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 39 · Design · 具体设计 30 54 · Coding · 具体编码 200 367 · Code Review · 代码复审 20 38 · Test · 测试(自我测试,修改代码,提交修改) 30 44 Reporting 报告 130 43 · Test Report · 测试报告 60 128 · Size Measurement · 计算工作量 40 51 · Postmortem &amp; Process Improvement Plan · 事后总结, 并提出过程改进计划 30 46 合计 610 920 二.</description>
</item>
<item>
<title>Touch Source of HashMap (1)</title>
<link>https://jordonyang.github.io/post/java/collections/map/hashmap_source0/</link>
<pubDate>Mon, 27 Aug 2018 12:01:48 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/collections/map/hashmap_source0/</guid>
<description>概述 横向维护一个Entry数组,用来存放最近一次添加的(key,value)形式的元素 每个数组元素纵向维护一个Entry链表,当发生哈希冲突时,将原先的Entry往后挪,即链地址法解决哈希冲突 方法签名 public class HashMap&lt;K,V&gt; extends AbstractMap&lt;K,V&gt; implements Map&lt;K,V&gt;, Cloneable, Serializable 具有Map的基本方法,可克隆、可序列化(自定义的序列化规则)
基本属性 // 默认容量为16 static final int DEFAULT_INITIAL_CAPACITY = 1 &lt;&lt; 4; // 最大容量 static final int MAXIMUM_CAPACITY = 1 &lt;&lt; 30; // 默认加载因子,值为0.75,扩容时用 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 空的入口数组 static final Entry&lt;?,?&gt;[] EMPTY_TABLE = {}; /** * 入口数组,用来存储链表的头结点 * 一开始将它指向 EMPTY_TABLE,这样的意图直到在第一次添加元素时 * 才正式将table指向大小接近threshold的Entry对象 */ transient Entry&lt;K,V&gt;[] table = (Entry&lt;K,V&gt;[]) EMPTY_TABLE; // 当前表中的元素个数 transient int size; /** * 阈值,超过后扩容,值为capacity * load factor * 一开始table为 EMPTY_TABLE,即创建HashMap对象但未往其中添加元素, * 当第一次往其中添加元素时,表的大小会膨胀到这个值 */ int threshold; // 加载因子 final float loadFactor; // 结构性修改的次数,与fail-fast机制有关 transient int modCount; 构造方法 1.</description>
</item>
<item>
<title>关于我</title>
<link>https://jordonyang.github.io/about/</link>
<pubDate>Thu, 23 Aug 2018 22:32:51 +0800</pubDate>
<guid>https://jordonyang.github.io/about/</guid>
<description>Experiences 2016年9月&ndash;2020年6月: 就读于广东工业大学 Tags Java AES DES RSA SSL Nginx design patttern JVM Redis
Contact 邮箱 [email protected]
Follow Github &nbsp; 豆瓣</description>
</item>
<item>
<title>The Garbage Collection Mechanism of the JVM</title>
<link>https://jordonyang.github.io/post/java/vm/gc_base/</link>
<pubDate>Sat, 04 Aug 2018 23:25:14 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/vm/gc_base/</guid>
<description>一. 概念 什么是垃圾收集?
垃圾收集(Garbage Collection,GC),指的是对Java堆和方法区的死对象进行回收。它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,现已十分成熟。
二. 收集的范围 哪些内存需要回收
在Java内存运行时区域中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,加上基本认为栈帧所分配的大小随类的结构确定而保持不变,因此这些区域的的内存分配和回收都具有确定性,所以我们的内存垃圾回收主要集中于 Java 堆和方法区中,在程序运行期间,这部分内存的分配和回收都是动态的 。
三. 收集目标 Java堆:回收目标为死对象,即那些不会不可能再被任何途径使用的对象。 方法区,即Hotspot VM中的永久代(Permanet Generation):收集效率非常低,其中进行内存垃圾回收的两个主要内容是废弃常量和无用的类。 四. 判活算法 怎么判断一个对象是否死对象?
1. 引用计数(Reference Counting) 即每个对象都有一个引用计数器,当有引用连接至对象时,引用计数加1;当有引用离开作用域或被置为null时,引用计数减1。此方法简单、高效,但无法解决对象相互循环引用的问题。
若对象间存在循环引用,可能出现“对象应该被回收,但引用计数却不为零”的情况。定位这样的交互自引用的对象组所需的工作量极大。引用记数常用来说明垃圾收集的工作方式,但似乎从未被应用于任何一种Java虚拟机实现中。
2. 可达性分析(Reachability Analysis) 从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。
GC停顿:亦称Stop The World(STW),即在整个可达性分析期间,整个执行系统看起来就像是被冻结在某个时间点上,不能出现在分析过程中对象引用关系还在不断变化的情况。
在Java语言中,GC Roots包括:
虚拟机栈中引用的对象 方法区中类静态属性实体引用的对象 方法区中常量引用的对象 本地方法栈中JNI引用的对象 五. 收集方法 有哪些垃圾收集算法?它们各有什么特点?
1. 标记 - 清除算法(Mark-Sweep) ( 1 )原始标记-清除算法 (Naïve mark-and-sweep) 每个对象在内存中都有一个标记位用于垃圾回收,标记该对象到 GC roots 是否可达,然后进行 标记-清理 过程</description>
</item>
<item>
<title>Dive into LinkedList</title>
<link>https://jordonyang.github.io/post/java/collections/list/linkedlist_source/</link>
<pubDate>Mon, 25 Jun 2018 16:44:03 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/collections/list/linkedlist_source/</guid>
<description>一. 概述 LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。 LinkedList 实现 List 接口,能对它进行队列操作。 LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。 LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。 LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。 LinkedList 是非同步的。
二. 基本属性 其中 size 为 LinkedList 的大小,first 和 last 分别为链表的头结点和尾结点。Node 为结点对象。
// 表中已有元素个数 transient int size = 0; // 头结点指针 transient Node&lt;E&gt; first; // 尾结点指针 transient Node&lt;E&gt; last; Node是LinkedList的一个静态内部类,采用典型的双链表结构,定义了存储的数据元素,前一个结点和后一个结点
private static class Node&lt;E&gt; { // 结点数据 E item; // 前一个结点 Node&lt;E&gt; next; // 后一个结点 Node&lt;E&gt; prev; Node(Node&lt;E&gt; prev, E element, Node&lt;E&gt; next) { this.</description>
</item>
<item>
<title>Dive into ArrayList</title>
<link>https://jordonyang.github.io/post/java/collections/list/arraylist_source/</link>
<pubDate>Sat, 23 Jun 2018 22:40:02 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/collections/list/arraylist_source/</guid>
<description>一. 继承体系 1.基于数组实现,是一个动态数组,其容量能自动增长。
2.ArrayList不是线程安全的,建议在单线程中使用,多线程可以选择Vector或CopyOnWriteArrayList。
3.实现了RandomAccess接口,可以通过下标序号进行快速访问。
4.实现了Cloneable接口,能被克隆。
5.实现了Serializable接口,支持序列化。
二. 基本属性 /** * 存储数组默认容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 当使用 new ArrayList(0) 时赋值给elementData * 第一次插入元素时,将重新开辟一个容量为1的存储数组,该引用将被弃用 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 当使用 new ArrayList() 时赋值给elementData * 第一次插入元素时,将重新开辟一个容量为10的存储数组,该引用将被弃用 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 该数组用来存放add进ArrayList的元素 */ transient Object[] elementData; /** * 对象数组中已经存放的元素个数,并不是存储数组的容量 */ private int size; /** * 数组可开辟的最大容量,实际上一个数组所能分配的容量大小取决于JVM分配的堆内存大小 * MAX_ARRAY_SIZE只是一个理论值,可能出于对JVM头信息存储的考虑而设计 * 因为在hugeCapacity 方法中 minCapacity &gt; MAX_ARRAY_SIZE) ?</description>
</item>
<item>
<title>Head First CPU Cache</title>
<link>https://jordonyang.github.io/post/cache/cpu_cache_base/</link>
<pubDate>Mon, 07 May 2018 16:00:29 +0800</pubDate>
<guid>https://jordonyang.github.io/post/cache/cpu_cache_base/</guid>
<description>0x01. 简介 CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小的多但交换速度却比内存要快得多。其出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾,因为CPU运算速度要比内存读写速度快很多,这样会使CPU花费很长时间等待数据到来或把数据写入内存。在缓存中的数据是内存中的一小部分,但由于程序局部性原理,这一小部分是短时间内CPU即将访问的,当CPU调用大量数据时,就可先缓存中调用,从而加快读取速度。
0x02. 工作原理 高速缓存的工作基于程序的局部性原理:程序的执行仅局限于某个部分,相应地,它所访问的存储空间也局限于某个区域。程序的局部性有两个方面的含义:
时间局部性:如果一个存储单元被访问,则可能该单元会很快被再次访问。产生时间局部性的典型原因是在程序中存在着大量的循环操作。 空间局部性:如果一个存储单元被访问,则该单元邻近的单元也可能很快被访问。这是因为程序中大部分指令是顺序存储、顺序执行的,数据一般也是以向童、数组、树、表等形式蔟聚地存储在一起的。 CPU缓冲技术就是利用程序的局部性原理,把程序中正在使用的部分存放在一个高速的容童较小的Cache中,使CPU的访存操作大多数针对Cache进行,从而使程序的执行速度大大提高。
0x03. 存储结构 设一个计算机系统的存储器地址有m位,则可以形成 M = 2m 个不同地址,该机器的高速缓存会被组织成一个含有 S = 2s 个高速缓存组(cache set)的数组,每个组含有E个高速缓存行(cache line)。每行是由一个 B = 2 b 字节的数据块、一个标识缓存行是否有效的有效位(valid bit)及 t = m - (b + s) 个标记位(tag bigs)组成,标记位是当前块的内存地址的位的一部分,用于唯一标识一个存储在缓存行的块。缓存行的具体结构如下图(a)所示:
此外,每个缓存块还可对应若干标志位,包括有效位(valid bit)、脏位(dirty bit)、使用位(use bit)等。这些位在保证正确性、排除冲突、优化性能等方面起着重要作用,如拥有一个脏位的缓存行表示它在从主存读取之后已经被更改,这意味着处理器已经将数据写入该行,并且新值没有一直传播到主存。
0x04. 运行流程 当一条加载指令指示CPU从主存地址A中读取一个字时,CPU将A发送给高速缓存,如果高速缓存中保存有地址A中的那个字的副本,它会立即返回该字给 CPU,高速缓存查找数据副本的流程大概如下:
高速缓存中的参数S和B将m个地址位分为三段,如上图(b)所示。 主存地址A中的s个被解释为一个无符号整数,作为缓存组的索引,用于标记待获取的字存储在哪一个缓存组中。 在有效位为1及缓存组确定的情况下,主存地址A中t个标记位标记待获取的字存储在该缓存组的哪一个缓存行中。 主存地址A中的b个块偏移位标记待获取的数据字从缓存行数据块的哪一个索引开始。 为什么使用中间位而不是高位作索引?
若使用高位作索引,则一些连续的内存块会映射到相同的缓存行,如图中前四行映射到第一个缓存组,第二个四个块映射到第二个组,依次类推,若一个程序具有良好的空间局部性,顺序扫描数组的元素,则在任何时刻,高速缓存中只存有一个块大小的数组内容,这样大大降低了对高速缓存的利用效率。相对而言,用中间位作索引,相邻的块总是映射到不同的高速缓存行。
0x05. 地址映射 根据高速缓存组中包含的缓存行数目E,高速缓存被划分为不同的类型,包括直接映射缓存、组相联缓存等。
1. 直接映射高速缓存 假设系统中CPU、寄存器文件、L1高速缓存和主存个数都为1,当CPU执行一条读内存字w的指令,它向L1高速缓存请求该字,若L1高速缓存有w的副本,则缓存命中(Cache Hit),L1高速缓存返回w的副本数据给CPU;否则缓存缺失(Cache Miss),拼接w中的标记位和索引位,得到w在主存中的块号,结合块内偏移得到该数据字,将之写入高速缓存并返回给CPU。高速缓存分别使用组索引,行标记、块偏移通过进行组选择、行匹配、字抽取三个步骤确定一个请求是否命中,然后取出被请求字。
1.1 组选择 高速缓存从w的地址中抽取出s个组索引位,将之解释为一个表示缓存组数组索引的无符号整数,如下图中的w的s位二进制组索引位为0001,被解释为缓存组1的索引。</description>
</item>
<item>
<title>Thread Run Method Execution Process</title>
<link>https://jordonyang.github.io/post/java/concurrency/base/run_exec/</link>
<pubDate>Sun, 25 Mar 2018 21:46:51 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/concurrency/base/run_exec/</guid>
<description>前言 : 之前听到很多说法都是:“单纯地执行Thread子类的run方法并没有多线程的效果,正确的做法是,用Thread子类对象执行Thread的start方法,这样start方法会自动执行子类的run方法。” 我对这种神奇的说法将信将疑,心里总感觉不踏实,于是点进Thread的源码了解了一下。
一. 入坑过程 然而,纵观 start 方法,并没有找到 run 这个词,那run方法又是在哪里被调用的呢?其中最有嫌疑的地方就是native方法start0。
Thread 类有个 registerNatives 本地方法,该方法主要的作用就是注册一些本地方法供 Thread 类使用,如 start0(),stop0() 等等,可以说,所有操作本地线程的本地方法都是由它注册的。这个方法放在一个静态代码块中,当 Thread 类被加载到 JVM 中的时候,它就会被调用,进而注册相应的本地方法。 而本地方法 registerNatives 是定义在 openjdk\jdk\src\share\native\java\lang\Thread.c文件 中的。Thread.c 是个很小的文件,它定义了各个操作系统平台都要用到的关于线程的公用数据和操作,如下:
可以容易的看出 Java 线程调用start-&gt;start0的方法,实际上会调用到 JVM_StartThread 方法,而 JVM_StartThread 方法则是在openjdk\hotspot\src\share\vm\prims\jvm.cpp中定义的
JVM_ENTRY 作为一个宏,用来定义 JVM_StartThread 函数,可以看到函数内创建了真正的平台相关的本地线程,其线程函数是 thread_entry,如下:
可见其中调用了 vmSymbols::run_method_name 方法, 而 run_method_name 是在 openjdk\hotspot\src\share\vm\classfile\vmSymbols.hpp 中用宏定义的:
这样不难想象到执行 Thread.start() 后的底层调用过程
二. 拓展 理解到 statrt0 方法为我们做了哪些事情后就很容易理解Java中线程启动的大致过程。
1. Thread子类 任务类继承Thread类,并覆盖它的run方法
当 subThread 调用父类的start方法时,由上面的过程可以知道,在JVM申请CPU线程资源并开启线程后,会执行 Thread类 的 run 方法,而由于JVM多态支持,最终调用的是 SubThread 的 run 方法,从而实现在一个线程中完成任务。</description>
</item>
<item>
<title>Decompilation for Foreach Syntax Sugar</title>
<link>https://jordonyang.github.io/post/java/syntax/sugar/foreach_decompilation/</link>
<pubDate>Sun, 25 Feb 2018 22:03:56 +0800</pubDate>
<guid>https://jordonyang.github.io/post/java/syntax/sugar/foreach_decompilation/</guid>
<description>前言 最近一直在看TiJ,在持有对象那章,Bruce Eckel说到Collection的内容可以用foreach遍历,原因是Java SE%引入了Iterable接口,该接口包含一个能够产生Iterator的iterator方法,并且Iterable接口被foreach用来在序列中移动。因此任何Iterable的实现类都可以用于foreach语句。那么我们在尝着这一语法糖的甜头时,JDK底层帮我们做了哪些事情呢?
实验 环境:IntelliJ IDEA 2018.1、jdk1.7.0_80
下面例子的Java代码中使用了两个foreach分别遍历数组和ArrayList:
package reversecompile; import java.util.ArrayList; import java.util.List; public class TestForEach { public static void main(String[] args) { String[] arr = &quot;I am a coder&quot;.split(&quot; &quot;); List&lt;String&gt; strings = new ArrayList&lt;&gt;(); for (String s : arr){ strings.add(s); System.out.println(s); } for (String s : strings){ System.out.println(s); } } } 通过IDEA反编译后的·TestForEach.class:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package reversecompile; import java.</description>
</item>
<item>
<title>Notes for Redis Api Learning</title>
<link>https://jordonyang.github.io/post/db/nosql/redis/redis_api/</link>
<pubDate>Mon, 20 Nov 2017 19:50:14 +0800</pubDate>
<guid>https://jordonyang.github.io/post/db/nosql/redis/redis_api/</guid>
<description>Redis简介及安装 简介 Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。
特点:
可以用来储存字符串,哈希结构,链表,集合,有序集合、 具有“持久化”功能,用来存储 安装 官网:https://redis.io
cd /usr/local/src $ wget http://download.redis.io/releases/redis-4.0.2.tar.gz $ tar xzf redis-4.0.2.tar.gz $ cd redis-4.0.2 $ make make PREFIX=/usr/local/redis install 启动服务
cd /usr/local/redis cp /usr/local/src/redis-4.0.2/redis.conf ./ cd .. ./bin/redis-server ./redis.conf 设置redis服务后台运行
vim redis.conf 将daemonize选项值改为yes
基本数据类型 通用键值操作 增加键
set keyname value 获取键的值
get keyname 查找键
keys pattern:
h?llo matches hello, hallo and hxllo h*llo matches hllo and heeeello h[ae]llo matches hello and hallo, but not hillo h[^e]llo matches hallo, hbllo, &hellip; but not hello h[a-b]llo matches hallo and hbllo 删除键</description>
</item>
<item>
<title>Video Live Broadcasting</title>
<link>https://jordonyang.github.io/post/network/nginx/nginx-rtmp/</link>
<pubDate>Wed, 15 Nov 2017 19:46:54 +0800</pubDate>
<guid>https://jordonyang.github.io/post/network/nginx/nginx-rtmp/</guid>
<description>理论基础 RTMP (Real Time Messaging Protocol)实时消息传输协议,基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议,它就像一个用来装数据包的容器,这些数据既可以是AMF格式的数据,也可以是FLV中的视/音频数据,主要用来在Flash /AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括Adobe Media Server/Ultrant Media Server/red5等。
FFmpeg (Fast Forward Mpeg)是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。
主要功能:视频采集,视频格式转换,视频截图,给视频加水印等。 支持的视频编码为Snow和FFV1。 支持的视频格式包括ASF、AVI、BFI[7]、IFF[8]、RL2[9]、FLV、MXF, Material eXchange Format,、SMPTE 377M、Matroska、Maxis XA[10]、MSN Webcam stream[11]、MPEG transport stream、TXD[6]、OMA[12]、GXF,、General eXchange Format, SMPTE 360M、mov,mp4、m4a、3gp等。 支持的协议:HTTP、RTP、RTSP、RealMedia RTSP/RDT、TCP、UDP、Gopher、RTMP 、RTMPT, RTMPE, RTMPTE, RTMPS (via librtmp)、SDP、MMS over TCP Nginx 是一个高性能的HTTP和反向代理服务器,是一款轻量级的Web服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
应用场景
http服务器,可以做静态网页的http服务器。 配置虚拟机 一个域名可以被多个ip绑定。可以根据域名的不同把请求转发给运行在不同端口的服务器。(一台服务器用来部署多个项目)
反向代理,负载均衡。把请求转发给不同的服务器。 反向代理服务器Ip地址域名绑定ip使用方向代理软件解决服务器集群问题。服务器1服务器1服务器
nginx-rtmp-module :一个由俄罗斯人Roman Arutyunyan开发出来的支持rtmp协议的Nginx模块,特征:
支持RTMP / HLS / MPEG-DASH直播 通过RTMP视频点播FLV / MP4格式文件,从本地文件系统或HTTP播放 流中继支持分布式流式传输:推拉式模式 在多个FLV中录制流 H264 / AAC支持 使用FFmpeg在线转码 HTTP回调(发布/播放/记录/更新等) 在某些事件上运行外部程序(exec) 用于录制音频/视频和丢弃客户端的HTTP控制模块 先进的缓冲技术可以将内存分配保持在最低水平,从而实现更快的数据流和更小的内存占用 可以与Wirecast,FMS,Wowza,JWPlayer,FlowPlayer,StrobeMediaPlayback,ffmpeg ,avconv,rtmpdump,flvstreamer等一起工作 以机器和人类可读的形式在XML / XSL中进行统计 Linux的/ FreeBSD的/ MacOS的/视窗 Video.</description>
</item>
<item>
<title>Construction and Operations of AVL</title>
<link>https://jordonyang.github.io/post/alg/tree/bst/avl-bst_construction_operation/</link>
<pubDate>Fri, 10 Nov 2017 17:17:36 +0800</pubDate>
<guid>https://jordonyang.github.io/post/alg/tree/bst/avl-bst_construction_operation/</guid>
<description>0x01. 定义 对于二叉查找树,当其高度最小时,其查找性能最优,虽然满二叉树和完全二叉树的高度都是最小的,但是在在插入或删除结点后维持其高度最小的代价较大。平衡二叉查找树是一种可兼顾查找与维护性能的折中方案。
平衡二叉查找树又称AVL树,得名于它的发明者Adelson-Velsky和Landis,他们在1962年的论文《An algorithm for the organization of information》中公开了这一数据结构。
平衡二叉树是具有以下性质的二叉查找树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的高度差的绝对值不超过1。结点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的结点被认为是平衡的。带有平衡因子-2或2的结点被认为是不平衡的,并需要重新平衡这棵树。平衡因子可以直接存储在每个结点中,或从可能存储在节点中的子树高度计算出来。
平衡二叉树的结点类型如下所示,其中属性height表示子树的高度,用于计算平衡因子。
private class Node { private final Key key; // the key private Value val; // the associated value private int height; // height of the subtree private int size; // number of nodes in subtree private Node left; // left subtree private Node right; // right subtree public Node(Key key, Value val, int height, int size) { this.</description>
</item>
<item>
<title>Construction and Operatons of BST</title>
<link>https://jordonyang.github.io/post/alg/tree/bst/construction_common_operaton/</link>
<pubDate>Sat, 21 Oct 2017 21:52:45 +0800</pubDate>
<guid>https://jordonyang.github.io/post/alg/tree/bst/construction_common_operaton/</guid>
<description>0x00. TOC 0x01.BST结构 0x02.基本操作 1.插入元素 2.判存 3.删除结点 4.求树的最大深度 5.搜索与遍历 0x03.参考 0x01. BST结构 二叉搜索树(BST, Binary Searching Tree)也称为二叉查找树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:TOC
若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值; 任意节点的左、右子树也分别为二叉查找树; 没有键值相等的节点。 二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为 O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、多重集、关联数组等。
二叉查找树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉查找树的存储结构。中序遍历二叉查找树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉查找树变成一个有序序列,构造树的过程即为对无序序列进行查找的过程。每次插入的新的结点都是二叉查找树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索、插入、删除的复杂度等于树高,期望 O(log n),最坏 O(n)(数列有序,树退化成线性表)。
虽然二叉查找树的最坏效率是 O(n),但它支持动态查询,且有很多改进版的二叉查找树可以使树高为 O(log n),从而将最坏效率降至 O(log n),如AVL树、红黑树等。二叉搜索树的Java数据结构定义如下:
// 根结点 private TreeNode root; // 二叉树结点类定义 private static class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int val) { this.val = val; } } 其中使用静态内部类TreeNode抽象表示二叉搜索树中的结点,同时将结点的数据定义为整型,当然如果要考虑扩展性,可以将之设定为Comparable类型,这样可以将任何一种的实现Comparable接口的类型数据作为val的类型,只需提供正确的compareTo()方法即可。</description>
</item>
<item>
<title>Awesome Terminator for Ubuntu</title>
<link>https://jordonyang.github.io/post/linux/tools/terminator/</link>
<pubDate>Sun, 01 Oct 2017 11:22:17 +0800</pubDate>
<guid>https://jordonyang.github.io/post/linux/tools/terminator/</guid>
<description>前言:Terminator是CrunchBang的默认终端,该终端基于GNOME terminal。Terminator最大的特点就是可以在一个屏幕下同时显示多个终端,可以自由的在一个窗口中分割区域建立新终端,通过鼠标拉伸调整每个终端的大小,对同时需要操作多个终端的用户非常方便。 同时操作多个VPS的时候不用切换终端窗口,在一个窗口中就可以非常方便地完成。这里做个简单的记录,免得下次换机器的时候又得找配置。
0x00. 下载与卸载 sudo add-apt-repository ppa:gnome-terminator sudo apt-get update sudo apt-get install terminator # 卸载 sudo apt-get remove terminator 0x01. 设置为默认终端 安装dconfig-editor sudo apt-get install dconf-tools 安装成功后,就可以在终端中通过命令 dconf-editor来打开这个工具,然后从左边的的菜单栏中按照下面的步骤依次进入指定的菜单项: org &gt; gnome &gt; desktop &gt; applications &gt; terminal 此时,我们可以看到使用 Terminator 作为默认终端工具的配置为:
exec terminator exec-arg -e 如果想使用 gnome-terminal 为默认终端工具的话,就将上面的配置更改为:
exec gnome-terminal exec-arg -x 保存退出之后,就可以使用快捷键Ctrl + Alt + T启动terminator了。
0x02. 美化 terminator的默认样式比较丑,不符合我这种追求cool stuff的人的审美,那怎么折腾呢?通过文件配置或者界面选择(说到底也是使用配置文件,但配置起来比较直观)就行了。
需要自己在~/.config下创建terminator/config文件,或者在terminator界面右键进入Preferences的Layout标签选择Add,将自动创建该文件。然后使用下面的命令编辑该文件;</description>
</item>
<item>
<title>Construction of Binary Heap and Heap Sort</title>
<link>https://jordonyang.github.io/post/alg/heap/heap_construction/</link>
<pubDate>Mon, 25 Sep 2017 22:32:08 +0800</pubDate>
<guid>https://jordonyang.github.io/post/alg/heap/heap_construction/</guid>
<description>前言 : 最近在看《算法导论》,从中感受到了算法世界的神奇,看完二叉堆后觉得实现起来应该不难,所以试着根据书中的伪代码用Java实现一遍。
0x00. 概念 1. 基本定义 二叉堆是一个数组,可近似看成完全二叉树,表示堆的数组A通常包含两个属性:A.length(数组长度)和A.heap-size(堆的有效元素)
private int[] heap; private int heapSize; public MyHeap(int[] array) { this.heap = array; this.heapSize = array.length; } 2. 通过下标定位亲属节点 由于堆可看做完全二叉树,所以可以通过某节点的下标获取其左右子节点和父节点
private int left(int i) { return 2 * i + 1; } private int right(int i) { return 2 * i + 2; } private int parent(int i) { return (i - 1) / 2; } 3. 堆的一些性质 最大堆性质:A[PARENT[i] &gt;= A[i]] 最小堆性质:A[PARENT[i] &lt;= A[i]] 含有n个元素的堆的高为:小于等于lgn的最大整数 当用数组表示存储了n个元素的堆时,叶节点下标分别为 (n/2) + 1, (n / 2) + 2, &hellip;, n(下标从1开始) 0x01.</description>
</item>
</channel>
</rss>