「操作系统」 操作系统基础知识

Posted by Dawn-K's Blog on March 4, 2021

操作系统基础知识

参考资料

概述

操作系统是一个系统软件,管理进程,调度系统资源,输入输出等,提供一个让用户和系统交互的操作界面。

线程进程

  • 进程是资源分配的最小单位,线程是 CPU 调度的最小单位
  • 多线程程序只要有一个线程崩溃,整个程序就崩溃了,但多进程程序中一个进程崩溃并不会对其它进程造成影响,因为进程有自己的独立地址空间,因此多进程更加健壮
  • 在进程切换时,涉及到整个当前进程 CPU 环境的保存环境的设置以及新被调度运行的 CPU 环境的设置,而线程切换只需保存和设置少量的寄存器的内容,并不涉及存储器管理方面的操作

线程共享数据

  • 进程代码段
  • 进程的公有数据(全局变量、静态变量。..)
  • 进程打开的文件描述符
  • 进程的当前目录
  • 信号处理器/信号处理函数:对收到的信号的处理方式
  • 进程 ID 与进程组 ID

线程独占的资源

  • 线程 ID
  • 一组寄存器的值
  • 线程自身的栈(堆是共享的)
  • 错误返回码:线程可能会产生不同的错误返回码,一个线程的错误返回码不应该被其它线程修改;
  • 信号掩码/信号屏蔽字 (Signal mask): 表示是否屏蔽/阻塞相应的信号 (SIGKILL, SIGSTOP 除外)

进程间通信方式

管道

特殊文件,一个管道可供进程进行半双工通信。此处是指匿名管道,仅能在有亲缘关系的进程之间使用(如父子或者兄弟之间).

命名管道

命名管道支持任意两个进程进行通信,而且是全双工的

消息队列

消息队列是一个消息的链表。可以供进程读写。

共享内存

多个进程共享一块内存区域,但是需要加锁和同步。

信号量

用于同步和互斥。

套接字

可用于更为一般的进程间通信,甚至可以是网络上不同机器的进程间通信。 域,端口号,协议类型。

操作系统种类

网络操作系统和分布式操作系统区别

网络操作系统是把在同一网络上的多个计算机连接起来,实现资源共享和通信。 分布式操作系统是在网络操作系统的基础上,实现工作的分布,让若干台计算机可以并行完成同一工作。

死锁

信号量与互斥量

  • 信号量为正时表示资源剩余量。
  • 信号量为负时表示正在等待的进程数。 这里之前写的不严谨,实际上信号量和互斥量是两种东西。 参考资料 信号量用于同步,互斥量用于互斥。
  • 信号量 (semaphore) 是作用于不同的进程,是进程通信的一种方式
  • 互斥量 (mutex) 是在一个进程之中的,互斥量值只能为 0/1, 是对资源的占有和释放,是一种锁机制

PV 操作

  • P 即 wait
  • V 即 signal

死锁的必要条件

  • 互斥条件:一个资源只能被一个进程使用
  • 请求与保持条件:一个进程因为请求其他资源而被阻塞时,不会放弃已经获得的资源。
  • 不剥夺条件:进程未使用完资源之前不能强行剥夺
  • 循环等待条件

解除死锁的方法

从上个问题,逐步解决必要条件。

  • 互斥问题是资源的性质决定的,没法解决。
  • 破坏请求与保持的条件,也就是让进程必须一次申请完所有的资源。
  • 破坏不剥夺条件,也就是进程如果申请失败必须归还所有的已经占用的资源。
  • 破坏循环等待条件,也就是给资源标号,只能按照递增的顺序申请,这样就不会循环等待了。

epoll

Epoll 的本质

select 和 epoll 都是为了解决系统 IO 问题的,后者是对前者的改进。比如某个进程要监视多个 socket , 如果接受到了信息,进程就再做下一步打算(可以理解为等待 IO 时阻塞了).

select

假如进程 A 同时监视的 sock1、sock2 和 sock3 三个 socket. 那么系统会将 A 加入这三个 socket 的等待队列中,一旦任意一个接受到了信息,就唤醒进程。然后将 A 从所有的等待队列中移除,然后放入到就绪队列,最后再遍历一次进程,得到是哪个进程发来的数据。 可见,加入,移除,查询哪个 socket , 一共需要三次遍历,效率低下。 所以 select 方法最多监视 1024 个进程。当然如果最开始调用 select 的时候就有 socket 有数据了,那么会直接返回,不用阻塞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int s = socket(AF_INET, SOCK_STREAM, 0);  
bind(s, ...)
listen(s, ...)
 
int fds[] =  存放需要监听的 socket
 
while(1){
    int n = select(..., fds, ...)
    for(int i=0; i < fds.count; i++){
        if(FD_ISSET(fds[i], ...)){
            //fds[i] 的数据处理
        }
    }
}

epoll 对 select 的改进

epoll 将操作分成了三步:epoll_create epoll_ctl epoll_wait

1
2
3
4
5
6
7
8
9
10
11
12
13
int s = socket(AF_INET, SOCK_STREAM, 0);   
bind(s, ...)
listen(s, ...)
 
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的 socket 添加到 epfd 中
 
while(1){
    int n = epoll_wait(...)
    for(接收到数据的 socket){
        //处理
    }

  • 某个进程调用 epoll_create 方法时,内核会创建一个 eventpoll 对象(也就是程序中 epfd 所代表的对象)
  • eventpoll 对象相当于是 socket 和进程之间的中介,socket 的数据接收并不直接影响进程,而是通过改变 eventpoll 的就绪列表来改变进程状态。
  • 当程序执行到 epoll_wait 时,如果 rdlist 已经引用了 socket, 那么 epoll_wait 直接返回,如果 rdlist 为空,阻塞进程。
  • 当 socket 接收到数据,中断程序一方面修改 rdlist, 另一方面唤醒 eventpoll 等待队列中的进程,进程 A 再次进入运行状态。也因为 rdlist 的存在,进程 A 可以知道哪些 socket 发生了变化。

扇区和簇

扇区 (Sector) 是磁盘最小的物理存储单元,是磁盘读写基本单位。但是扇区太多了,操作系统难以对每个扇区寻址,所以引入了簇。 簇 (Clust) 是多个扇区组成的数据块,是操作系统文件存储的最小单位。也就是一个文件再小也要占据一个簇。然后每个文件的最后一个簇可能未使用全。这个在 Windows 下叫簇,在 Linux 下如 Ext4 等文件系统中叫做块 (Block). 磁盘控制器是用来映射块和簇的。

FAT32 和 NTFS

文件系统 FAT 与 NTFS 的简介和区别

FAT 是文件配置表 (File Allocation Table). FAT32 为 FAT 的最后一个产品,采用 32 位文件分配表,可以管理单文件大小最大为 4G.

FAT 系统

  • 安全性差
  • 容易产生磁盘碎片
  • 难以恢复等问题

相比于 FAT 文件管理来说,NTFS 文件管理有

  • 更安全的文件保障,可以加密
  • 更好的磁盘压缩功能,减少磁盘碎片
  • 支持最大 2TB 的硬盘
  • 可以赋予单个文件和文件夹权限等种种优点。

僵尸进程

任何一个子进程 (init 除外)在 exit() 之后,并非马上就消失掉,而是留下一个称为僵尸进程 (Zombie) 的数据结构,等待父进程处理。可以令父进程接到信号的时候执行 waitpid()

孤儿进程

孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被 init 进程(进程号为 1) 所收养,并由 init 进程对它们完成状态收集工作。

优先级反转

试想如下三个进程 A 优先级很低,目前占有一个锁 B 优先级中等 C 优先级高,且目前需要 A 的锁

那么 C 会因为 A 的锁而一直阻塞,而 A 因为本身优先级很低,所以很难被运行,这样就导致 B 的优先级比另外两个都高,这就是优先级反转

解决方案

  1. 设置优先级上限,也就是把临界区的优先级设置的超过其他所有进程,任何一个进入此临界区的变量,都会被设置为这种超高优先级,就不会出现优先级反转的情况了
  2. 优先级继承,当一个高优先级进程等待一个低优先级进程持有的资源时,低优先级进程将暂时获得高优先级进程的优先级别,在释放共享资源后,低优先级进程回到原来的优先级别。
  3. 临界区禁止中断