c++面经汇总(操作系统)

本文详细阐述了进程与线程的概念,探讨它们在并发中的角色,以及进程与线程的同步机制。重点讲解了内存结构、进程线程通信方式、操作系统内存管理、进程线程区别,以及为何需要线程而非仅进程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说一下进程与线程的概念,以及为什么要有进程和线程,他们各自又是怎样同步的
  1. 基本概念:
    进程是对运行时程序的封装,是操作系统进行资源调度和分配的基本单位,实现了操作系统的并发
    线程是进程的子任务,是cpu调度和分派的基本单位,实现了进程内部的并发;
    线程是操作系统可以识别的最小执行单位和调度单位,每个线程完成不同的任务,但是线程之间共享同一内存单元;

  2. 区别

    1. 进程是操作系统进行资源调度和分派的基本单位,一个进程可以拥有多个线程,多个线程实现了进程内部的并发
    2. 一个线程只能属于一个进程,线程是依赖进程而存在的
    3. 进程在执行的过程中拥有独立的内存单元,但是同属于一个进程的不同线程之间是共享所属进程的内存资源,包括有进程的代码段;数据段(全局变量和静态变量);扩展段(堆存储);进程的公有数据(利用这些公有数据,可以实现进程之间的相互通信); 但是每一个线程都拥有自己独立的栈,栈又叫做运行时段,存放的是局部变量和临时变量
    4. 系统开销:由于进程在创建和销毁时候,系统都会位置分配或者回收资源,因此,进程在创建的时候,操作系统所付出的开销要远大于线程的创建与销毁,另一方面当进程被撤销的时候,会涉及到当前进程CPU环境的保存以及新的进程创建时,CPU环境的设计;而线程在切换的时候,只需要保存少量的寄存器的内容;所以就切换来说,进程的开销也要大于线程的开销
    5. 通信:进程之间的通信很复杂,下面会讲,而线程由于同属于一个进程,那么所具有的地址空间也是相同的,因此进行通信的时候,可以直接读取进程的资源,比较容易;
进程之间的通信方式

概述:进程之间的通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及socket套接字

  1. 管道:管道包括 普通管道、命名管道
    1. 普通管道 :普通管道是半双工的,即只能向一个方向进行数据的流动,具有固定的读写端;普通管道只能用作具有血缘关系的进程之间的通信(父子进程,兄弟进程)
    2. 命名管道FIFO:其相对于普通管道来说具有的优势是,不仅仅在具有血缘关系的进程中进行通讯,无关的进程之间也是可以的;FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中;
  2. 系统IPC
    1. 消息队列 :
      1. 一个消息队列由一个标识符来进行标记(消息队列克服了信号传递信息少,(管道只能承载无格式字节流以及缓冲区大小受限的特点),具有写的权限的进程,按照一定的规则,向消息队列中添加消息,具有读消息队列的权限的进程则可以从队列中读取消息
      2. 特点:
        1. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
        2. 消息队列独立于发送与接受进程,当进程终止的时候,队列中的内容并不被删除
        3. 消息队列可以实现随机消息的查询,并不一定按照消息先进先出或者其他的固定规则进行查询
    2. 信号量 (信号量控制的是堆共享资源进行访问的数量)
      1. 特点:
        1. 信号量用于进程间同步,如果要在进程之间传递数据需要结合共享内存
        2. 信号量集于系统的PV(对信号量进行加减的操作,以实现进程对资源的互斥使用)操作,程序堆幸好两的操作都是原子操作
        3. 每次对信号量的PV操作不仅仅限于对信号量的+1-1,也可以加减任意整数
    3. 信号signal(是一种比较负责的通讯方式,用来通知接收进程某个时间已经发生)
    4. 共享内存(多个进程可以访问的同一块内存,不同进程可以及时看到对方进程中对共享内存中数据的更新,这种方式一般要和某种同步一起操作,如信号量)
      1. 特点:共享内存是最快的一IPC,因为进程可以直接对内存进行读取
      2. 由于可以多个进程共同操作,因此需要进行同步
      3. 信号量与共享内存一般结合使用
  3. 套接字socket :它不同于其他的通信方式是因为,他是主机之间的进程通信;
线程之间的通讯方式
  1. 临界区:通过多线程的串行化来访问公共资源或者一段代码,速度快,适合控制数据访问;
  2. 互斥锁 只有具有互斥锁对象的线程,才能访问公共资源,由于互斥对象只有一个,因此可以保证公共资源不会被多个线程同时访问;
  3. 信号量:用来控制有限的线程数目来同时访问一个资源
请你说一说操作系统中的程序的内存结构

在这里插入图片描述
一个程序的本质上都是由BSS段、data段、text段,三个段组成的,可以看到一个可执行程序在存储时分为代码段、数据区、和未初始化数据区三部分。

BSS(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域,BSS段属于静态分配,程序结束后静态变量资源由系统自动释放

数据段:存放程序中已经初始化的全局变量的一块内存区域,数据段也属于静态内存分配;

代码段:存放程序执行代码的一块内存区域,这部分区域的大小在程序运行 前就已经确定,并且内存区域属于制度,在代码段中,也可能包含一些只读的常数变量;

注意:

  1. bss段的内容并不存放在磁盘上的程序文件中,其原因时内核在程序开始运行前将他们设置为0,需要存放在程序文件中的只有正文段和初始化数据段;
  2. data段为数据分配空间,数据保存到目标文件中
  3. 数据段包含经过初始化的全局变量以及他们的指,Bss段的大小通过可执行文件中的到,然后根据得到的这个内存的大小,在数据段之后分配一个同样大小的内存段容纳Bss段,然后Bss段和数据段合并称之为数据段
  4. 可执行文件在运行期间又多出来两个区域,堆区和栈区
    1. 栈区:栈区由系统自动分配与释放,存放的是局部变量与函数参数,当一个函数调用的时候,函数的地址首先放在开辟的栈中(每一个函数都有自己对用的栈)其次是函数的返回地址入栈,然后开始执行函数的逻辑,函数中的局部变量与函数参数,都在栈中进行分配与释放,当函数执行结束之后,函数地址出栈,返回后函数的返回地址出栈,栈区的地址是从高地址向低地址增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,因此,当申请的栈的空间超过这个预设的值,就会发生栈溢出,这也就是为什么,如果递归的层次太多的话,会导致栈溢出
    2. 堆区:堆区是程序员进行申请与释放的(通过new/malloc进行申请,通过delete与free进行释放,一定要成对出现)他的地址是从低地址到高地址进行增长的;
操作系统中的缺页中断

操作系统发生缺页中断是应为当充许远在使用malloc与mmap等内存分配的函数时,分配的时候,仅仅时建立了进程的虚拟地址空间,并没有分配虚拟内存对应的物理地址,当进程访问这些没有建立映射的虚拟地址的时候,触发器自动触发一个缺页异常(通俗点来讲,在使用内存分配函数的时候(malloc、mmap)系统最初仅仅是知道要分配参数那么大的地址,但是还没有真正的分配,也就是只有空间,但是还没有地址,因此这个时候就会产生一个缺页中断,然后通过缺页中断的机制,进行一个地址的映射,使被分配内存者拥有真正的地址);

缺页中断:在请求分页的系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中,每当所要访问的页面不在内存中的时候,会产生一次缺页中断,然后操作系统将缺少的地址找到,调入内存

缺页中断所执行的操作:

  1. 保护CPU现场
  2. 分析中断原因
  3. 转入缺页中断程序进行处理
  4. 恢复CPU现场,继续执行
什么是并发,什么是并行

并发是指,在宏观上看,两个进程同时运行,但是在微观上看,实际上是每一个进程都只执行一个分配好的时间片

并行是指真正意义上的多个进程同时进行,比如现在的多核CPU,两个进程分别运行在两个核上,两者互不影响

操作系统的页表寻址

首先,由于现代的程序变得越来越大,因此如果要把某个程序全部装入内存的话,需要的时间太大了,另外,如果一个程序结束了,需要把他换出,并且把其他的程序在进行换入,这样的s时间开销太大了,因此就想到了使用虚拟内存,所谓的虚拟内存就是,并不真实存在的内存,操作系统通过将虚拟内存划分为相同大小的页,然后通过MMU(内存管理单元)进行虚拟内存到物理内存的映射,实际上,操作系统为每一个进程都维护了一个从虚拟地址到物理地址的映射关系的数据结构----页表,通过页表,找到页的基地址,然后通过基地址进行的偏移长度就可以找到真正的物理地址,这样的话就可以不用每次都全部的转入转出,而是在找不到的时候再进行,加快了效率,也方便了转入转出;

为什么有了进程之后还要有线程
  1. 进程有很大的缺陷就是,当某个进程在执行的时候如果被阻塞,那么整个进程就会被挂起,即使,在进程中有些工作并不需要等待的资源就可以工作
  2. 进程进行通信的时候,开销很大,而线程进行通信的时候,只需要保留某些寄存器的数据即可,粒度更小,更快捷
  3. 在新建进程的时候,需要分配给进程独立的空间,建立很多东西来维持它的代码段,数据段、bss段等,开销太大了,因此需要一个开销小,粒度也小的方式
在单核的机器上写多线程,是否需要考虑加锁

当然需要,多线程是指多个线程同时运行,只要涉及到共享资源的使用就需要加锁,实际上,多线程在执行的时候,并不是真正的同时运行,而是每一个线程执行一个时间片,当时间片消耗完后,将其挂起执行另外的线程,只不过时间片很小,所以在宏观上来看,是同时运行的,但是,不管它是怎样划分,怎样执行的,只要涉及到共享资源,就一定要加锁,不然会导致对共享数据的修改发生冲突;

线程间的同步方式
  1. 信号量:信号量 S>=0 时,S 表示可用资源的数量,执行一次P操作意味着请求分配一个资源,因此 S 的值减 1 ;信号量 S<0 时,表示已经没有可用资源,S 的绝对值表示当前等待该资源的进程数,请求者必须等待其他线程释放该类资源才能继续运行,而执行一个V操作意味着释放一个资源,因此 S 的值加 1 ;若 S<0 ,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。注意,信号量的值只能通过PV操作来改变。
  2. 互斥量 :互斥量又称互斥锁,主要用于线程互斥,当某线程进入临界区的时候,需要获得互斥锁并且加锁,离开的时候,需要解锁 ,以唤醒其他等待该互斥锁的线程
多线程与多进程的区别
  1. 进程是操作系统进行资源分配和调度的基本单位,线程是CPU进行分配和调度的基本单位
  2. 多线程共享同一个进程的地址空间,如果某一个线程意外终止,会导致同一个进程中的其他线程也被终止,但是进程之间不一样,每一个进程都拥有自己独立的空间,所以进程之间是不会相互影响的
  3. 线程通信方便,线程的创建、销毁、切换简单,在一个进程中,进程的创建、销毁、切换很复杂
  4. 进程的同步简单,线程的同步复杂
游戏服务器应该为每个用户开辟一个线程还是进程

上面已经说到了,如果开辟的是一个线程的话,在同一个进程中的一个线程意外终止,会影响当前进程的其他线程也会被终止,但是游戏中的实际情况是,玩家之间并不互相影响,因此应该开的是进程而不是线程

OS中的缺页置换算法
  1. FIFO:如果当前内存中并没有要找的页,那么就按照先进先出的顺序,将最先进入内存的页移出,将缺少的页移入
  2. 最近最少使用算法(LRU):找到最长时间没有使用过的页,将其移出,将缺少的页移入

缺页置换 :所谓的缺页置换就是,当访问的是内存中没有存在的页的时候(前提是内存空间已经满了,如果没满,直接调入缺少的页),从内存中调出某页,再调入需要的页的现象

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值