CPPInterview

操作系统

操作系统考点

  1. 进程管理
    • 进程、线程、协程
    • 进程通信方式
    • 线程同步方式
    • 死锁的条件
    • 生产者-消费者
  2. 内存管理
    • 分页、分段、段页
    • 页面置换算法
    • 虚拟地址空间
    • 快表
    • 伙伴系统
    • SLAB分配器
    • 页表目录
  3. 文件管理
    • VFS虚拟文件系统
    • page cache
    • FAT
    • ext2
  4. IO
    • 中断机制
    • DMA和通道
    • IO多路复用技术
    • 内核态和用户态

      内核态和用户态

      - 系统调用
      - 中断
      - 异常 [参考视频](https://www.bilibili.com/video/BV16J411p7f1)
      - 内核态用于执行一些特权指令,比如中断机制,原语,进程管理等,目的是保护系统程序。
      - 内核态通常包括三个方面:系统调用,中断和异常
      - 中断和异常都可以实现用户态到内核态的切换,是通过硬件实现的。
      - 中断:IO完成,定时器时钟中断。
      - 异常:越界,溢出,缺页,异常不能被屏蔽,一旦出现应立即处理。 ### 写文件的流程
      - fwrite可以把数据写到用户空间缓冲区,但不会立即写到内核
      - write内核函数
      - fflush把用户缓冲区的数据立即写到内核中的页缓冲区
      - 当我们写文件的时候,内核的文件系统模块把数据保存在页缓冲区中,不会立即写到存储设备。我们可以使用fsync把文件修改过的属性和数据立即写到存储设备,或者使用fsyncdata把文件修改过的数据立即写到存储设备。
      - 应用程序可以使用glibc库封装的标准IO流函数访问文件。
      - 标准IO流提供了缓冲区,目的是尽可能减少调用read和write的次数,提高性能。 ### 写入文件到磁盘的过程
      - [参考博客](https://blog.csdn.net/bandaoyu/article/details/102594469)
      - fwrite---application data---clib buffer-----page cache----disk
      - fwrite返回的时候,数据还在clib buffer中,调用fclose可以触发文件写入到disk中。
      - clib buffer刷新到page cache内核缓冲区可以调用fflush函数(非系统调用)
      - page cache内核缓冲区强制刷新到磁盘可以调用fsync函数(系统调用)
      - write函数直接从application data拷贝到page cache内核缓冲区 ## 进程 ### linux文件类型
      - -普通文件
      - d目录
      - l符号连接
      - s套接字
      - b块文件
      - c字符文件
      - p管道 ### 什么是进程
      - 进程是系统进行资源分配的基本单位,是程序加载到内存后的执行过程。进程一般由数据段,代码段和进程控制块三部分组成。系统通过进程控制块感知进程的存在并对进程进行控制。由于进程之间虚拟地址空间相互独立,多进程比多线程更安全,一个进程基本上不会影响另外一个进程。
      

进程三种状态

  1. 就绪
  2. 运行
  3. 阻塞

    IPC进程间通信

  4. 管道
  5. 共享内存
  6. 消息队列
  7. 信号(开销小)
  8. 信号量
  9. socket

    僵尸进程

    • 子进程先结束,而父进程没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。

      孤儿进程

    • 父进程先结束,而子进程仍然存活,此时子进程称为孤儿进程,将由系统的init进程负责回收相关资源。

      什么是线程

    • 线程是CPU调度的基本单位。一个进程可以包含多个线程,线程占有的系统资源很少,线程可以和同属于一个进程的其他线程共享进程所拥有的全部资源。多线程之间对内存共享,线程间通信可以直接基于共享内存来实现,比多进程之间通信更方便。多线程之间切换不需要切换虚拟内存空间、文件描述符等,所以线程的上下文切换也比多进程轻量。

      线程间同步

  10. 互斥锁
  11. 读写锁(读时共享,写时互斥)
  12. 条件变量
  13. 信号量(互斥锁的升级版)
  14. 自旋锁(可以避免进程或线程上下文的开销)

线程共享资源

  1. 文件描述符表(打开的文件)
  2. 进程用户ID和进程组ID
  3. 进程的内存地址空间
    • .text代码段
    • .data数据段
    • .bss
    • 堆区
    • 全局变量
    • 静态变量
  4. 每种信号的处理方式
  5. 进程的当前目录

线程独享资源

  1. 线程栈
  2. 寄存器的值
  3. 线程ID
  4. 错误返回码errno变量
  5. 线程信号屏蔽字
  6. 线程优先级

    多进程和多线程的应用场景

  7. 一般不同任务间需要大量的通信,使用多线程的场景比多进程多。IO密集型。
  8. 但是多进程有更高的容错性,一个进程的崩溃不会导致整个系统的崩溃,在任务安全性较高的情况下,采用多进程。CPU密集型。

进程线程的本质区别

  1. 进程更安全,一个进程完全不会影响另外的进程。
  2. 进程间通信比线程间通信的性能差很多,线程切换开销更低。

进程和线程的区别

IO

阻塞IO和非阻塞IO

同步IO和异步IO

事件处理模式

  1. reactor 同步IO模型通常用于实现reactor模式。要求主线程只负责监听文件描述符是否有事件发生,有的话就立即将该事件通知工作线程。
  2. proactor 异步IO模型通常用于实现proactor模式。也可以用同步IO模拟出proactor模式。proactor将所有IO操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。

Reactor模式的工作流程

  1. 主线程往epoll内核事件表中注册socket上的就绪事件。
  2. 主线程调用epoll_wait等待socket上有数据可读。
  3. 当socket上有数据可读时,epoll_wait通知主线程。主线程将socket可读事件放入请求队列。
  4. 睡眠在请求队列上的某个工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件。
  5. 主线程调用epoll_wait等待socket可写。
  6. 当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列。
  7. 睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。

并发模式

  1. 半同步半异步模式:同步线程用于处理客户逻辑,异步线程用于处理IO事件。异步线程监听到客户请求后,就将其封装成请求对象并插入到请求队列中,请求队列将通知某个工作在同步模式下的工作线程来读取并处理该请求对象。半同步半反应堆模式采用的事件处理模式是reactor模式:它要求工作线程自己从socket上读取客户请求和往socket写入服务器应答。半同步半反应堆也可以模拟proactor模式,即由主线程来完成数据的读写。在这种情况下,主线程会将应用程序数据,任务类型等信息封装为一个任务对象然后将其插入请求队列工作线程从请求对象取得任务对象以后,可直接处理无需执行读写操作。 问题:主线程和工作线程共享请求队列需要加锁。工作线程较少时可能产生请求任务堆积。

  2. 领导者追随者模式

虚拟地址空间

内核态和用户态

进程调度方式

  1. 抢占式:立马停止。
  2. 非抢占式:时间片用完或者等待资源时,再调用另一个进程。

进程调度算法

  1. 先来先服务
  2. 短作业优先
  3. 优先级调度
  4. 时间片轮转
  5. 高响应比优先

管道

大端字节序和小端字节序

  1. 大端字节序:网络字节序(高位存低位)
  2. 小端字节序:主机字节序,现代PC机采用小端字节序(低位存低位,高位存高位)

比如0x1f3f5f7f 地址0x1000 0x1001 0x1002 0x1003

大端法:7f存在0x1003 5f存0x1002 3f存0x1001 1f存0x1000 低存高

小端法:7f存在0x1000 5f存0x1001 3f存0x1002 1f存0x1003 低存低

socket server

socket 创建socket文件描述符 bind 绑定IP和端口号 listen 设置监听连接数的上限
accept 阻塞等待数据到来 read/write 处理客户端的业务 close 关闭文件描述符

socket client

socket 创建套接字文件描述符 bind 绑定IP和端口号(也可以隐式绑定) connect 尝试连接服务器(建立TCP连接) write/read 处理服务器端的业务

五种网络IO模型

  1. 同步阻塞IO
  2. 同步非阻塞IO
  3. IO多路复用
  4. 信号驱动IO
  5. 异步IO

协程

信号

什么是死锁

fork函数

exec族

Linux server

mmap存储映射

异步IO原理

文件

ret = read(fds[0], buf, sizeof(buf)); //返回读取到的字节数
write(fds[1], str, strlen(str));

fork

int pid = fork()
if(pid > 0){} 父进程
else if(pid == 0){}子进程 -1表示失败

pipe

itn fds[2];
int ret = pipe(fds);
0表示成功 -1表示失败

exec

execlp("ls","随便什么都可以","-l","-a",NULL); //最后一定要传入NULL用于指示不定长参数的结尾

信号

void my_handler(int sig){
    printf("hello world!\n");
    abort(); //终止进程
}
int main(){
    signal(SIGALRM, my_handler); //注册信号捕捉函数
    alarm(1); //定时
    for(int i = 0; ; i++){
        printf("%d\n",i);
    }
    return 0;
}

alarm

定时器,只能支持到秒级,与进程状态无关,进程挂起时也在计时中。

每个进程都有且只有一个唯一个定时器,二次调用覆盖原来的定时器,返回原来定时的剩余时间。

alarm(5) //5秒后程序终止
alarm(0) //取消闹钟

setitimer微秒级定时

动态分配内存

分页内存管理

分段内存管理

虚拟内存

进程调度算法

自旋锁

参考博客链接

伙伴系统

参考博客链接

slab机制

页表

僵尸进程

孤儿进程

内存空间分布

上下文切换开销

ELF文件执行的流程

文件系统的主要功能

VFS 虚拟文件系统

日志文件系统

打开文件描述符表

打开文件描述符表,简称为打开文件表。 当进程打开一个文件的时候,虚拟文件系统会创建文件的一个打开实例:file结构体,然后在进程的打开文件表中分配一个索引inode,这个索引称为文件描述符,最后把文件描述符和file结构体的映射添加到打开文件表中。 file结构体包括:

打开文件表是PCB进程控制块的一部分,进程控制块是个task_struct结构体包括:

文件系统

文件系统应具有以下功能

  1. 完成文件存储系统的管理,即分配空间和回收空间。
  2. 实现文件名到物理地址的映射。
  3. 实现文件和目录的建立,读写管理。
  4. 向用户提供有关文件和目录操作的接口。

文件系统层次结构

IO调度算法

分页管理

虚拟文件系统VFS

mount挂载

linux上运行可执行文件的过程

内存管理

虚拟地址空间

读写文件的方式

IO控制方式

中断机制

参考博客

VFS中的数据结构

超级块Super Block

索引节点iNode

目录项

打开文件表