从零起步学习计算机操作系统:进程篇(知识扩展提升)

从零起步学习计算机操作系统:进程篇(知识扩展提升) Q1:进程间的通信方式有哪些A1管道:分为有名管道和匿名管道匿名管道顾名思义它没有名字标识匿名管道是特殊文件只存在于内存没有存在于文件系统中shell命令中的 “ | ” 竖线就是匿名管道通信的数据是无格式的流并且大小受限通信的方式是单向的数据只能在一个方向上流动如果要双向通信需要创建两个管道再来匿名管道是只能用于存在父子关系的进程间通信匿名管道的生命周期随着进程创建而建立随着进程终止而消失。命名管道突破了匿名管道只能在亲缘关系进程间的通信限制因为使用命名管通的前提需要在文件系终创建一个类型为p的设备文件那么毫无关系的进程就可以通过这个设备文件进行通信。另外不管是匿名管道还是命名管道进程写入的数据都是缓存在内核中另一个进程读取数据时候自然也是从内核中获取同时通信数据都遵循先进先出原则不支持lseek之类的文件定位操作。消息队列:消息队列克服了管道通信的数据是无格式的节流的问题消息队列实际上是保存在内核的「消息链表」消息队列的消息体是可以用户自定义的数据类型发送数据时会被分成一个一个独立的消息体当然接收数据时也要与发送方发送的消息体的数据关型保持一致这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的毕竟每次数据的写入和读取都需要经过用户态与内核态之同的拷贝过程。共享内存:共享内存可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销它直接分配一个共享空间每个进程都可以直接访问就像访问进程自己的空间一样快捷方便不需要陷入内核态或者系统调用大大提高了通信的速度享有最快的进程间通信方式之名。但是便捷高效的共享内存通信带来新的问题多进程竞争同个共享资源会造成数据的错乱。信号量:信号量可以保护共享资源以确保任何时刻只能有一个进程访问共享资源这种方式就是互斥访问。信号量不仅可以实现访问的互斥性还可以实现进程间的同步信号量其实是一个计数器表示的是资源个数其值可以通过两个原子操作来控制分别是P操作和V操作。信号:与信号量名字很相似的叫信号它两名字虽然相似但功能一点儿都不一样。信号是异步通信机制信号可以在应用进程和内核之间直接交互内核也可以利用信号来通如用户空间的进程发生了哪些系统事件信号事件的来源主要有硬件来源(键盘CltrC)和软件来源(如 kill 命令)一旦有信号发生进程有三种方式响应信号1.执行默认操作、2.捕捉信号、3.忽略信号。有两个信号是应用进程无法捕捉和忽略的即SIGKILL和SIGSTOP这是为了方便我们能在任何时候结束或停止某个进程。Q2:讲解一下互斥与同步A2互斥Mutual Exclusion定义在同一时刻只允许一个线程/进程访问共享资源临界区。核心目的防止竞态条件Race Condition保证数据一致性。厕所类比 公共厕所只有一个坑位共享资源。互斥就是门锁一个人进去后锁门其他人必须在外面等直到里面的人出来。目的避免两个人同时进去的尴尬数据错乱。同步Synchronization定义多个线程/进程在执行次序上协调相互等待以达到某种执行顺序。核心目的保证任务执行的时序性和协作性。接力赛类比 4 名运动员跑接力赛。同步就是交接棒第二个人必须等第一个人跑到交接区并递棒后才能跑。目的保证顺序正确A 做完B 才能做。Q3:讲解一下锁包括互斥锁自旋锁读写锁乐观锁与悲观锁A3:悲观锁 vs 乐观锁设计理念这是最上层的并发控制策略不仅适用于代码也适用于数据库。悲观锁 (Pessimistic Locking)核心思想“总觉得别人会改我的数据所以操作前先上锁。”行为获取锁 → 执行操作 → 释放锁。适用场景写操作多冲突频繁。Java 实现synchronized,ReentrantLock。数据库实现SELECT ... FOR UPDATE。乐观锁 (Optimistic Locking)核心思想“觉得别人不会改我的数据所以先操作提交时检查版本。”行为读取数据记版本号 → 执行操作 → 提交时对比版本号CAS。适用场景读多写少冲突少。Java 实现AtomicInteger(CAS),LongAdder。数据库实现version字段UPDATE table SET vv1 WHERE id1 AND versionold_version。互斥锁 vs 自旋锁等待机制当锁被占用时线程该怎么办这是操作系统层面的核心区别。1. 互斥锁 (Mutex Lock / Blocking Lock)机制如果锁被占用线程放弃 CPU进入阻塞状态Blocked由操作系统调度唤醒。优点不浪费 CPU 资源适合锁持有时间长的场景。缺点用户态→内核态切换上下文切换开销大微秒级。Java 对应synchronized(重量级),ReentrantLock(阻塞后)。 流程 申请锁 → 失败 → 挂起线程 (OS 介入) → 睡眠 → 被唤醒 → 重新竞争2. 自旋锁 (Spin Lock)机制如果锁被占用线程不放弃 CPU在用户态循环检查Busy Wait直到锁释放。优点无上下文切换适合锁持有时间极短的场景。缺点浪费 CPU 资源若锁持有时间长会导致 CPU 100%。Java 对应Atomic类CAS 自旋ReentrantLock(尝试获取阶段) 流程 申请锁 → 失败 → while(!tryLock()) { } (空循环) → 成功关键权衡什么时候自旋Java 的ReentrantLock其实是自旋 互斥的组合先自旋尝试几次避免轻微竞争就阻塞。如果自旋失败再挂起线程进入互斥阻塞。警告Java 没有直接暴露SpinLock类因为单核 CPU 自旋是死循环永远拿不到锁多核 CPU 也需谨慎使用。读写锁访问模式针对读多写少的场景优化提升并发度。核心规则操作读锁 (Read Lock)写锁 (Write Lock)读锁✅ 兼容 (共享)❌ 互斥写锁❌ 互斥❌ 互斥 (独占)读 - 读不互斥多个线程可同时读。读 - 写互斥。写 - 写互斥。潜在问题与优化写饥饿如果读请求源源不断写线程可能永远拿不到锁。锁降级Java 支持 写锁→读锁 降级不支持 读锁→写锁 升级防死锁。进阶StampedLock(Java 8)性能更好支持乐观读但不可重入。