面试--每日一经

操作系统

死锁

死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
 
死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个进程使用;
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
  • 不可剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺;
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

并发与并行

并发是指在一段时间内,宏观上来看,多个进程都得到了执行,但微观上,某一个时刻,只有一个进程在占用CPU资源;

并行 是指,宏观上,多个进程在一段时间内都得到了执行,同时微观上,每个时刻也都有多个进程在被执行(占用CPU资源),前提是有多个CPU。

进程与线程

进程是操作系统分配资源的单位,线程是调度的基本单位,线程之间共享进程资源

进程

进程:进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程。进程是一个正在执行的程序的实例,包括程序计数器、寄存器和程序变量的当前值。
进程包括三大部分:代码段、数据段、和进程控制块。

进程的五大特性

  • 并发性:各个进程是并发执行的,并不是一个进程执行完在执行另一个进程。
  • 独立性:不同的进程只完成自己特定的任务,进程之间的工作不会相互影响。
  • 动态性:进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。
  • 制约性:一、计算机资源是有限的,而计算机中的进程有很多,进程之间就只能相互制约争夺某些资源;二、某些进程具有亲缘关系或者依赖关系或者因访问共享数据/资源或进程间同步而产生制约。
  • 结构性:每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制块三部分组成的。

进程控制块PCB
存放进程的管理和控制信息的数据结构称为进程控制块。
其作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程。

进程与程序的关系

  • 1.程序是进程产生的基础,进程是程序的载体

  • 2.程序每次运行构成不同的进程

  • 3.进程是程序功能的具体体现

  • 4.通过多次执行,一个程序可以对应多个进程;通过调用关系,一个进程可以包括多个程序

进程和程序间的区别

  • 进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行,进程有核心态/用户态
  • 进程是暂时的,程序是永久的:进程是一个状态变化的过程,程序可长久保存
  • 进程与程序的组成不同:进程的组成包括程序,数据和进程控制块(进程的状态信息)

线程

线程: 是进程的一个执行流程,一个进程可以由多个线程组成。一个进程可以同时运行多个不同的线程,每个线程完成不同的任务。
线程是操作系统能够进行运算调度的最小单位。

线程特点

  • 共享: 在同一进程中的各个线程,都可以共享该进程所拥有的资源。
  • 并发: 同一进程或者不同进程间的多个线程可以并发运行充分利用和发挥了处理机与外围设备并行工作的能力。
  • 轻量: 线程的实体由TCB,程序,数据构成,基本不拥有系统资源,只有少量必不可少的能够保证自身独立运行的资源。
  • 独立调度和分派的基本单位: 在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。

线程与进程的区别

  • 本质区别: 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位。

  • 包含关系: 一个进程至少有一个线程,线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

  • 资源开销: 每个进程都有独立的地址空间,进程之间的切换会有较大的开销;线程可以看做轻量级的进程,同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

  • 影响关系: 一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮。

计算机网络

三次握手与四次挥手

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。

  • 三次握手: 为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。
  • 四次挥手: 即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

数据结构

C++

C与C++中struct的区别,class与struct的区别

C++ 中保留了C语言的 struct 关键字,并且加以扩充。在C语言中,struct 只能包含成员变量,不能包含成员函数。而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。
C++中的 struct 和 class 基本是通用的,唯有几个细节不同:

  • 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
  • class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
  • class 可以使用模板,而 struct 不能。

C++面向对象的四大特性

一般将面向对象是三大特性,封装、继承、多态,也可以把抽象作为另一大特性。

  • 封装: 隐藏对象的属性和实现细节,仅对外公开接口,控制程序堆类属性的读取和操作。
  • 继承: 子类可以继承父类的属性和行为,代码复用,不用额外编写。
  • 多态: 调用相同的函数,在不同的子类中具有不同的表现形式。
  • 抽闲: 数据抽象,过程抽象。数据抽象,关注于目标的特性信息;过程抽象,关注于目标的功能是什么,而不是功能是怎么实现的。

指针与常量

  • 指针常量: 指针在前,常量在后,表示指针类型的常量,指针不能指向别的内存,但指向的内容可以修改。
  • 常量指针: 常量在前,指针在后,表示指向常量类型的指针,指针可以修改指向的地址,但是指向的内容不能修改。

虚函数(virtual)底层实现原理

每一个包含有虚函数的类都有一个与之对应的虚函数表(virtual table),它是由该类所有的虚函数对应的函数指针构成的数组,同时对象还携带一个虚表指针(virtual table pointer),当对象调用一virtual函数,实际被调用的函数取决于该对象的虚表指针所指向的虚表中,编译器在其中寻找适当的函数指针。

程序运行时内存分区

程序运行是内存分区主要分为BSS段、数据段、代码段、堆、栈。

  • BSS: 一般是指存放程序中未初始化的全局变量的一块内存区域。BSS段属于静态内存分配。
  • 数据段 一般是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
  • 代码段: 通常是指用来存放程序执行代码的一块内存区域,包含只读常量。
  • 栈: 存放非静态局部变量,以及函数调用时函数参数、返回值。
  • 堆: 放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。

堆和栈的区别

从数据结构层面,堆和栈均是一种简单的数据结构。

  • 堆是满足父子节点具有一定大小关系的完全二叉树(例如:大根堆,父节点权值大于子节点权值),可以支持对数级别的插入与删除。
  • 栈是一种先入后出的简单数据结构。

从操作系统层面,堆和栈都与资源分配有关

  • 堆中存放的是程序员申请的内存,在空间上可能不连续,一般是在程序执行过程中动态分配的内存。
  • 栈中存放的是系统自动分配的一块连续的内存空间,其大小在编译期时由编译器参数决定,存放非静态局部变量,和函数调用栈的保存,包含函数参数、返回值。
  • 栈由系统自动分配,速度较快,而堆空间是程序员手动分配,使用更加灵活。

Mutex与Lock_guard

  • mutex:
    • C++ 标准库提供的互斥锁类,用于实现对共享资源的互斥访问
    • 在需要保护共享资源的代码段前后分别调用 lock() 和 unlock() 成员函数来上锁和解锁互斥锁
  • lock_guard:
    • 它提供了一种方便的方式来管理互斥锁的上锁和解锁。
    • 在创建时自动上锁互斥锁,在其作用域结束时自动解锁互斥锁,无需手动调用 lock() 和 unlock()。
  • unique_lock:
    • 另一种互斥锁管理类,提供更多灵活的性能和功能,适用于更复杂的线程。
    • 支持手动控制上锁、解锁,并且可以在持有锁的情况下进行更多的操作,如可延迟上锁、尝试上锁、超时上锁等

线程锁

C++ 实现线程锁的有互斥锁递归锁读写锁条件变量

  • 互斥锁(mutex):
    • 互斥锁是最常见的线程锁形式,用于实现互斥访问共享资源。
    • 同一时间只允许一个线程持有互斥锁,其他线程在尝试上锁时会被阻塞。
  • 递归锁(recursive_mutex):
    • 递归锁是互斥锁的一种变体,允许同一线程多次获得锁。
    • 当线程多次获取递归锁时,必须相应地释放相同数量的锁。
    • 递归锁可以防止同一线程在递归调用中出现死锁的情况。
  • 读写锁(shared_mutex):
    • 读写锁用于提高读操作的并发性,允许多个线程同时读取共享资源。
    • 当有线程进行写操作时,读写锁会互斥地排斥其他线程的读操作和写操作。
    • 适用于读多写少的场景,可以提高并发性能。
  • 条件变量(condition_variable):
    • 条件变量用于线程间的通信和同步,允许线程等待某个条件满足后再继续执行。
    • 通常与互斥锁一起使用,以确保线程等待和唤醒的正确顺序。
    • 条件变量可以用于实现线程的等待、通知和超时等操作。

静态多态与动态多态

  • 静态多态: 依靠重载和模板实现,是指在编译器编译器间就指定类型。
  • 动态多态: 依靠继承与虚函数实现,在程序运行期间才能确定类型。

右值引用与左值引用

  • 左值: 最早右值是指能出现在复制语句左侧的值,即变量、解除引用的指针,程序可以获取其地址。
  • 右值: 可出现在复制表达式右边,但不能对齐应用地址运算符的值,包括字面常量、表达式(x+y)或者返回值的函数(该函数返回的不是引用)。
  • 左值引用: 标识符关联到左值(传统引用)。
  • 右值引用: C++1新增特性,使用 && 表示,可关联到右值,

移动语义与右值引用

  • 移动语义: 是指将某个对象持有的资源或者内容转移给另一个对象,在转移资源后被移动对象处于“有效但未定义”状态,即对象没有析构,可以作为普通对象使用此为 “有效” ,但内容不见了,不能继续访问,此为 “未定义”
  • 右值引用: 为了实现移动语义,在进行赋值运算或者构造函数时,避免深层拷贝,设置函数参数类型为右值引用,调用函数时可以使用右值作为参数,实现移动语义,一般情况只有右值才能实现右值应用,可以使用(std::move())函数将左值强制转换为右值。

赋值构造函数与移动构造函数

  • 赋值构造函数: 是类的常见构造函数,使用const左值引用作为参数,这个引用关联到左值实参(如果传入的是右值,则会在函数调用是临时创建一个左值,用传入的右值对其赋值,函数中左值参数可以关联到临时左值的地址,函数结束后,临时左值销毁),可以实现深拷贝。
  • 移动构造函数: 使用左值引用作为参数,没有const修饰符,该引用关联到右值实参,将所有权转移给新的对象,可以修改实参,实现浅拷贝。结束后需要将实参指向nullptr指针,但不是调用析构函数,防止多次内层释放。
  • move函数: 将左值强制转化为右值,实现移动语义。

static_cast、const_cast、dynamic_cast、reinterpret_cast

C++ 提供了static_cast、const_cast、dynamic_cast、reinterpret_cast四种强制类型转换的方式。

  • static_cast: 传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换,例如non-const对象转为const对象。
  • const_cast: 该运算符用来修改类型的const(唯一有此能力的C+±style转型操作符)或volatile属性。除了const 或volatile修饰之外, new_type和expression的类型是一样的。
  • dynamic_cast: 用于执行安全的下行类型转换,通常用于类层次结构中的多态类型转换。
  • reinterpret_cast: 用于低级别的类型转换,主要用于指针类型的转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。

静态编译与动态编译

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值