Skip to content

操作系统面试题

更新: 8/23/2025 字数: 0 字 时长: 0 分钟

线程和进程有什么区别?
  • 进程(Process):就像一个独立的工厂。这个工厂有自己的地盘(内存空间),自己的机器设备(资源,比如文件、网络连接),自己的工人(线程),并且它能独立生产产品(执行任务)。一个工厂倒闭了,不会影响其他工厂正常运转。
    • 特点:独立性强,资源开销大,但很安全。
  • 线程(Thread):就像工厂里的工人。一个工厂里可以有很多工人,这些工人共享工厂的设备和材料(共享进程的内存和资源),他们各自负责生产流程中的一个环节,或者一起完成一个大任务。如果一个工人偷懒了,可能会影响整个工厂的生产效率;如果他搞砸了生产线,整个工厂可能都会受影响。
    • 特点:共享资源,开销小,切换快,但相互影响大(一个线程出问题容易影响整个进程)。

打个比方:

你正在用电脑看电影(一个进程),同时打开了 Word 文档在写东西(另一个进程),这两个任务互不干扰。

电影播放器这个“工厂”里,一个“工人”(线程 A)负责解码视频,另一个“工人”(线程 B)负责解码音频,还有一个“工人”(线程 C)负责显示字幕。它们共享电影播放器的内存空间和 CPU 时间,共同完成电影播放这个任务。如果解码视频的线程卡住了,整个电影可能就会卡住。

总结:进程是资源分配的基本单位,线程是 CPU 调度的基本单位。一个进程可以包含多个线程,但一个线程只能属于一个进程。

进程之间的通信方式有哪些?

进程就像不同的工厂,它们各自独立。但有时候,不同的工厂之间需要互相协作,交换信息,比如工厂 A 生产了半成品,需要交给工厂 B 继续加工。那么,它们之间怎么“打电话”或者“寄快递”呢?

这些“打电话”或“寄快递”的方式就是进程间通信(IPC)。

  1. 管道(Pipes):
    • 理解:就像一根水管,数据只能单向流动(或双向但需要两根管子),而且通常只能在有亲缘关系(比如父子进程)的工厂之间使用。
    • 应用:Linux 命令行里经常用 |(管道符),比如 ls -l | grep .txt,就是把 ls -l 的输出作为 grep 的输入。
  2. 消息队列(Message Queues):
    • 理解:就像一个“留言板”或“邮局”,工厂们可以把消息写下来投递进去,其他工厂可以去读取。消息是有序的,而且可以按类型分类。
    • 应用:比如一个日志收集进程把日志信息放到队列,另一个日志分析进程去队列里取。
  3. 共享内存(Shared Memory):
    • 理解:就像划出一块“公共仓库”,所有工厂都可以直接读写这个仓库里的东西。这是最快的方式,因为它省去了数据的复制过程。
    • 应用:数据库服务器和客户端之间可能用共享内存来快速交换数据。
    • 注意:因为大家都能读写,所以需要额外的机制(比如“交通信号灯”——信号量)来避免同时修改导致数据混乱。
  4. 信号量(Semaphores):
    • 理解:就像一个“交通信号灯”或“计数器”,本身不传输数据,而是用来控制多个工厂对共享资源的访问。比如,只有绿灯亮了(信号量允许),工厂才能进入“公共仓库”取货。
    • 应用:常和共享内存一起使用,避免数据竞争。
  5. 信号(Signals):
    • 理解:就像一个“紧急通知”或“中断”,用于通知某个工厂发生了特定事件。比如,工厂着火了(收到终止信号),就得赶紧停工。
    • 应用Ctrl+C 终止程序就是发送一个信号。
  6. 套接字(Sockets):
    • 理解:就像“电话”,可以进行本地通信,也可以跨电脑(网络)通信。是最灵活、应用最广的方式。
    • 应用:浏览器访问网站(HTTP 协议),聊天软件通信等,都是基于套接字。

总结:选哪种方式取决于需求:速度要求高用共享内存,需要远程通信用套接字,简单通知用信号,有序通信用消息队列等。

进程的调度算法你知道吗?

你的电脑 CPU 就像一个“总经理”,而每个进程(工厂)都是一个“项目”。总经理手里有很多项目(进程)等着处理,但他只有一个大脑,不能同时做所有事。那么,他得有个“排班表”或者“工作优先级”来决定先处理哪个项目,后处理哪个,以及每个项目处理多久。这个“排班表”和“优先级”的规则,就是进程调度算法。

调度算法的目标通常是:

  • 公平:尽量让每个进程都有机会执行。
  • 效率:CPU 尽量不空闲。
  • 响应快:用户操作能很快得到反馈。
  • 吞吐量高:单位时间内完成更多任务。

常见的调度算法有:

  1. 先来先服务(FCFS - First-Come, First-Served):
    • 理解:最简单粗暴,谁先提交项目,就先处理谁的。
    • 缺点:如果一个项目耗时特别长,后面的项目就得一直等着,效率低下。就像银行排队,前面有个办大业务的,后面排队的人都得傻等。
  2. 短作业优先(SJF - Shortest Job First):
    • 理解:哪个项目看起来最短,就先处理哪个。
    • 优点:整体效率高,平均等待时间最短。
    • 缺点:很难提前准确知道一个项目到底有多短,而且长作业可能一直得不到执行(“饥饿”)。
  3. 优先级调度(Priority Scheduling):
    • 理解:每个项目都有一个重要程度(优先级),总经理优先处理重要性高的。
    • 缺点:低优先级的项目可能永远得不到执行(“饥饿”),除非有“老化机制”:随着等待时间的增加,优先级逐渐提高。
  4. 时间片轮转(Round Robin - RR):
    • 理解:给每个项目分配一个很短的“时间片”(比如 10 毫秒),时间到了就暂停这个项目,去处理下一个项目,轮流着来。
    • 优点:公平,响应快,适合分时系统(多个用户共享电脑)。
    • 应用:现代操作系统中最常用的算法之一。
  5. 多级反馈队列(Multilevel Feedback Queue):
    • 理解:这是一个更复杂的“组合拳”。它设置多个队列,每个队列有不同的优先级和时间片。新来的项目放在最高优先级队列,如果一个项目用完时间片还没完成,就把它降级到下一个较低优先级队列。如果项目长时间没执行,又可以提升其优先级(“老化”)。
    • 优点:综合了以上算法的优点,既能保证响应速度,又能兼顾长短作业和优先级。
    • 应用:绝大多数现代操作系统(如 Windows、Linux)的调度器都基于这种思想。

总结:调度算法就像 CPU 这个“总经理”在管理他手头一大堆任务的策略。没有哪个算法是万能的,现代操作系统通常会结合多种算法,形成一套复杂的调度策略,以应对不同类型的任务和系统需求。

I/O 模型有哪些?

I/O(Input/Output)就是输入/输出,比如读写文件,或者网络数据的发送和接收。I/O 模型就是应用程序(你的程序)和操作系统(管家)之间,处理数据输入输出时,采取的不同“沟通方式”或“协作模式”。

想象一下你要去饭店吃饭(I/O 操作),有几种不同的模式:

  1. 阻塞 I/O (Blocking I/O):
    • 理解:你点完菜(发起 I/O 请求),就坐在桌子前干等着,菜没上齐你就啥也干不了,只能一直等,等到菜上来了你才能开始吃。
    • 特点:最简单,但效率低,如果一个 I/O 操作耗时,整个程序都会卡住。
    • 应用:大多数传统同步 I/O 操作(如 C 语言的read()write()),在数据未准备好时都会阻塞。
  2. 非阻塞 I/O (Non-blocking I/O):
    • 理解:你点完菜(发起 I/O 请求),服务员说“菜还没好,你先去逛逛吧”。你可以立刻离开,去干别的事情。过一会儿你再来问“菜好了吗?”(轮询)。如果菜好了就吃,没好就再回去逛。
    • 特点:程序不会被阻塞,可以同时处理其他事情。但需要不断地去“问”(轮询),如果 I/O 事件很多,会浪费 CPU 资源。
    • 应用:将 socket 设置为非阻塞模式后进行 I/O 操作。
  3. I/O 多路复用 (I/O Multiplexing):
    • 理解:你有多个朋友(多个文件描述符/socket)都想点菜。你不是一个一个去问他们“菜好了吗?”,而是找一个“总服务员”(select/poll/epoll),你把所有朋友的点菜需求都告诉他,然后你就可以去喝茶了。总服务员会帮你监听所有朋友的菜品状态,一旦有任何一个朋友的菜好了,他就会通知你:“XX 的菜好了!”然后你再去处理那一个已经就绪的菜。
    • 特点:相比非阻塞 I/O,避免了大量的轮询。一个线程可以同时监听多个 I/O 事件。是高性能网络编程的基石。
    • 应用selectpollepollkqueue等。
  4. 信号驱动 I/O (Signal-driven I/O):
    • 理解:你点完菜(注册一个信号处理函数),然后你就直接去睡觉了(干别的事情)。当你的菜好了,饭店会直接给你打个电话(发送一个信号),把你叫醒,然后你再起来去吃。
    • 特点:相比多路复用更省心,不需要自己去监听。但实现复杂,而且信号机制本身有限制。在实际网络编程中不如多路复用常用。
    • 应用:使用SIGIO信号。
  5. 异步 I/O (Asynchronous I/O - AIO):
    • 理解:这是最高级的模式。你点完菜(发起 I/O 请求)后,就彻底不管了,甚至连“总服务员”都不用管。当菜不仅好了,而且饭店都已经帮你把菜送到你面前了(数据已拷贝到你的缓冲区),才会通知你“菜已送到,可以直接开吃!”。你完全不需要参与数据准备和拷贝的过程。
    • 特点:最彻底的非阻塞,程序发起请求后可以完全不用管 I/O 操作,直到数据已经可用。
    • 应用:Windows 的 IOCP 模型,Linux 的aio_read()/aio_write()等。但 Linux 下的异步 I/O 发展相对滞后,应用不如 Windows 广泛,通常所说的异步是指 I/O 多路复用。

总结:随着 I/O 模型的发展,我们希望程序在等待 I/O 数据时能做更多的事情,从而提高系统的并发能力和响应速度。

Select、Poll、Epoll 之间有什么区别?

这三者都是 I/O 多路复用的具体实现,就像都是“总服务员”,但他们的工作方式和效率有所不同。

想象你是一个班主任,要检查班上同学的作业(socket)。

  1. Select:

    • 工作方式:就像你有一个小本子,上面记满了全班同学的名字(文件描述符 fd)。每隔一会儿,你得把整个小本子从头到尾翻一遍(遍历 fd 集合),看看哪些同学的作业写完了。然后,你把写完作业的同学名字标记出来,通知他们。
    • 限制:
      • 本子容量有限:默认能记的名字数量有限(宏 FD_SETSIZE,通常是 1024)。如果班级人数超过这个数,就没法记了。
      • 效率问题:班级人数越多,你翻小本子的时间就越长。而且每次你都要把整个本子(fd 集合)传给校长(内核),校长也要从头到尾检查一遍,效率不高。
      • 每次都重置:每次调用完,你本子上标记的写完作业的同学名字就没了,下次要再检查,得重新从头开始标记。
    • 应用:最老,兼容性最好。
  2. Poll:

    • 工作方式:类似 Select,但你用的是一个更大的活页夹,可以记无限多的同学名字(没有 FD_SETSIZE 限制)。你还是得从头到尾检查这个活页夹,看看哪些同学写完了作业。
    • 限制:
      • 没有容量限制:可以处理更多的文件描述符。
      • 效率问题依旧:随着同学数量增多,你检查活页夹的时间依然会线性增长,校长检查的时间也一样。
    • 应用:比 Select 新一点,解决了 Select 的容量限制问题,但效率提升不明显。
  3. Epoll:

    • 工作方式:这是一个智能管家。你告诉他你要监听哪些同学的作业(注册 fd),然后你就可以去喝茶了。这个管家不会主动去检查每个同学。而是,只有当某个同学的作业写完了(I/O 事件就绪),这个同学会主动通知智能管家。管家会把这些“已就绪”的同学名单放在一个小纸条上。你来问的时候,管家直接把小纸条给你,上面列的就是当前写完作业的同学。
    • 优点:
      • 效率高:不用每次都遍历所有同学,只关心那些“主动汇报”的。同学数量再多,你拿小纸条的时间都是很快的(O(1) 复杂度)。
      • 没有容量限制:理论上能处理的 fd 数量只受内存限制。
      • 事件通知:基于事件通知,而不是轮询。
      • 边缘触发 (ET) 和水平触发 (LT):就像管家可以配置,是只在作业刚写完时通知你一次(ET),还是只要作业没收走,就一直通知你(LT)。ET 模式效率更高,但编程复杂。
    • 应用:Linux 特有,是目前高性能网络服务器(如 Nginx、Redis)最常用的 I/O 多路复用技术。

总结表格:

特性SelectPollEpoll
工作模式轮询(遍历整个 fd 集合)轮询(遍历整个 fd 集合)事件驱动(只返回就绪的 fd)
fd 上限默认 1024 (FD_SETSIZE)理论无上限 (受内存限制)理论无上限 (受内存限制)
效率低 (O(N) 随 fd 数量线性增长)低 (O(N) 随 fd 数量线性增长)高 (O(1) 无论 fd 数量多少,只关心就绪的)
内核/用户态数据拷贝每次调用都拷贝整个 fd 集合每次调用都拷贝整个 fd 集合只拷贝一次监听的 fd,返回时只拷贝就绪的 fd
触发方式水平触发 (LT)水平触发 (LT)水平触发 (LT) 和 边缘触发 (ET)
兼容性跨平台,最老跨平台,比 Select 新Linux 特有
为什么网络 I/O 会被阻塞?

网络 I/O 被阻塞,就像你打电话给一个朋友(发起网络请求)。

1. 建立连接阶段的阻塞(connect()):

  • 你拨通电话(connect()),但朋友的电话可能占线、或者没信号、或者没人接。你就会一直举着手机等着,直到电话通了或者挂断(连接建立成功或失败)。
  • 原因connect() 操作需要经历 TCP 三次握手,这是一个耗时过程。在握手完成之前,系统会一直等待,导致程序阻塞。

2. 数据读写阶段的阻塞(read()/write()):

  • 发送数据(write():你要给朋友发微信消息(write()),但你朋友的网络信号不好,或者微信服务器忙。你的消息发出去后,操作系统(管家)会帮你把消息放到一个发送缓冲区里,然后尝试发出去。如果缓冲区满了,或者网络非常拥堵,消息发不出去,你就会等着,直到消息能发出去或者缓冲区有空位。
    • 原因:TCP 发送缓冲区满、网络拥塞、接收方处理慢等。
  • 接收数据(read():你在等朋友给你发消息(read())。如果朋友没发,或者网络有延迟,你就会干等着,直到有新消息来。
    • 原因:接收缓冲区没有数据、网络延迟、发送方没有发送数据等。

根本原因:

  1. 数据未准备好:当你尝试从网络读取数据时,如果对方还没有发送数据,或者数据还在路上,那么你的程序会一直等待数据到达,直到有数据可读。
  2. 缓冲区已满:当你尝试向网络发送数据时,数据会先放到操作系统内部的发送缓冲区。如果发送速度太快,或者接收方处理太慢导致接收缓冲区慢,发送方的发送缓冲区就会满。此时,你的程序会等待,直到缓冲区有空间再次写入。
  3. 系统调用阻塞:默认情况下,像read()write()connect()这些系统调用是阻塞的。这意味着当它们被调用时,如果它们所依赖的事件(数据到达、缓冲区有空间、连接建立)没有立即发生,调用它们的进程就会被暂停,直到事件发生。

如何避免阻塞?

就是前面提到的 I/O 模型:

  • 非阻塞 I/O:你去问“有数据吗?”。如果没有,立刻告诉你“没有”,你就可以去做别的事,而不是傻等。
  • I/O 多路复用:你找一个“总服务员”(select/poll/epoll),让他帮你监听多个连接。一旦有哪个连接的数据准备好了,他会通知你,你再去处理那个已经就绪的连接,而不是所有都去轮询或等待。
  • 异步 I/O:你把任务交给操作系统,然后完全不管。数据准备好并拷贝到你的内存后,操作系统会通知你。

通过这些方式,我们可以让程序在等待网络 I/O 的时候,不再原地踏步,而是能去处理其他任务,从而提高程序的并发能力。

什么是用户态和内核态?

在操作系统中,CPU 有不同的权限级别,主要分为:

  • 用户态(User Mode):普通应用程序运行的模式,能访问的资源有限,比如只能操作自己的内存空间,不能直接操作硬件或调用底层指令。
  • 内核态(Kernel Mode):操作系统内核运行的模式,权限最高,可以直接操作硬件、管理内存、调度进程。

为什么要分两种态?

出于安全稳定性考虑:

  • 如果所有程序都能直接操作硬盘/内存/网卡,系统很快就会崩溃。
  • 用户态出错不会影响整个系统,而内核态是“上帝视角”,必须严格保护。

切换场景

  • 读写文件:应用程序在用户态调用 read(),操作系统会切换到内核态完成硬盘访问。
  • 网络收发:send/recv 会从用户态进入内核态操作网卡。

所以 用户态 → 内核态 是通过 系统调用(System Call)实现的。

到底什么是 Reactor?

Reactor 模式是一种事件驱动的并发处理模型,常用于高性能网络编程(比如 Netty、Nginx)。

核心思想

一个线程监听事件,来了事件就“分发”给对应的处理器去处理

而不是传统的“一连接一个线程”,避免线程过多导致资源浪费。

结构组成

  1. Reactor(事件分发器):负责监听事件(读/写/连接等),并将事件分发。
  2. Handler(事件处理器):负责具体的业务逻辑处理。

类比生活

  • 餐厅里只有一个服务员(Reactor),他负责盯着大厅,一旦某个顾客举手(事件发生),就把任务交给后厨(Handler)去做。
  • 如果没有 Reactor,可能就是“一桌一个服务员”,成本太高。
为什么要有虚拟内存?

虚拟内存(Virtual Memory)是操作系统用来抽象和管理物理内存的一种机制。

核心目标

  1. 扩展内存:把一部分硬盘空间当成“假的内存”(交换空间 swap),让程序觉得有很大的可用空间。
  2. 隔离性:每个进程都以为自己独占内存,从 0x0000 开始,互不干扰,提升安全性。
  3. 简化编程:程序员不需要关心物理内存在哪里,只管用虚拟地址。
  4. 内存保护:一个进程不能随便访问另一个进程的内存。

工作原理

  • CPU 访问的地址是 虚拟地址
  • 操作系统通过 页表(Page Table) 把虚拟地址映射到 物理地址
  • 如果物理内存不够,就把一部分数据换到硬盘。

好处

  • 程序不用担心内存碎片或地址冲突。
  • 系统运行多个大程序时,也能“假装”有足够内存。
说下你常用的 Linux 命令?

文件和目录管理:

  • ls:列出目录内容 (ls -l, ls -a, ls -lh)
  • cd:切换目录 (cd /var/log, cd .., cd ~)
  • pwd:显示当前工作目录
  • mkdir:创建目录 (mkdir my_project, mkdir -p /path/to/new/dir)
  • rmdir:删除空目录
  • cp:复制文件或目录 (cp file1 file2, cp -r dir1 dir2)
  • mv:移动或重命名文件/目录 (mv oldname newname, mv file1 /path/to/dir)
  • rm:删除文件或目录 (rm file.txt, rm -rf my_dir) 慎用 rm -rf
  • find:在文件系统中查找文件 (find . -name "*.log", find / -type f -size +1G)
  • locate:快速查找文件 (基于数据库,需要 updatedb)

查看文件内容:

  • cat:查看文件全部内容 (cat /etc/passwd)
  • less:分页查看文件内容 (支持搜索和滚动,更适合大文件)
  • more:类似于 less,但功能较少
  • head:查看文件开头几行 (head -n 10 file.txt)
  • tail:查看文件末尾几行 (tail -n 10 file.txt, tail -f /var/log/syslog 用于实时监控日志)
  • grep:在文件中搜索文本模式 (grep "error" /var/log/messages, ls -l | grep ".conf")

系统信息和进程管理:

  • ps:显示当前进程快照 (ps aux, ps -ef)
  • top:实时查看系统进程、CPU、内存使用情况
  • htop:类似于 top,但提供更友好的交互界面和更多功能
  • kill:终止进程 (kill PID, kill -9 PID 强制终止)
  • free:显示内存使用情况
  • df:显示磁盘空间使用情况 (df -h)
  • du:显示文件或目录的磁盘使用量 (du -sh /var/log)
  • uname:显示系统信息 (uname -a)
  • uptime:显示系统运行时间、用户数和平均负载
  • history:显示历史命令
  • sudo:以超级用户权限执行命令

网络工具:

  • ping:测试网络连通性
  • ip addrifconfig (旧版): 显示网络接口信息
  • netstat (旧版) 或 ss (新版): 显示网络连接、路由表等
  • ssh:远程登录到其他服务器
  • scp:安全复制文件到远程服务器
  • wgetcurl:下载文件或与 Web 服务交互

文件权限和所有权:

  • chmod:修改文件或目录权限 (chmod 755 script.sh)
  • chown:修改文件或目录所有者 (chown user:group file.txt)

压缩与解压:

  • tar:打包和解包文件 (tar -czvf archive.tar.gz dir, tar -xzvf archive.tar.gz)
  • gzip, gunzip:压缩和解压文件
  • zip, unzip:压缩和解压文件
什么是分段、什么是分页?

分段和分页是两种主要的内存管理技术,用于将程序的逻辑地址空间映射到物理内存地址空间。它们的目的是为了解决内存碎片问题,并提供多任务环境下的内存保护和共享。

  • 分段(Segmentation)

    • 按照程序的逻辑结构划分内存,比如代码段、数据段、堆、栈。
    • 每个段的长度不固定,按需分配。
    • 优点:符合程序员的逻辑思维,便于保护和共享(例如多个程序共享同一代码段)。
    • 缺点:容易产生外部碎片(段和段之间可能空闲但不连续)。
  • 分页(Paging)

    • 把内存和进程空间都划分为大小相同的固定块(页 / 页框,一般 4KB)。
    • 地址 = 页号 + 页内偏移,通过页表映射。
    • 优点:避免外部碎片,方便虚拟内存实现。
    • 缺点:需要维护页表,会有查表开销;可能产生内部碎片(一个页用不满浪费)。
  • 区别总结

    • 分段:逻辑上划分,不同段大小不一 → 外部碎片问题。
    • 分页:物理上划分,固定大小 → 内部碎片问题。
    • 实际操作系统常常段页结合(先分段,每段再分页)。
什么是软中断、什么是硬中断?

中断是操作系统处理外部事件和设备请求的核心机制。它允许 CPU 暂停当前任务,转而去处理更紧急或重要的事件,处理完毕后再返回原任务。中断可以分为硬中断和软中断。

  • 硬中断(Hardware Interrupt)

    • 外部硬件设备 触发,比如键盘输入、网卡收到数据、磁盘 IO 完成。
    • 异步发生,CPU 必须及时响应。
    • 硬件通过中断控制器向 CPU 发中断信号。
  • 软中断(Software Interrupt)

    • 程序主动触发,通过执行特定指令(如 x86 的 int 指令)或系统调用产生。
    • 常见于用户态调用内核服务,比如 Linux 的 syscall
    • Linux 内核里还有“软中断”的特殊机制,用于延迟处理一些网络/IO 请求,避免在硬中断里做耗时操作。
  • 对比总结

    • 硬中断:外设触发,异步。
    • 软中断:软件指令触发,主动。
    • 硬中断通常优先级更高。

贡献者

The avatar of contributor named as LI SIR LI SIR

页面历史