八股文之操作系统


协程?

协程协程是一种用户态的轻量级线程,协程的调度完全由用户控制。

协程的优势:

  1. 协程调用跟切换比线程效率高:协程执行效率极高。协程不需要多线程的锁机制,可以不加锁的访问全局变量,所以上下文的切换非常快。
  2. 协程占用内存少:执行协程只需要极少的栈内存(大概是4~5KB),而默认情况下,线程栈的大小为1MB。
  3. 切换开销更少:协程直接操作栈基本没有内核切换的开销,所以切换开销比线程少。

进程和线程的区别?

线程和进程最大的区别在于:进程是CPU分配资源的单位;线程是CPU调度的单位

一个程序至少有一个进程,一个进程至少有一个线程

  • 从系统调度上看:进程是资源管理的基本单位,线程是程序执行的基本单位。
  • 从上下文切换上看:线程上下文切换比进程上下文切换要快得多。
  • 从系统开销上看:创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。

进程间的通信方式

进程之间的通信方式包括管道,信号,信号量,消息队列,共享内存,socket六种方式。

管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

有名管道 (namedpipe) :有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

信号 (sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

信号量(semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

消息队列( messagequeue ) :消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

套接字(socket ) :套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。

线程之间的通信方式?

锁机制:包括互斥锁、读写锁、条件变量。

  1. 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
  2. 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
  3. 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

**信号机制(Signal)**:类似进程间的信号处理

线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

**信号量机制(Semaphore)**:包括无名线程信号量和命名线程信号量

实现进程之间的同步

实现进程间同步主要有如下方法:

  • 信号量
  • 用P、V原语
  • 生产者-消费者模型

虚拟内存解决了什么问题

虚拟内存是什么?

  • 虚拟内存技术实际上就是建立了 “内存一外存”的两级存储器的结构。利用局部性原理实现髙速缓存
  • 虚拟内存是一种计算机系统内存管理技术。虚拟内存通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

解决什么问题?

  1. 进程隔离:当没有虚拟内存的时候,会直接操作物理内存,会写入不同进程对同一物理地址进行抢占和覆盖造成程序上不可预知的错误。
  2. 缓存作用:虚拟内存的访问速度比磁盘快很多,所以将磁盘一些热度数据放到虚拟内存提高访问速度。
  3. 内存管理:直接操作物理内存会让开发者在开发中要顾及十分复杂的操作方式,所以虚拟内存同时也把物理内存抽象成页表结构进行管理,方便程序运行时对内存的申请,释放等一系列操作。

共享内存是一种用于实现进程间通信(IPC)的方法,不同进程通过访问同一块内存区域实现数据共享和交互

虚拟内存是依靠什么实现的呢?

  1. 请求分页存储管理。
  2. 请求分段存储管理。
  3. 请求段页式存储管理。

讲一讲IO多路复用?

IO****多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;多路是指网络连接,复用指的是同一个线程。

IO多路复用有三种实现方式:select,poll, epoll

  • select:时间复杂度O(n),知道有I/O事件发生了,却并不知道是哪几个流;只能轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。
  • poll:时间复杂度O(n),poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的。
  • epoll:时间复杂度O(1),epoll可以理解为event poll,epoll会把哪个流发生了怎样的I/O事件通知我们。所以说epoll实际上是事件驱动(每个事件关联上fd) 的,此时对这些流的操作都是有意义的。

下面举一个例子,模拟一个tcp服务器处理30个客户socket。

假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:

  1. (select)第一种选择:按顺序逐个检查,先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡住,全班都会被耽误。这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。
  2. (poll)第二种选择:你创建30个分身,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。
  3. (select)第三种选择,你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。

这种就是IO复用模型,Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式

这样,整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。

epoll 中,什么是水平触发,什么是边缘触发

  • 水平触发工作方式(LT)

处理socket时,即使一次没将数据读完,下次调用epoll_wait时该文件描述符也会就绪,可以继续读取数据

  • 边沿触发工作方式(ET)

处理socket时没有一次将数据读完,那么下次再调用epoll_wait该文件描述符将不再显示就绪,除非有新数据写入 在该工作方式,当一个文件描述符就绪时,我们要一次性的将数据读完

区别:

  • 水平触发是只要读缓冲区有数据,就会一直触发可读信号,

  • 边缘触发仅仅在空变为非空的时候通知一次

  • epol底层实现

  • epoll:代码

  • ```C
    #include <sys/epoll.h>
    int epoll_create(int size); // 在内核里,一切皆文件。所以,epoll向内核注册了一个文件系统,
    //epoll_create的作用是创建一个epoll模型,该模型在底层建立了->
    //红黑树,就绪队列,回调机制
    //size可以被忽略,不做解释
    int epoll_ctl(int epfd, int op, int fd, struct epoll_events *event);
    //epfd:epoll_create()的返回值(epoll的句柄,本质上也是一个文件描述符)
    //op:表示动作,用三个宏来表示
    // EPOLL_CTL_ADD:注册新的fd到epfd中
    // EPOLL_CTL_MOD:修改已经注册的fd的监听事件
    // EPOLL_CTL_DEL:从epfd中删除一个事件
    //fd:需要监听的文件描述符
    //event:具体需要在该文件描述符上监听的事件
    int epoll_wait(int epfd, struct epoll_event *events, int maxevents,

                      int timeout);     
    

    //函数调用成功,返回文件描述符就绪的个数,也就是就绪队列中文件描述符的个数,
    //返回0表示超时,小于0表示出错
    //epoll_event结构体
    struct epoll_event{

      uint32_t events;  /* Epoll events */ 
      epoll_data_t data;/* User data variable */    
    

    }__EPOLL_PACKED;
    //events可以是一堆宏的集合,这里介绍几个常用的
    // EPOLLIN:表示对应的文件描述符可以读(包括对端socket正常关闭)
    // EPOLLOUT:表示对应的文件描述符可以写
    // EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,
    // 默认情况下epoll为水平触发(Level Triggered)模式
    typedef union epoll_data{

             void *ptr;
             int fd;
             uint32_t u32;
             uint64_t u64;
     }epoll_data_t;     
    

    //联合体里通常只需要填充fd就OK了,其他参数暂时可以不予理会

    
    ## 怎么避免死锁
    
    破坏死锁的四个必要条件
    
    1. **互斥条件**:一个资源每次只能被一个线程使用; 
    2. **请求与保持条件**:一个进程因请求资源而阻塞时,对已获得的资源保持不放; 
    3. **不剥夺条件**:进程已获得的资源,在末使用完之前,不能强行剥夺; 
    4. **循环等待条件**:若干进程之间形成一种头尾相接的循环等待资源关系
    
    ## 什么是上下⽂切换?
    
    - 当前任务在执⾏完 CPU 时间⽚切换到另⼀个任务之前会先保存⾃⼰的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。**任务从保存到再加载的过程就是⼀次上下⽂切换。**
    
    ## CPU 上下⽂切换的类型
    
    CPU 上下⽂切换⾄少有三种不同的类型:
    
    - **进程上下⽂切换**
    
    - 1. 进程是由内核管理的,进程切换只能发⽣在内核态。因此,进程上下⽂不仅包括虚拟内存、栈和全局变量等用户空间资源,还包括内核栈和寄存器等内核空间的状态。
    
    - **线程上下⽂切换**
    
    - 1. 线程的上下文切换其实可以分为两种情况:
         1. 1,前后两个线程属于不同的进程。此时,由于资源不共享,切换过程与进程上下文切换相同。
         2. 2,前后两个线程属于同一个进程。此时,由于虚拟内存是共享的,所以切换时虚拟内存的资源保持不变,只需要切换线程的私有数据、寄存器等未共享的数据。
    
    - **中断上下⽂切换**
    
    - 1. 与进程上下文不同,**中断上下文切换不涉及进程的用户态**。因此,即使中断进程中断了处于用户态的进程,也不需要保存和恢复进程的虚拟内存、全局变量等用户态资源。
      2. **中断上下文切换也会消耗 CPU**。过多的切换次数会消耗大量的 CPU 资源,甚至严重降低系统的整体性能。
    
    https://blog.csdn.net/tiantianhaoxinqing__/article/details/125735692
    
    Linux 按照特权级别将进程的运⾏空间划分为**内核态**和**用户态**
    
    - **内核空间**(`Ring 0`)拥有最⾼权限,可以直接访问所有资源;
    - **用户空间**(`Ring 3`)只能访问受限资源,不能直接访问内存等硬件设备。它必须 通过**系统调⽤**被陷⼊(`trapped`)内核中才能访问这些特权资源。
    
    从另⼀个⻆度看,当⼀个进程在**⽤⼾空间**运⾏时,称为该进程的**⽤⼾态**,当它落⼊**内核空间**时,称为该进程的**内核态**。
    
    ## **并发与并行的区别是什么?**
    
    - 并发: 同⼀时间段,多个任务都在执⾏ (单位时间内不⼀定同时执⾏); 
    - 并⾏: 单位时间内,多个任务同时执⾏。
    
    最关键的点就是:**是否是『同时』**。
    
    ## **进程与线程的切换流程?**
    
    进程切换分两步:
    
    1. 切换页表以使用新的地址空间,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
    2. 切换内核栈和硬件上下文。
    
    对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2步是进程和线程切换都要做的。
    
    因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。
    
    ## **进程调度策略有哪几种?** 
    
    > 先来先服务,短作业优先,最短剩余时间优先,时间片轮转,优先级调度。
    
    - **先来先服务**:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一 直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。另外,对i/o密集型进程也不利,因为这种进程每次进行I/o操作之后又得重新排队。
    - **短作业优先**:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
    - **最短剩余时间优先**:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一.个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
    - **时间片轮转**:将所有就绪进程按FCFS的原则排成一一个队列,每次调度时,把CPU时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把CPU时间分配给队首的进程。时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。而如果时间片过长,那么实时性就不能得到保证。
    - **优先级调度**:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
    
    ## **进程有哪些状态?**
    
    进程一共有5种状态,分别是**新建、就绪、运行、阻塞、终止。**
    
    ## **什么是用户态和内核态?**
    
    用户态和系统态是操作系统的两种运行状态:
    
    - **用户态**:用户态运行的程序只能受限地访问内存,只能直接读取用户程序的数据,并且不允许访问外围设备,用户态下的CPU不允许独占,也就是说CPU能够被其他程序获取。
    - **内核态**:内核态运行的程序可以访问计算机的任何数据和资源,不受限制,包括外围设备,比如网卡、硬盘等。处于内核态的CPU可以从一个程序切换到另外一个程序,并且占用CPU不会发生抢占情况。
    
    区别在于运行指令集的权限不一样。内核态运行 CPU 指令集的权限更高(ring 0),用户态运行 CPU 指令集权限更低(ring 3)。所以如果用户态需要操作硬件相关的内容,只能切换到内核态去提高运行 CPU 指令集的权限去运行。
    
    ## **什么时候会导致用户态切换到内核态**
    
    - 系统调用。用户态调用一些内核态提供的接口,用户态主动切换至内核态。比如 redis fork 进程进行 rdb 时就会进行系统调用
    - 异常。当进程在用户态执行任务的时候,发生一些异常会切换到内核态去处理。比如 redis rdb 时  cow 会触发缺页异常
    - 中断。外围硬件设备如果处理完请求,就会向 CPU  发出中断信号,让 CPU 暂停执行下一条指令,转到中断信号对应的程序去执行。比如磁盘 IO 读写完成
    
    ## 说一说你对Linux内核的了解。
    
    内核是操作系统的核心,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能和稳定性。
    
    Linux 内核有 4 项工作:
    
    1. **内存管理:** 追踪记录有多少内存存储了什么以及存储在哪里
    2. **进程管理:** 确定哪些进程可以使用中央处理器(CPU)、何时使用以及持续多长时间
    3. **设备驱动程序:** 充当硬件与进程之间的调解程序/解释程序
    4. **系统调用和安全防护:** 从流程接受服务请求
    
    ## **软链接和硬链接有什么区别?**
    
    Linux链接文件类似于Windows下的快捷方式。 
    
    **软链接:软链接不占用磁盘空间,原文件删除则软链接失效。**
    
    **硬链接:相当于对源文件一个备份。删除硬链接文件不影响原文件,与源文件同步变化。**
    
    **语法格式(ln)区别**:
    
    - 硬链接:ln 源文件 链接名
    - 软链接:ln -s 源文件 链接名
    
    注意:**链接的源文件路径要写绝对路径,否则会报错。**
    
    ## **什么是内存抖动,有什么解决方案**
    
    **内存抖动**指的是在一段时间内频繁创建对象和销毁对象所引起的频繁 GC 的现象
    
    对于 Go 而言,是没有分代的概念的,只有在频繁创建会分配在堆上大对象,或者是逃逸的对象,就会引起内存抖动
    
    具体的解决方案也很常见
    
    - 池化技术,池化频繁创建的对象,复用原有对象去避免创建和销毁的动作
    - 编码习惯,避免在一些循环里面去创建对象
    
    ## 深拷贝和浅拷贝的区别是什么?
    
    **浅拷贝**只是对指针的拷贝,拷贝后两个指针指向同一个内存空间;
    
    **深拷贝**不断对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同的地址空间。
    
    ## **大端地址和小端地址是什么含义,怎么判断计算机是大端还是小端**
    
    1. 什么是大端和小端
       1. 大端(高尾端):按照内存增长方向,数据的尾端放在高位内存
       2. 小端(低尾端):按照内存的增长方向,数据的尾端放在地位内存
    2. 如何判断 CPU  是大端还是小端现代的 CPU 
    3. 一般都是采用小端。进行本机验证可以使用 C 语言的结构体验证,因为 C 语言的结构体的变量都是按顺序进行存放,只需要验证变量的地址位置就能得知 CPU 是大端还是小端模式
    4. 网络传输的大小端问题是什么由于网络传输协议都是按照大端模式的,所以在通信前发送数据都需要进行字节序的转换
    
    ## **外中断和内中断有什么区别**
    
    - **外中断:**指的是 CPU 指令以外的事件导致的中断,比如 IO 输入输出、时钟事件等
    - **内中断:**又叫做异常,指的是 CPU 指令导致的中断,比如运行时发生堆栈溢出,数组越界
    
    ## **中断的处理过程?**
    
    1. 保护现场:将当前执行程序的相关数据保存在寄存器中,然后入栈。
    2. 开中断:以便执行中断时能响应较高级别的中断请求。
    3. 中断处理
       1. 关中断:保证恢复现场时不被新中断打扰
       2. 恢复现场:从堆栈中按序取出程序数据,恢复中断前的执行状态。
    
    # linux基本命令
    
    熟练使用Linux常用的命令
    
    Is
    
    clear
    
    cd
    
    pwd
    
    mkdir
    
    touch
    
    rm
    
    cp
    
    mv
    
    chmod
    
    find
    
    grep
    
    重定向
    
    软连接、硬连接
    
    tar
    
    shutdown
    
    reboot
    
    who
    
    exit
    
    passwd
    
    rwx 含义 
    
    r read 表示可读取,对于一个目录,如果没有r权限,那么就意味着不能通过ls查看这个目录的内容。 
    
    w write 表示可写入,对于一个目录,如果没有w权限,那么就意味着不能在目录下创建新的文件。 
    
    x excute 表示可执行,对于一个目录,如果没有x权限,那么就意味着不能通过cd进入这个目录。 
    
    ![img](https://nateshao-blog.oss-cn-shenzhen.aliyuncs.com/img/20230215011632.png)
    
    ## **1.1 Linux里如何查看一个想知道的进程?**
    
    **查看进程运行状态的指令**:ps命令。“**ps -aux | grep PID**”,用来查看某PID进程状态
    
    > ps使用示例  //显示当前所有进程   ps -A   //与grep联用查找某进程   ps -aux | grep apache    //查看进程运行状态、查看内存使用情况的指令均可使用top指令。 top
    
    ## **1.2 Linux里如何查看带有关键字的日志文件?**
    
    1. **cat 路径/文件名 | grep 关键词**
    
     \# 返回test.log中包含http的所有行 cat test.log | grep "http"
    
    1. **grep -i 关键词 路径/文件名** (与方法一效果相同,不同写法而已)
    
     \# 返回test.log中包含http的所有行(-i忽略大小写) grep -i "http" ./test.log 
    
    ## **1.3 Linux修改主机名的命令是什么?**
    
    1. 如果只需要临时更改主机名,可以使用hostname命令。
       1. ```Shell
          sudo hostname <new-hostname> 
          # 例如: sudo hostname myDebian #myDebian为修改名
  1. 如果想永久改变主机名,可以使用hostnamectl命令
    1. ```Shell
      sudo hostnamectl set-hostname myDebian #myDebian为修改名
      
      ### Linux查看进程:“ps -aux | grep PID”
      
      ### Linux查看关键字的日志文件?
      
      1. **cat 路径/文件名 | grep 关键词**
         1. ```Shell
            # 返回test.log中包含http的所有行 
            cat test.log | grep "http"
  2. grep -i 关键词 路径/文件名 (与方法一效果相同,不同写法而已)
    1. ```Shell

      返回test.log中包含http的所有行(-i忽略大小写)

      grep -i “http” ./test.log ^a 行首,搜寻以 m 开头的行;grep -n ‘^a’ 1.txt
      ke$ 行尾,搜寻以 ke 结束的行;grep -n ‘ke$’ 1.txt
      
      ### **Linux查看内存:free命令**
      
      Linux free命令用于**显示内存状态**。free指令会显示内存的使用情况,**包括实体内存,虚拟的交换文件内存,共享内存区段,以及系统核心使用的缓冲区等。**
      
      ### **Linux查看进程:top命令**
      
      **top**命令。**显示当前系统正在执行的进程的相关信息,包括进程 ID、内存占用率、CPU 占用率等**
      
      ### Linux查看连接数:**netstat**
      
      ```Shell
      //示例
      查看Web服务器(Nginx Apache)的并发请求数及其TCP连接状态:
      netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
      解释:
      返回结果示例: 
      LAST_ACK 5   (正在等待处理的请求数) 
      SYN_RECV 30 
      ESTABLISHED 1597 (正常数据传输状态) 
      FIN_WAIT1 51 
      FIN_WAIT2 504 
      TIME_WAIT 1057 (处理完毕,等待超时结束的请求数) 
       
      状态:描述 
      CLOSED:无连接是活动的或正在进行 
      LISTEN:服务器在等待进入呼叫 
      SYN_RECV:一个连接请求已经到达,等待确认 
      SYN_SENT:应用已经开始,打开一个连接 
      ESTABLISHED:正常数据传输状态 
      FIN_WAIT1:应用说它已经完成 
      FIN_WAIT2:另一边已同意释放 
      ITMED_WAIT:等待所有分组死掉 
      CLOSING:两边同时尝试关闭 
      TIME_WAIT:另一边已初始化一个释放 
      LAST_ACK:等待所有分组死掉

通过端口查进程,通过进程查端口?

  • linux下通过进程名查看其占用端口

    1. 先查看进程pid:ps -ef | grep 进程名
    2. 通过pid查看占用端口:netstat -nap | grep 进程pid

linux通过端口查看进程:netstat -nap | grep 端口号

说说僵尸进程和孤儿进程。

  1. 孤儿进程:一个父进程退出,子进程还在运行,子进程就会成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
  2. 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用waitwaitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。

文章作者: 千羽
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 千羽 !
评论
  目录