- 进程与线程的区别
- 进程调度算法的特点以及使用场景
- 进程通信方法的特点以及使用场景IPC
- 死锁
- 虚拟内存的作用,分页系统实现虚拟内存原理
- 页面置换算法的原理
- 比较分页与分段的区别
- 分析静态链接的不足,以及动态链接的特点
- linux中僵尸进程与孤儿进程的区别
- linux中硬链接与软链接的区别
(单位, 包含关系, 创建, 通信, 创建销毁切换成本, 资源)
- 进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
- 同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程。
- 进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
- 线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多
- 线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
- 进程拥有的资源: 虚存空间,对处理器, 进程间通信资源, 文件和IO资源的受保护访问; 线程的资源: 线程状态, 处理器上下文(寄存器和程序计数器),执行栈, 线程本地的存储空间.
- 线程有自己的私有属性TCB,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志
(FCFS, RR, 总时间最短, 剩余时间最短, 响应性最高, 多级反馈)
进程调度算法首先分为两种: 抢占式和非抢占式
- 抢占式: 当一个进程占用处理器时, 可以被中断
- 非抢占式: 一旦选择了一个进程使用处理器, 只能等到其主动释放处理器.
(5种)
- 信号: 传递的信息少, 通常是一个通知, 由被调用进程注册signal处理函数对其处理
- 管道: 依赖父进程, 将上一个进程的输出作为下一个进程的输入, unix中管道以文件的形式存在,会产生读写阻塞.
- 消息队列: 不依赖父进程可以在多个独立的进程时间传递信息, 更加结构化消息.
- 共享内存: 速度最快, 通过地址空间的映射实现, 需要应用程序自己控制同步问题.
- socket
- 死锁概念: 进程之间相互等待对方占有的资源而无法运行的情况
- 饥饿概念: 由于资源分配策略不公平导致某些进程持续得不到资源或不能执行
- 死锁产生条件: 资源独占 不可剥夺 持续申请 循环等待(前三个是必要条件)
- 解决死锁的策略
- 死锁预防: 按一定的顺序获取锁打破循环等待
- 死锁避免: 银行家算法
- 死锁检测: Aloc + available的算法
- 死锁恢复:
死锁的代码:
class DeadLock implements Runnable {
private String lock1, lock2;
public DeadLock(String lock1, String lock2) {
this.lock1 = lock1;
this.lock2 = lock2;
}
public void run() {
synchronized(lock1) {
System.out.println(Thread.currentThread().getName() + " obtain " + lock1);
try {
Thread.sleep(200);
synchronized(lock2) {
System.out.println(Thread.currentThread().getName() + " obtain " + lock2);
}
}
catch (InteruptionException e) {
}
}
}
}
public class DeadLockSample {
public static void main(String[] args) {
String s1 = "lock1";
String s2 = "lock2";
new Thread(new DeadLock(s1, s2)).start();
new Thread(new DeadLock(s2, s1)).start();
}
}
上述代码大概率可以出现死锁的情况, 可以使用jstack工具进入当前的JVM进程查看线程的情况, 来分析是否有死锁, 重点的查看block的线程的锁占用情况.
银行家算法的实现:
// todo
Java和数据库中防止死锁的基本方法是保证加锁的顺序一致.
数据库中用for update?
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
- 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕。
(局部性原理 2条, 虚存作用 2条, 虚存地址查找实际页面的过程)
虚拟内存的基础是局部性原理
- 时间局部性, 如果一个数据正在被访问, 它最近还可能被访问
- 空间局部性, 如果一个数据正在被访问, 它临近的数据最近也可能被访问
虚拟内存的作用
- 应用程序的大小不受物理内存大小的限制, 与物理内存大小解耦, 程序员不需要考虑内存的overlay
- 程序不需要完全装入内存就可以运行, 有更多进程可以进入内存, 提高处理器的吞吐率
分页是系统的虚存实现原理
- 逻辑地址: 程序在编译时地址使用的是逻辑地址, 逻辑地址可以看做是页号+偏移量
- 进程运行时当需要访问一个逻辑地址时, 首先按照页号去进程的页表查找页框号, 如果当前页在内存中, 则得到页框号+偏移量获得物理地址; 如果当前页不在内存中, 则报缺页中断,进程被阻塞由OS负责将当前页从磁盘中加载到内存, 这个过程还需要考虑页面置换
- 由于虚拟内存的大小例如32位地址4k的页面大小, 则页号空间为2^20, 因此需要采用多级页表, 页表的管理也按照虚存进行管理, 如果每次访问一个虚存都需要查找页表则会导致性能的下降, 因此引入了TLB(转换检测缓冲区), 首先在TLB中查找页框号, 如果没有再去页表中查找.
(4种)
- OPT: 未来最长时间不会用到的页, 不可能实现
- LRU: 内存中最久没有访问的页, 硬件实现难度大
- FIFO: 最早加载的也可能是频繁访问的, 频繁切换
- 时钟: 设置访问位;记录上一次检查到这一次之间是否被访问过, 如果访问过,就暂时不替换, 但是把访问位置为0; 如果所有的页访问位都为1, 转一圈全部置为0后, 遇到的第一个0的页面置换出去
(可见性, 大小, 碎片)
- 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的 ;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的。
- 段的大小不固定,有它所完成的功能决定;页大大小固定,由系统决定
- 段向用户提供二维地址空间;页向用户提供的是一维地址空间
- 段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。
静态链接的不足:
- 可执行文件增大
- 一旦库被修改整个文件需要重新编译
- 不能共享代码
僵尸进程是等待父进程通过wait方法回收的进程, 而孤儿进程是自己进程结束以前父进程就已经结束了, 孤儿进程依赖root进行回收.
(创建是否需要存在, 元数据是否相同, 删除的影响, 是否可以链接目录)
linux中, 文件包括3个部分, 文件名, innode, block. 文件名和innode是保存在目录的一条目录项中的, 而block是文件数据的存放位置, innode中记录了文件的元数据.
linux中使用对一个文件使用硬链接是对同一个inode的增加了不同文件路径名, 而对一个文件使用软连接则是创建了一个普通文件, 文件的用户数据块的内容是被链接的文件的路径. 二者的区别:
- 硬链接依赖inode, 因此只有文件存在时才可以创建; 软链接就是创建了一个普通文件, 因此可以对并不实际存在的路径创建.
- 硬链接关联的文件具有相同的元数据, 因为它们的inode只有一个; 软链接关联的两个文件的元数据可以不同, 因此可以具有不同的权限
- 删除创建硬链接会修改inode使得硬链接计数改变, 修改软链接不会修改源文件的inode.
- 硬链接不能链接目录, 因为当前目录和父目录两个文件的实现就是硬链接,随意的链接目录会造成目录环; 软链接可以链接目录.