操作系统面试知识点

来自公众号:Java面试那点事

1. 什么是僵尸进程? 什么是孤儿进程? 有什么危害?

僵尸进程:

  • 一个进程使用 fork 创建子进程, 如果子进程退出, 而父进程并没有调用 wait 或者
    waitpid 获取子进程的状态信息, 那么子进程的进程描述符等一系列信息还会保存在系
    统中。 这种进程称之为僵尸进程。
  • “僵尸” 进程是一个早已死亡的进程, 但在进程表( processs table) 中仍占了一个位置
    (slot) 。 由于进程表的容量是有限的, 所以, defunct 进程不仅占用系统的内存资源,
    影响系统的性能, 而且如果其数目太多, 还会导致系统瘫痪。

处理僵尸进程:

  • 改写父进程, 在子进程死后要为它收尸。 具体做法是接管 SIGCHLD 信号。 子进程死后,
    会发送 SIGCHLD 信号给父进程, 父进程收到此信号后, 执行 waitpid () 函数为子进程
    收尸。 这是基于这样的原理: 就算父进程没有调用 wait, 内核也会向它发送 SIGCHLD 消
    息, 尽管默认处理是忽略, 如果想响应这个消息, 可以设置一个处理函数。
  • 把父进程杀掉。 父进程死后, 僵尸进程成为” 孤儿进程”, 过继给 1 号进程 init, init 始
    终会负责清理僵尸进程. 它产生的所有僵尸进程也跟着消失。

孤儿进程:

  • 父进程运行结束, 但子进程还在运行 (未运行结束) 的子进程就称为孤儿进程。
  • 孤儿进程最终会被 init 进程 (进程号为 1) 所收养, 因此 init 进程此时变成孤儿进程
    的父进程, 并由 init 进程对它们完成状态收集工作。

2. CPU 的上下文切换有几种? 系统中断进行了几次上下文切换?

上下文切换 (Context Switch) 是一种将 CPU 资源从一个进程分配给另一个进程的机制。 操
作系统需要先存储当前进程的状态 (包括内存空间的指针, 当前执行完的指令等等), 再读
入下一个进程的状态, 然后执行此进程。

CPU 的上下文切换分三种: 进程上下文切换、 线程上下文切换、 中断上下文切换。

  • 系统调用过程中也会发生 CPU 上下文切换。 CPU 寄存器会先保存用户态的状态, 然后
    加载内核态相关内容。 系统调用结束之后, CPU 寄存器要恢复原来保存的用户态, 继
    续运行进程。 所以, 一次系统调用, 发生两次 CPU 上下文切换。
  • 进程是由内核管理和调度的, 进程的切换只能发生在内核态。 进程上下文切换与系统调
    用的不同在于, 进程的调用会保存用户空间的虚拟内存, 全局变量等信息, 但是系统调
    用的上下文则不会, 因为其未发生进程的变化。
  • 内核中的任务调度实际是在调度线程, 进程只是给线程提供虚拟内存、 全局变量等资源。
    线程上下文切换时, 共享相同的虚拟内存和全局变量等资源不需要修改。 而线程自己的
    私有数据, 如栈和寄存器等, 上下文切换时需要保存。

3. 进程的通信方式? 效率最高的通信方式是什么?

  • 管道: 管道是单向的、 先进先出的、 无结构的、 固定大小的字节流, 它把一个进程的标
    准输出和另一个进程的标准输入连接在一起。 写进程在管道的尾端写入数据, 读进程在
    管道的道端读出数据。
  • 信号量: 信号量是一个计数器, 可以用来控制多个进程对共享资源的访问。 它常作为一
    种锁机制, 防止某进程正在访问共享资源时, 其它进程也访问该资源。 因此, 主要作为
    进程间以及同一进程内不同线程之间的同步手段。
  • 消息队列: 是一个在系统内核中用来保存消息的队列, 它在系统内核中是以消息链表的
    形式出现的。 消息队列克服了信号传递信息少、 管道只能承载无格式字节流以及缓冲区
    大小受限等缺点。
  • 共享内存: 共享内存允许两个或多个进程访问同一个逻辑内存。 这一段内存可以被两个
    或两个以上的进程映射至自身的地址空间中, 一个进程写入共享内存的信息, 可以被其
    他使用这个共享内存的进程, 通过一个简单的内存读取读出, 从而实现了进程间的通信。
    如果某个进程向共享内存写入数据, 所做的改动将立即影响到可以访问同一段共享内存
    的任何其他进程。 共享内存是最快的 IPC 方式。
  • 套接字: 套接字也是一种进程间通信机制, 与其它通信机制不同的是, 它可用于不同机
    器间的进程通信。

4. 进程调度算法有几种? 应用最广泛的是什么?

FIFO 或 First Come, First Served (FCFS) 先来先服务

  • 调度的顺序就是任务到达就绪队列的顺序。
  • 公平、 简单 (FIFO 队列)、 非抢占、 不适合交互式。
  • 未考虑任务特性, 平均等待时间可以缩短。

Shortest Job First (SJF) 最短作业优先

  • 最短的作业 (CPU 区间长度最小) 最先调度。
  • 由于作业的长短只是根据用户所提供的估计执行时间而定的, 而用户又可能会有意或无
    意地缩短其作业的估计运行时间, 致使该算法不一定能真正做到短作业优先调度。
  • SJF 可以保证最小的平均等待时间。

Shortest Remaining Job First (SRJF) 最短剩余作业优先

  • SJF 的可抢占版本, 比 SJF 更有优势。
  • SJF (SRJF): 如何知道下一 CPU 区间大小? 根据历史进行预测: 指数平均法。

优先权调度

  • 每个任务关联一个优先权, 调度优先权最高的任务。
  • 优先权太低的任务一直就绪, 得不到运行, 出现 “饥饿” 现象。

Round-Robin (RR) 轮转调度算法

  • 设置一个时间片, 按时间片来轮转调度(“轮叫” 算法)
  • 优点: 定时有响应, 等待时间较短;
  • 缺点: 上下文切换次数较多;
  • 时间片太大, 响应时间太长; 吞吐量变小, 周转时间变长; 当时间片过长时, 退化为 FCFS。

多级队列调度

  • 按照一定的规则建立多个进程队列
  • 不同的队列有固定的优先级(高优先级有抢占权)
  • 不同的队列可以给不同的时间片和采用不同的调度方法
  • 存在一定程度的 “饥饿” 现象;

多级反馈队列

  • 在多级队列的基础上, 任务可以在队列之间移动, 更细致的区分任务。
  • 可以根据 “享用” CPU 时间多少来移动队列, 阻止 “饥饿”。
  • 最通用的调度算法, 多数 OS 都使用该方法或其变形, 如 UNIX、 Windows 等。

多级反馈队列算法详解:

  • 进程在进入待调度的队列等待时, 首先进入优先级最高的 Q1 等待。
  • 首先调度优先级高的队列中的进程。 若高优先级中队列中已没有调度的进程, 则调度次
    优先级队列中的进程。 例如: Q1,Q2,Q3 三个队列, 只有在 Q1 中没有进程等待时才去
    调度 Q2, 同理, 只有 Q1,Q2 都为空时才会去调度 Q3。
  • 对于同一个队列中的各个进程, 按照时间片轮转法调度。 比如 Q1 队列的时间片为 N,
    那么 Q1 中的作业在经历了 N 个时间片后若还没有完成, 则进入 Q2 队列等待, 若
    Q2 的时间片用完后作业还不能完成, 一直进入下一级队列, 直至完成。
  • 在低优先级的队列中的进程在运行时, 又有新到达的作业, 那么在运行完这个时间片后,
    CPU 马上分配给新到达的作业(抢占式) 。

5. 进程和线程的区别?

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动, 进程是系统进行资源
分配和调度的一个独立单位。

线程是进程的一个实体, 是 CPU 调度和分派的基本单位, 它是比进程更小的能独立运行的
基本单位。

区别:

  • 进程有自己的独立地址空间, 线程没有
  • 进程是资源分配的最小单位, 线程是 CPU 调度的最小单位。
  • 进程和线程通信方式不同 (线程之间的通信比较方便。 同一进程下的线程共享数据, 比
    如全局变量, 静态变量, 通过这些数据来通信不仅快捷而且方便, 当然如何处理好这些
    访问的同步与互斥正是编写多线程程序的难点。 而进程之间的通信只能通过进程通信的
    方式进行。 )
  • 进程上下文切换开销大, 线程开销小; 对进程进程操作一般开销都比较大, 对线程开销
    就小了
  • 一个进程挂掉了不会影响其他进程, 而线程挂掉了会影响其他线程。

6. 虚拟内存的作用与特性?

具有请求调入功能和置换功能, 能从逻辑上对内存容量加以扩充得一种存储器系统。 其逻辑
容量由内存之和和外存之和决定。

  • 多次性, 是指无需在作业运行时一次性地全部装入内存, 而是允许被分成多次调入内存
    运行。
  • 对换性, 是指无需在作业运行时一直常驻内存, 而是允许在作业的运行过程中, 进行换
    进和换出。
  • 虚拟性, 是指从逻辑上扩充内存的容量, 使用户所看到的内存容量, 远大于实际的内存
    容量。

当每个进程创建的时候, 内核会为进程分配 4G 的虚拟内存, 当进程还没有开始运行时,
这只是一个内存布局。 实际上并不立即就把虚拟内存对应位置的程序数据和代码( 比
如.text .data 段) 拷贝到物理内存中, 只是建立好虚拟内存和磁盘文件之间的映射就好(叫
做存储器映射) 。 这个时候数据和代码还是在磁盘上的。 当运行到对应的程序时, 进程去寻
找页表, 发现页表中地址没有存放在物理内存上, 而是在磁盘上, 于是发生缺页异常, 于是
将磁盘上的数据拷贝到物理内存中。

7. 虚拟内存的实现方式? 分别有何种缺陷?

方式:

  • 请求分页存储管理。 将虚拟内存空间和物理内存空间皆划分为大小相同的页面, 如
    4KB、 8KB 或 16KB 等, 并以页面作为内存空间的最小分配单位, 一个程序的一个页面
    可以存放在任意一个物理页面里。 页是信息的物理单位
  • 请求分段存储管理。 将用户程序地址空间分成若干个大小不等的段, 每段可以定义一组
    相对完整的逻辑信息。 段是信息的逻辑单位

分页存储的缺点: 产生内部碎片

分段存储的缺点: 产生外部碎片

采用段页式管理就是将程序分为多个逻辑段, 在每个段里面又进行分页, 即将分段和分页组
合起来使用。 这样做的目的就是想同时获得分段和分页的好处, 但又避免了单独分段或单独
分页的缺陷。

8. 页面置换算法?

  • 最优页面置换算法(往后看) : 最理想的状态下, 我们给页面做个标记, 挑选一个最远
    才会被再次用到的页面调出
  • 先进先出页面置换算法(FIFO) 及其改进(往前看) : 这种算法的思想和队列是一样的,
    该算法总是淘汰最先进入内存的页面, 即选择在内存中驻留时间最久的页面予淘汰。
  • 最近最少使用页面置换算法 LRU( 往前看) : 总是选择在最近一段时间内最久不用的
    页面予以淘汰。 即淘汰最近最长时间未访问过的页面。

9. 什么是中断? 产生中断的方式?

所谓的中断就是在计算机执行程序的过程中, 由于出现了某些特殊事情, 使得 CPU 暂停对
程序的执行, 转而去执行处理这一事件的程序。 等这些特殊事情处理完之后再回去执行之前
的程序。 中断(广义) 是激活操作系统的唯一方式。

产生方式:

  • 由计算机硬件异常或故障引起的中断, 称为内部异常中断;
  • 由程序中执行了引起中断的指令而造成的中断, 称为软中断(这也是和我们将要说明的
    系统调用相关的中断) ;
  • 由外部设备请求引起的中断, 称为外部中断。

10. 什么是系统调用?

  • 操作系统提供的函数就被称为系统调用(system call) 。 程序的执行一般是在用户态下
    执行的, 但当程序需要使用操作系统提供的服务时, 比如说打开某一设备、 创建文件、
    读写文件(这些均属于系统调用) 等, 就需要向操作系统发出调用服务的请求, 这就是
    系统调用。

11. 什么会导致用户态陷入内核态?

  • 系统调用: 操作系统提供的函数就被称为系统调用(system call) 。
  • 异常: 当 CPU 在执行运行在用户态下的程序时, 发生了某些事先不可知的异常, 这时
    会触发由当前运行进程切换到处理此异常的内核相关程序中, 也就转到了内核态, 比如
    缺页异常。
  • 外围设备的中断: 当外围设备完成用户请求的操作后, 会向 CPU 发出相应的中断信号,
    这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程
    序, 如果先前执行的指令是用户态下的程序, 那么这个转换的过程自然也就发生了由用
    户态到内核态的切换。 比如硬盘读写操作完成, 系统会切换到硬盘读写的中断处理程序
    中执行后续操作等。

12. 陷阱和中断的区别?

  • 陷阱指令可以使执行流程从用户态陷入内核并把控制权转移给操作系统, 使得用户程序
    可以调用内核函数和使用硬件从而获得操作系统所提供的服务, 比如用视频软件放一个
    电影, 视频软件就发出陷阱使用显示器和声卡从而访问硬件。
  • 中断是由外部事件导致并且它发生的时间是不可预测的, 这一点和陷阱不同。 外部事件
    主要是指时钟中断, 硬件中断等。 CPU 决定切换到另一个进程运行, 就会产生一个时
    钟中断, 切换到下一个进程运行。

13. 互斥和同步的关系?

  • 互斥某一资源同时只允许一个访问者对其进行访问, 具有唯一性和排它性。 但互斥无法
    限制访问者对资源的访问顺序, 即访问是无序的。
  • 同步是指在互斥的基础上(大多数情况) , 通过其它机制实现访问者对资源的有序访问。
    在大多数情况下, 同步已经实现了互斥, 特别是所有写入资源的情况必定是互斥的。 少
    数情况是指可以允许多个访问者同时访问资源。

实现同步和互斥的方式:

  • 临界区: 通过对多线程的串行化来访问公共资源或一段代码, 速度快, 适合控制数据访
    问。
  • 互斥量: 为协调共同对一个共享资源的单独访问而设计的。
  • 信号量: 为控制一个具有有限数量用户资源而设计。
  • 事 件: 用来通知线程有一些事件已发生, 从而启动后继任务的开始。

14. 死锁产生的条件?

  • 互斥条件 (Mutual exclusion): 资源不能被共享, 只能由一个进程使用。
  • 请求与保持条件 (Hold and wait): 已经得到资源的进程可以再次申请新的资源。
  • 非抢占条件 (No pre-emption): 已经分配的资源不能从相应的进程中被强制地剥夺。
  • 循环等待条件 (Circular wait): 系统中若干进程组成环路, 该环路中每个进程都在等待
    相邻进程正占用的资源。

15. 信号量的 PV 实现?

struct semaphore {
int count;
queueType queue;
};
// P 操作, 进程请求分配一个资源
void semWait(semaphore s){
s.count--;
if(s.count < 0){
/*把当前进程插入队列*/
/*阻塞当前进程*/
}
} /
/ V 操作, 进程释放一个资源
void semSignal(semaphore s){
s.count++;
if(s.count <= 0){
/*把进程 p 从队列中移除*/
/*把进程 p 插入就绪队列*/
}
} 

总结: V 原语操作的本质在于: 一个进程使用完临界资源后, 释放临界资源, 使 S 加 1,
以通知其它的进程, 这个时候如果 S<=0, 表明有进程阻塞在该类资源上, 因此要从阻塞队
列里唤醒一个进程来 “转手” 该类资源; 如果资源数 count>0, 代表阻塞队列没有进程阻塞,
就不需要唤醒操作。

16. 生产者消费者的代码实现?

semaphore s = 1; // 临界区互斥信号量
semaphore empty = n; // 空闲缓冲区
semaphore full = 0; // 满缓冲区
void producter(){
while(true){
semWait(empty);
semWait(s);
/*把数据送到缓冲区*/
semSignal(s);
semSignal(full);
}
} v
oid consumer(){
while(true){
semWait(full);
semWait(s);
/*从缓冲区取出数据*/
semSignal(s);
semSignal(empty);
}
} 

17. 如何动态查看服务器日志文件?

  • 命令 tail -f 文件 可以对某个文件进行动态监控, 例如 tomcat 的日志文件, 会随着程
    序的运行, 日志会变化, 可以使用 tail -f catalina-2016-11-11.log 监控 文 件的变化

18. 如何打包压缩文件?

Linux 中的打包文件一般是以.tar 结尾的, 压缩的命令一般是以.gz 结尾的。 而一般情况下
打包和压缩是一起进行的, 打包并压缩后的文件的后缀名一般.tar.gz。

命令: tar -zcvf 打包压缩后的文件名 要打包压缩的文件

  • z: 调用 gzip 压缩命令进行压缩
  • c: 打包文件
  • v: 显示运行过程
  • f: 指定文件名

解压压缩包: 命令: tar [-xvf] 压缩文件, 其中: x: 代表解压

19. 修改 /test 下的 aaa.txt 的权限为属主有全部权限, 属主所在的组有读写权限, 其他用户只有读的权限?

  • 修改文件 / 目录的权限的命令: chmod
  • 修改 /test 下的 aaa.txt 的权限为属主有全部权限, 属主所在的组有读写权限, 其他
    用户只有读的权限: chmod u=rwx,g=rw,o=r aaa.txt

20. 查找 text.txt 文件中的 abc 的位置?

  • grep 要搜索的字符串 要搜索的文件 --color: 搜索命令, –color 代表高亮显示
  • grep “abc” text.txt

21. 在 /home 目录下查找以.txt 结尾的文件名?

  • find /home -name “*.txt”

22. 普通文件 IO 页缓存需要复制几次? 具体过程是什么?

  • Linux 的缓存 IO 机制中, 操作系统会将 IO 的数据缓存在文件系统的页缓存中, 也就
    是说, 数据会先被拷贝到操作系统内核的缓冲区, 然后才会从操作系统内核的缓冲区拷
    贝到应用程序的地址空间。
  • 所有的文件内容的读取(无论一开始是命中页缓存还是没有命中页缓存) 最终都是直接
    来源于页缓存。 当将数据从磁盘复制到页缓存之后, 还要将页缓存的数据通过 CPU 复
    制到 read 调用提供的缓冲区中, 这就是普通文件 IO 需要的两次复制数据复制过程。
    其中第一次是通过 DMA 的方式将数据从磁盘复制到页缓存中; 第二次是将数据从页缓
    存复制到进程自己的的地址空间对应的物理内存中。

23. 常见的 IO 模型?

  • 同步阻塞 IO(blocking IO) : 当用户进程调用了 recvfrom 这个系统调用, kernel 就开
    始了 IO 的第一个阶段: 准备数据, 对于网络 IO 来说, 很多时候数据在一开始还没有
    到达。 这个过程需要等待, 也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个
    过程的。 而在用户进程这边, 整个进程会被阻塞。
  • 同步非阻塞 IO(nonblocking IO) 当用户进程发出 read 操作时, 如果 kernel 中的数据
    还没有准备好, 那么它并不会 block 用户进程, 而是立刻返回一个 error。 从用户进程
    角度讲 , 它发起一个 read 操作后, 并不需要等待, 而是马上就得到了一个结果。 用
    户进程判断结果是一个 error 时, 它就知道数据还没有准备好, 于是它可以再次发送
    read 操作。 一旦 kernel 中的数据准备好了, 并且又再次收到了用户进程的 system call,
    那么它马上就将数据拷贝到了用户内存, 然后返回。
  • IO 多路复用(IO multiplexing) I/O 多路复用就是通过一种机制, 一个进程可以监视多
    个描述符, 一旦某个描述符就绪(一般是读就绪或者写就绪) , 能够通知程序进行相应
    的读写操作。 可达到在同一个线程内同时处理多个 IO 请求的目的。 select, poll, epoll
    都是 IO 多路复用的机制。 但 select, poll, epoll 本质上都是同步 I/O, 因为他们都需
    要在读写事件就绪后自己负责进行读写, 也就是说这个读写过程是同步阻塞的。
  • 异步 IO (asynchronous IO) 用户进程发起 read 操作之后, 立刻就可以开始去做其它
    的事。 而另一方面, 从 kernel 的角度, 当它受到一个 asynchronous read 之后, 首先
    它会立刻返回, 所以不会对用户进程产生任何 block。 然后, kernel 会等待数据准备完
    成, 然后将数据拷贝到用户内存, 当这一切都完成之后, kernel 会给用户进程发送一个
    signal, 告诉它 read 操作完成了。

24. IO 多路复用的 select、 poll 和 epoll 函数的区别?

  • select 函数监视文件描述符, 调用后 select 函数会阻塞, 直到有描述符就绪, 或者超
    时, 函数返回, 当 select 函数返回后, 就可以遍历描述符, 找到就绪的描述符。 select
    的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制
  • 没有最大限制(但是数量过大后性能也是会下降) 。 和 select 函数一样, poll 返回后,
    需要轮询来获取就绪的描述符。
  • epoll 是 Linux 内核为处理大批量文件描述符而作了改进的 poll, 是 Linux 下多路复用
    IO 接口 select/poll 的增强版本, 它能显著提高程序在大量并发连接中只有少量活跃的
    情况下的系统 CPU 利用率。 另一点原因就是获取事件的时候, 它无须遍历整个被侦听
    的描述符集, 只要遍历那些被内核 IO 事件异步唤醒而加入 Ready 队列的描述符集合
    就行了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值