6、其他拓展

厨子大约 8 分钟操作系统面试题原创技术拓展程序员系统架构前沿技术

其它补充

Linux 的同步机制

🌟I/O 多路复用

  • 边缘触发
  • 水平触发

🌟大端序小端序

🌟零拷贝

BIO(同步阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)

  • BIO 堵塞 IO:发现被使用,则一直等待
  • NIO:发现被使用,先去干别的,然后每隔一段时间再回来,然后看看用完没。
  • AIO:发现被使用,先去干别的,等结束了我通知你,你再回来

并发和并行的区别

  • 并发(concurrency):指宏观上看起来两个程序在同时运行,比如说在单核 cpu 上的多任务。但是从微观上看两个程序的指令是交织着运行的,指令之间交错执行,在单个周期内只运行了一个指令。这种并发并不能提高计算机的性能,只能提高效率(如降低某个进程的相应时间)。
  • 并行(parallelism):指严格物理意义上的同时运行,比如多核 cpu,两个程序分别运行在两个核上,两者之间互不影响,单个周期内每个程序都运行了自己的指令,也就是运行了两条指令。这样说来并行的确提高了计算机的效率。所以现在的 cpu 都是往多核方面发展。

什么是信号

一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。 Linux 系统上支持的 30 种不同类型的信号。 每种信号类型都对应于某种系统事件。低层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程而言是不可见的。信号提供了一种机制,通知用户进程发生了这些异常。

  1. 发送信号:内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程。发送信号可以有如下两种原因:

    • 内核检测到一个系统事件,比如除零错误或者子进程终止。
    • —个进程调用了 kill 函数, 显式地要求内核发送一个信号给目的进程。一个进程可以发送信号给它自己。
  2. 接收信号:当目的进程被内核强迫以某种方式对信号的发送做出反应时,它就接收了信号。进程可以忽略这个信号,终止或者通过执行一个称为信号处理程序(signal handler)的用户层函数捕获这个信号。

操作系统中用到了什么排序算法

进程调度:堆排序

文件系统排序:快速排序/归并排序

I/O 调度算法:插入排序/归并排序

内存管理:基数排序(变种)

网络协议栈:计数排序应用

了解的 I/O 模型有哪些

I/O 模型
阻塞?
同步?
核心机制
适用场景
阻塞 I/O

同步
线程全程等待
简单低并发程序
非阻塞 I/O

同步
线程轮询检查
需兼顾其他任务的程序
I/O 多路复用
是*
同步
`select`/`epoll` 监听
高并发网络服务

注:I/O 多路复用中,select/epoll 调用本身是阻塞的,但可监听多个 I/O。

  • 阻塞 I/O

    • 调用 I/O 操作时,线程一直等待,直到数据就绪或操作完成。
    • 期间线程无法执行其他任务(CPU 闲置)。
  • 非阻塞 I/O

    • 调用 I/O 操作时,若数据未就绪,​立即返回错误​(如 EWOULDBLOCK)。
    • 线程需轮询检查数据是否就绪(消耗 CPU)。
    • 线程可执行其他任务(但需主动轮询)。
  • I/O 多路复用

    • 使用 select/poll/epoll 等系统调用,​单线程监听多个 I/O 事件。
    • 当某个 I/O 就绪时,通知线程处理。

常见的 I/O 多路复用机制

select

  • 跨平台:支持所有主流操作系统(Linux/Windows/macOS)。

  • 基于轮询:通过遍历文件描述符集合(fd_set)检查就绪状态。

  • 限制

    • 单个进程最多监听 1024 个文件描述符(FD)。
    • 每次调用需全量拷贝 fd_set 到内核。
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd1, &read_fds);  _// 添加监听 fd_
int ret = select(fd1 + 1, &read_fds, NULL, NULL, NULL);  _// 阻塞等待_
if (FD_ISSET(fd1, &read_fds)) { _/* 处理就绪的 fd */_ }
  • 缺点
    • O(n) 时间复杂度:每次遍历所有 FD,性能随 FD 数量线性下降。
    • 重复初始化:每次调用需重新设置 fd_set

poll

  • 改进 select 的 FD 数量限制,使用链表存储 FD(理论无上限)。
  • 仍需要遍历所有 FD 检查就绪状态(O(n) 时间复杂度)。
struct pollfd fds[2];
fds[0].fd = fd1; fds[0].events = POLLIN;
int ret = poll(fds, 2, timeout);  _// 阻塞等待_
if (fds[0].revents & POLLIN) { _/* 处理就绪的 fd */_ }

**对比 **select

特性
select
poll
FD 数量
1024(硬编码)
无限制(受内存约束)
性能
O(n)
O(n)
编程复杂度
需手动管理 fd_set
结构体更清晰

epoll(Linux 专属)​

  • 事件驱动:内核通过回调机制直接通知就绪的 FD,无需遍历(O(1) 时间复杂度)。
  • 高效内存:使用红黑树管理 FD,支持水平触发(LT)​边缘触发(ET)​模式。

核心函数

  • epoll_create():创建 epoll 实例。
  • epoll_ctl():添加/修改/删除监听的 FD。
  • epoll_wait():等待就绪事件。
int epfd = epoll_create1(0);
struct epoll_event ev, events[10];
ev.events = EPOLLIN; ev.data.fd = fd1;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &ev);  _// 注册 fd_

int ret = epoll_wait(epfd, events, 10, timeout);  _// 等待事件_

for (int i = 0; i < ret; i++) { _/* 处理 events[i] */_ }

触发模式

  • 水平触发:只要 FD 可读/可写,epoll_wait() 会持续通知(默认模式,类似 poll)。
  • 边沿触发:仅在 FD 状态变化时通知一次(需一次性处理完数据,否则可能丢失事件)。

优点

  • 高性能:支持百万级并发(如 Nginx、Redis)。
  • 低开销:无需每次调用传递所有 FD。

kqueue(FreeBSD/macOS 专属)​

  • 类似 epoll,但使用事件队列机制,支持更多事件类型(如文件修改、信号)。
  • 代码复杂度较高,但功能更强大。
struct kevent ev, events[10];
int kq = kqueue();
EV_SET(&ev, fd1, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &ev, 1, NULL, 0, NULL);  _// 注册 fd_
int ret = kevent(kq, NULL, 0, events, 10, &timeout);  _// 等待事件_

对比 epoll

特性
epoll
kqueue
平台
Linux
FreeBSD/macOS
事件类型
网络 I/O 为主
支持文件、信号等
性能
O(1)
O(1)

IOCP(Windows 专属)

  • 基于异步 I/O 的多路复用模型,与 epoll/kqueue 设计哲学不同。
  • 需配合完成端口(Completion Port)​使用,适合高吞吐场景。
  • 线程池从完成端口获取已完成的 I/O 操作,而非主动监听 FD。

总结对比

机制
平台支持
时间复杂度
最大并发量
编程复杂度
`select`
跨平台
O(n)
1024

`poll`
跨平台
O(n)
无硬限制

`epoll`
Linux
O(1)
百万级

`kqueue`
FreeBSD/macOS
O(1)
百万级

`IOCP`
Windows
O(1)
百万级
极高