auto commit
This commit is contained in:
parent
d0fa9aff51
commit
480a8070cd
|
@ -223,7 +223,7 @@ System.out.println(s4 == s5); // true
|
|||
|
||||
Java 的参数是以值传递的形式传入方法中,而不是引用传递。
|
||||
|
||||
Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。而在方法中改变对象的成员变量值也会使原对象的成员变量发生变化,因为是改变同一个地址指向的内容。
|
||||
以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。但是如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。
|
||||
|
||||
```java
|
||||
public class Dog {
|
||||
|
@ -237,10 +237,6 @@ public class Dog {
|
|||
return name;
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String getObjectAddress() {
|
||||
return super.toString();
|
||||
}
|
||||
|
@ -255,9 +251,6 @@ public class PassByValueExample {
|
|||
func(dog);
|
||||
System.out.println(dog.getObjectAddress()); // Dog@4554617c
|
||||
System.out.println(dog.getName()); // A
|
||||
func2(dog);
|
||||
System.out.println(dog.getObjectAddress()); // Dog@4554617c
|
||||
System.out.println(dog.getName()); // C
|
||||
}
|
||||
|
||||
private static void func(Dog dog) {
|
||||
|
@ -266,13 +259,6 @@ public class PassByValueExample {
|
|||
System.out.println(dog.getObjectAddress()); // Dog@74a14482
|
||||
System.out.println(dog.getName()); // B
|
||||
}
|
||||
|
||||
private static void func2(Dog dog) {
|
||||
System.out.println(dog.getObjectAddress()); // Dog@4554617c
|
||||
dog.setName("C");
|
||||
System.out.println(dog.getObjectAddress()); // Dog@4554617c
|
||||
System.out.println(dog.getName()); // C
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1419,7 +1419,7 @@ public final int incrementAndGet() {
|
|||
}
|
||||
```
|
||||
|
||||
以下代码是 getAndAddInt() 源码,var1 指示对象内存地址,var2 指示该字段相对对象内存地址的偏移,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 该字段内存地址中的值==var5,那么就更新内存地址为 var1+var2 的变量为 var5+var4。可以看到 getAndAddInt() 在一个循环中进行,发生冲突的做法是不断的进行重试。
|
||||
以下代码是 getAndAddInt() 源码,var1 指示对象内存地址,var2 指示该字段相对对象内存地址的偏移,var4 指示操作需要加的数值,这里为 1。通过 getIntVolatile(var1, var2) 得到旧的预期值,通过调用 compareAndSwapInt() 来进行 CAS 比较,如果 该字段内存地址中的值 ==var5,那么就更新内存地址为 var1+var2 的变量为 var5+var4。可以看到 getAndAddInt() 在一个循环中进行,发生冲突的做法是不断的进行重试。
|
||||
|
||||
```java
|
||||
public final int getAndAddInt(Object var1, long var2, int var4) {
|
||||
|
|
|
@ -102,7 +102,7 @@ Class 文件中的常量池(编译器生成的各种字面量和符号引用
|
|||
|
||||
## 直接内存
|
||||
|
||||
在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为其避免了在 Java 堆和 Native 堆中来回复制数据。
|
||||
在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
|
||||
|
||||
# 二、垃圾收集
|
||||
|
||||
|
@ -146,7 +146,7 @@ Java 虚拟机使用该算法来判断对象是否可被回收,在 Java 中 GC
|
|||
|
||||
### 3. 引用类型
|
||||
|
||||
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。
|
||||
无论是通过引用计算算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。
|
||||
|
||||
Java 具有四种强度不同的引用类型。
|
||||
|
||||
|
@ -256,7 +256,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
|
|||
一般将 Java 堆分为新生代和老年代。
|
||||
|
||||
- 新生代使用:复制算法
|
||||
- 老年代使用:标记 - 清除 或者 标记 - 整理 算法
|
||||
- 老年代使用:标记 - 清理 或者 标记 - 整理 算法
|
||||
|
||||
## 垃圾收集器
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
* [GNU](#gnu)
|
||||
* [开源协议](#开源协议)
|
||||
* [二、磁盘](#二磁盘)
|
||||
* [HDD](#hdd)
|
||||
* [磁盘接口](#磁盘接口)
|
||||
* [磁盘的文件名](#磁盘的文件名)
|
||||
* [三、分区](#三分区)
|
||||
|
@ -185,21 +184,6 @@ GNU 计划,译为革奴计划,它的目标是创建一套完全自由的操
|
|||
|
||||
# 二、磁盘
|
||||
|
||||
## HDD
|
||||
|
||||
Hard Disk Drives(HDD) 俗称硬盘,具有以下结构:
|
||||
|
||||
- 盘面(Platter):一个硬盘有多个盘面;
|
||||
- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道;
|
||||
- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小;
|
||||
- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写);
|
||||
- 制动手臂(Actuator arm):用于在磁道之间移动磁头;
|
||||
- 主轴(Spindle):使整个盘面转动。
|
||||
|
||||
<div align="center"> <img src="../pics//014fbc4d-d873-4a12-b160-867ddaed9807.jpg" width=""/> </div><br>
|
||||
|
||||
[Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1)
|
||||
|
||||
## 磁盘接口
|
||||
|
||||
### 1. IDE
|
||||
|
@ -1025,10 +1009,10 @@ g/re/p(globally search a regular expression and print),使用正则表示式
|
|||
|
||||
```html
|
||||
$ grep [-acinv] [--color=auto] 搜寻字符串 filename
|
||||
-c : 计算找到个数
|
||||
-c : 统计个数
|
||||
-i : 忽略大小写
|
||||
-n : 输出行号
|
||||
-v : 反向选择,亦即显示出没有 搜寻字符串 内容的那一行
|
||||
-v : 反向选择,也就是显示出没有 搜寻字符串 内容的那一行
|
||||
--color=auto :找到的关键字加颜色显示
|
||||
```
|
||||
|
||||
|
@ -1068,7 +1052,7 @@ $ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt)
|
|||
|
||||
awk 每次处理一行,处理的最小单位是字段,每个字段的命名方式为:\$n,n 为字段号,从 1 开始,\$0 表示一整行。
|
||||
|
||||
示例 1:取出登录用户的用户名和 ip
|
||||
示例:取出登录用户的用户名和 IP
|
||||
|
||||
```html
|
||||
$ last -n 5
|
||||
|
@ -1077,7 +1061,9 @@ dmtsai pts/0 192.168.1.100 Thu Jul 9 23:36 - 02:58 (03:22)
|
|||
dmtsai pts/0 192.168.1.100 Thu Jul 9 17:23 - 23:36 (06:12)
|
||||
dmtsai pts/0 192.168.1.100 Thu Jul 9 08:02 - 08:17 (00:14)
|
||||
dmtsai tty1 Fri May 29 11:55 - 12:11 (00:15)
|
||||
```
|
||||
|
||||
```html
|
||||
$ last -n 5 | awk '{print $1 "\t" $3}'
|
||||
```
|
||||
|
||||
|
@ -1087,7 +1073,7 @@ $ last -n 5 | awk '{print $1 "\t" $3}'
|
|||
$ awk '条件类型 1 {动作 1} 条件类型 2 {动作 2} ...' filename
|
||||
```
|
||||
|
||||
示例 2:/etc/passwd 文件第三个字段为 UID,对 UID 小于 10 的数据进行处理。
|
||||
示例:/etc/passwd 文件第三个字段为 UID,对 UID 小于 10 的数据进行处理。
|
||||
|
||||
```text
|
||||
$ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}'
|
||||
|
@ -1104,7 +1090,7 @@ awk 变量:
|
|||
| NR | 目前所处理的是第几行数据 |
|
||||
| FS | 目前的分隔字符,默认是空格键 |
|
||||
|
||||
示例 3:输出正在处理的行号,并显示每一行有多少字段
|
||||
示例:显示正在处理的行号以及每一行有多少字段
|
||||
|
||||
```html
|
||||
$ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}'
|
||||
|
@ -1125,19 +1111,19 @@ dmtsai lines: 5 columns: 9
|
|||
|
||||
示例一:查看自己的进程
|
||||
|
||||
```
|
||||
```sh
|
||||
# ps -l
|
||||
```
|
||||
|
||||
示例二:查看系统所有进程
|
||||
|
||||
```
|
||||
```sh
|
||||
# ps aux
|
||||
```
|
||||
|
||||
示例三:查看特定的进程
|
||||
|
||||
```
|
||||
```sh
|
||||
# ps aux | grep threadx
|
||||
```
|
||||
|
||||
|
@ -1147,7 +1133,7 @@ dmtsai lines: 5 columns: 9
|
|||
|
||||
示例:两秒钟刷新一次
|
||||
|
||||
```
|
||||
```sh
|
||||
# top -d 2
|
||||
```
|
||||
|
||||
|
@ -1157,7 +1143,7 @@ dmtsai lines: 5 columns: 9
|
|||
|
||||
示例:查看所有进程树
|
||||
|
||||
```
|
||||
```sh
|
||||
# pstree -A
|
||||
```
|
||||
|
||||
|
@ -1167,14 +1153,12 @@ dmtsai lines: 5 columns: 9
|
|||
|
||||
示例:查看特定端口的进程
|
||||
|
||||
```
|
||||
```sh
|
||||
# netstat -anp | grep port
|
||||
```
|
||||
|
||||
## 进程状态
|
||||
|
||||
<div align="center"> <img src="../pics//76a49594323247f21c9b3a69945445ee.png" width=""/> </div><br>
|
||||
|
||||
| 状态 | 说明 |
|
||||
| :---: | --- |
|
||||
| R | running or runnable (on run queue) |
|
||||
|
@ -1183,9 +1167,11 @@ dmtsai lines: 5 columns: 9
|
|||
| Z | zombie (terminated but not reaped by its parent) |
|
||||
| T | stopped (either by a job control signal or because it is being traced) |
|
||||
|
||||
<div align="center"> <img src="../pics//76a49594323247f21c9b3a69945445ee.png" width=""/> </div><br>
|
||||
|
||||
## SIGCHLD
|
||||
|
||||
当一个子进程改变了它的状态时:停止运行,继续运行或者退出,有两件事会发生在父进程中:
|
||||
当一个子进程改变了它的状态时(停止运行,继续运行或者退出),有两件事会发生在父进程中:
|
||||
|
||||
- 得到 SIGCHLD 信号;
|
||||
- waitpid() 或者 wait() 调用会返回。
|
||||
|
@ -1194,7 +1180,7 @@ dmtsai lines: 5 columns: 9
|
|||
|
||||
其中子进程发送的 SIGCHLD 信号包含了子进程的信息,包含了进程 ID、进程状态、进程使用 CPU 的时间等。
|
||||
|
||||
在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息。父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。
|
||||
在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。
|
||||
|
||||
## wait()
|
||||
|
||||
|
@ -1206,11 +1192,7 @@ pid_t wait(int *status)
|
|||
|
||||
如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1,同时 errno 被置为 ECHILD。
|
||||
|
||||
参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL:
|
||||
|
||||
```c
|
||||
pid = wait(NULL);
|
||||
```
|
||||
参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL。
|
||||
|
||||
## waitpid()
|
||||
|
||||
|
@ -1238,9 +1220,9 @@ options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 w
|
|||
|
||||
僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。
|
||||
|
||||
系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
|
||||
系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
|
||||
|
||||
要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵死进程所占有的资源,从而结束僵尸进程。
|
||||
要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。
|
||||
|
||||
# 参考资料
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* [字符串](#字符串)
|
||||
* [时间和日期](#时间和日期)
|
||||
* [三、索引](#三索引)
|
||||
* [B Tree 原理](#b-tree-原理)
|
||||
* [B+ Tree 原理](#b-tree-原理)
|
||||
* [索引分类](#索引分类)
|
||||
* [索引的优点](#索引的优点)
|
||||
* [索引优化](#索引优化)
|
||||
|
@ -125,49 +125,33 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提
|
|||
|
||||
索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
|
||||
|
||||
## B Tree 原理
|
||||
## B+ Tree 原理
|
||||
|
||||
### 1. B-Tree
|
||||
### 1. 数据结构
|
||||
|
||||
<div align="center"> <img src="../pics//06976908-98ab-46e9-a632-f0c2760ec46c.png"/> </div><br>
|
||||
B Tree 指的是 Balance Tree,也就是平衡树。平衡树时一颗查找树,并且所有叶子节点位于同一层。
|
||||
|
||||
定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构:
|
||||
B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具有 B Tree 的平衡性,并且通过顺序访问指针来提高区间查询的性能。
|
||||
|
||||
- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
|
||||
- 一个节点中的 key 从左到右非递减排列;
|
||||
- 如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于等于 key<sub>i</sub> 且小于等于 key<sub>i+1</sub>。
|
||||
|
||||
查找算法:首先在根节点进行二分查找,如果找到则返回对应节点的 data,否则在相应区间的指针指向的节点递归进行查找。
|
||||
|
||||
由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作以保持 B-Tree 性质。
|
||||
|
||||
### 2. B+Tree
|
||||
|
||||
<div align="center"> <img src="../pics//7299afd2-9114-44e6-9d5e-4025d0b2a541.png"/> </div><br>
|
||||
|
||||
与 B-Tree 相比,B+Tree 有以下不同点:
|
||||
|
||||
- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度);
|
||||
- 内节点不存储 data,只存储 key;
|
||||
- 叶子节点不存储指针。
|
||||
|
||||
### 3. 顺序访问指针
|
||||
在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于等于 key<sub>i</sub> 且小于等于 key<sub>i+1</sub>。
|
||||
|
||||
<div align="center"> <img src="../pics//061c88c1-572f-424f-b580-9cbce903a3fe.png"/> </div><br>
|
||||
|
||||
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
|
||||
### 2. 操作
|
||||
|
||||
### 4. 优势
|
||||
操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
|
||||
|
||||
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B Tree 作为索引结构,主要有以下两个原因:
|
||||
插入删除操作记录会破坏平衡树的平衡性,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作。
|
||||
|
||||
### 3. 与红黑树的比较
|
||||
|
||||
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,主要有以下两个原因:
|
||||
|
||||
(一)更少的检索次数
|
||||
|
||||
平衡树检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(log<sub>d</sub>N),其中 d 为每个节点的出度。
|
||||
|
||||
红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,因此检索的次数也就更多。
|
||||
|
||||
B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。
|
||||
红黑树的出度为 2,而 B+ Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B+ Tree 大非常多,因此检索的次数也就更多。
|
||||
|
||||
(二)利用计算机预读特性
|
||||
|
||||
|
@ -175,8 +159,6 @@ B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了
|
|||
|
||||
操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,相邻的节点也能够被预先载入。
|
||||
|
||||
更多内容请参考:[MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
||||
|
||||
## 索引分类
|
||||
|
||||
### 1. B+Tree 索引
|
||||
|
@ -442,3 +424,4 @@ MySQL 读写分离能提高性能的原因在于:
|
|||
- [服务端指南 数据存储篇 | MySQL(09) 分库与分表带来的分布式困境与应对之策](http://blog.720ui.com/2017/mysql_core_09_multi_db_table2/ "服务端指南 数据存储篇 | MySQL(09) 分库与分表带来的分布式困境与应对之策")
|
||||
- [How to create unique row ID in sharded databases?](https://stackoverflow.com/questions/788829/how-to-create-unique-row-id-in-sharded-databases)
|
||||
- [SQL Azure Federation – Introduction](http://geekswithblogs.net/shaunxu/archive/2012/01/07/sql-azure-federation-ndash-introduction.aspx "Title of this entry.")
|
||||
- [MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
||||
|
|
|
@ -420,6 +420,8 @@ Reids 具体有 6 种淘汰策略:
|
|||
|
||||
使用 Redis 缓存数据时,为了提高缓存命中率,需要保证缓存数据都是热点数据。可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。
|
||||
|
||||
Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰。
|
||||
|
||||
# 八、持久化
|
||||
|
||||
Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
|
||||
|
@ -601,3 +603,4 @@ Redis 没有关系型数据库中的表这一概念来将同种类型的数据
|
|||
- [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide)
|
||||
- [Redis 应用场景](http://www.scienjus.com/redis-use-case/)
|
||||
- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
|
||||
- [Using Redis as an LRU cache](https://redis.io/topics/lru-cache)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* [段页式](#段页式)
|
||||
* [分页与分段的比较](#分页与分段的比较)
|
||||
* [五、设备管理](#五设备管理)
|
||||
* [磁盘结构](#磁盘结构)
|
||||
* [磁盘调度算法](#磁盘调度算法)
|
||||
* [六、链接](#六链接)
|
||||
* [编译系统](#编译系统)
|
||||
|
@ -304,7 +305,7 @@ void P2() {
|
|||
|
||||
为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。
|
||||
|
||||
注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,也就无法执行 up(empty) 操作,empty 永远都为 0,那么生产者和消费者就会一直等待下去,造成死锁。
|
||||
注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等待下,不会释放锁,消费者因此也会永远等待下去。
|
||||
|
||||
```c
|
||||
#define N 100
|
||||
|
@ -314,7 +315,7 @@ semaphore empty = N;
|
|||
semaphore full = 0;
|
||||
|
||||
void producer() {
|
||||
while(TRUE){
|
||||
while(TRUE) {
|
||||
int item = produce_item();
|
||||
down(&empty);
|
||||
down(&mutex);
|
||||
|
@ -325,7 +326,7 @@ void producer() {
|
|||
}
|
||||
|
||||
void consumer() {
|
||||
while(TRUE){
|
||||
while(TRUE) {
|
||||
down(&full);
|
||||
down(&mutex);
|
||||
int item = remove_item();
|
||||
|
@ -542,7 +543,7 @@ int pipe(int fd[2]);
|
|||
|
||||
它具有以下限制:
|
||||
|
||||
- 只支持半双工通信(单向传输);
|
||||
- 只支持半双工通信(单向交替传输);
|
||||
- 只能在父子进程中使用。
|
||||
|
||||
<div align="center"> <img src="../pics//53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png"/> </div><br>
|
||||
|
@ -707,7 +708,7 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户
|
|||
|
||||
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。
|
||||
|
||||
为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到一部分不在物理内存中的地址空间时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。
|
||||
为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。
|
||||
|
||||
从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序称为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。
|
||||
|
||||
|
@ -715,12 +716,11 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户
|
|||
|
||||
## 分页系统地址映射
|
||||
|
||||
- 内存管理单元(MMU):管理着地址空间和物理内存的转换。
|
||||
- 页表(Page table):页(地址空间)和页框(物理内存空间)的映射表。例如下图中,页表的第 0 个表项为 010,表示第 0 个页映射到第 2 个页框。页表项的最后一位用来标记页是否在内存中。
|
||||
内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。
|
||||
|
||||
下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。因此对于虚拟地址(0010 000000000100),前 4 位是用来存储页面号,而后 12 位存储在页中的偏移量。
|
||||
下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位,也就是存储页面号,剩下 12 个比特位存储偏移量。
|
||||
|
||||
(0010 000000000100)根据前 4 位得到页号为 2,读取表项内容为(110 1),它的前 3 为为页框号,最后 1 位表示该页在内存中。最后映射得到物理内存地址为(110 000000000100)。
|
||||
例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1)。该页在内存中,并且页框的地址为 (110 000000000100)。
|
||||
|
||||
<div align="center"> <img src="../pics//cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png" width="500"/> </div><br>
|
||||
|
||||
|
@ -828,11 +828,22 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问
|
|||
|
||||
# 五、设备管理
|
||||
|
||||
## 磁盘结构
|
||||
|
||||
- 盘面(Platter):一个磁盘有多个盘面;
|
||||
- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道;
|
||||
- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小;
|
||||
- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写);
|
||||
- 制动手臂(Actuator arm):用于在磁道之间移动磁头;
|
||||
- 主轴(Spindle):使整个盘面转动。
|
||||
|
||||
<div align="center"> <img src="../pics//014fbc4d-d873-4a12-b160-867ddaed9807.jpg"/> </div><br>
|
||||
|
||||
## 磁盘调度算法
|
||||
|
||||
读写一个磁盘块的时间的影响因素有:
|
||||
|
||||
- 旋转时间(主轴旋转磁盘,使得磁头移动到适当的扇区上)
|
||||
- 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)
|
||||
- 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)
|
||||
- 实际的数据传输时间
|
||||
|
||||
|
@ -933,8 +944,9 @@ gcc -o hello hello.c
|
|||
- Tanenbaum A S, Bos H. Modern operating systems[M]. Prentice Hall Press, 2014.
|
||||
- 汤子瀛, 哲凤屏, 汤小丹. 计算机操作系统[M]. 西安电子科技大学出版社, 2001.
|
||||
- Bryant, R. E., & O’Hallaron, D. R. (2004). 深入理解计算机系统.
|
||||
- 史蒂文斯. UNIX 环境高级编程 [M]. 人民邮电出版社, 2014.
|
||||
- [Operating System Notes](https://applied-programming.github.io/Operating-Systems-Notes/)
|
||||
- [进程间的几种通信方式](http://blog.csdn.net/yufaw/article/details/7409596)
|
||||
- [Operating-System Structures](https://www.cs.uic.edu/\~jbell/CourseNotes/OperatingSystems/2_Structures.html)
|
||||
- [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php)
|
||||
- [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1)
|
||||
- [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1)
|
||||
|
|
Loading…
Reference in New Issue
Block a user