腾讯面试准备-2024.3.25
- 腾讯面试准备-2024.3.25
- 自我介绍
- C++11/14/17新特性
- C++11新特性
- C++14新特性
- C++17新特性
- struct和class的区别
- 进程状态
- 现代的流媒体通信协议栈
- 流媒体协议详解
- extern "C"
- 程序从编译到执行的过程
- 进程、线程、协程
- 进程
- 线程
- 协程
- 如何实现一个信号与槽系统?
- 进程间的通信方式
腾讯面试准备-2024.3.25
自我介绍
略。
C++11/14/17新特性
C++11新特性
- 自动类型推导(Type Inference):引入了 auto 关键字,可以根据初始化表达式自动推导变量的类型。
- 统一的初始化语法(Uniform Initialization):可以使用花括号 {} 初始化对象,无论是基本类型、数组、类对象还是容器。
- lambda 表达式:可以在代码中定义匿名函数,简化函数对象的创建和使用。
- 范围-based for 循环:用于遍历容器中的元素,提供了一种更简洁、安全的遍历方式。
- nullptr:引入了 nullptr 关键字,代表空指针,替代了传统的 NULL 宏。
C++14新特性
- C++14引入了通用lambda表达式,可以使用auto关键字作为参数类型和返回类型,使得lambda表达式更加灵活。
- C++14引入了二进制字面量,允许程序员使用二进制表示法来表示整数值。二进制字面量以前缀0b或0B开头,后面跟着一串二进制数字。例如,0b101010表示十进制数42。
- 数字分隔符使得大数字的可读性变得更高了。
C++17新特性
- 自从 C++11 起,C++ 就已经支持以 u8 为前缀的 UTF8 字符串字面量。C++17 进一步支持以 u8 为前缀的 UTF8 字符字面量。比如:char c = u8’6’。
- C++17 之前,在类中定义的非 const 静态变量,需要在类的外面进行初始化。C++17 引入了内联变量的概念,可以直接在类中定义并初始化非 const 静态变量。
- C++17 中,if 和 switch 语句允许我们在条件表达式里声明一个初始化语句。
- C++17 终于将文件系统纳入标准中,提供了关于文件系统的很多功能,基本上应有尽有。比如:创建目录、拷贝文件、判断文件是否存在等等。
struct和class的区别
- 默认访问权限:结构体的成员默认访问权限是公共的(public),类的成员默认访问权限是私有的(private)。
- 成员函数:类可以包含成员函数,这些函数可以操作类的私有成员,并且可以实现类的行为和功能。结构体也可以有成员函数,但是它们的主要目的是为了实现一些操作,而不是定义类似于类的行为。
- 继承:类可以通过继承实现子类与父类之间的关系,可以使用公共、保护或私有继承来控制成员的访问权限。结构体也可以继承,但由于其成员默认是公共的,继承可能导致访问权限问题。
- 构造函数和析构函数:类可以拥有构造函数和析构函数,用于对象的初始化和清理。结构体没有默认的无参构造函数,且只能声明有参的构造函数,没有析构函数,它们的使用场景通常是比较简单的数据封装。
- 默认成员访问标签:在类中,可以使用访问标签(public、private、protected)来指定成员的访问权限。在结构体中,无法使用访问标签来指定成员的访问权限,所有成员都默认是公共的。
- class是引用类型,struct是值类型。作为参数传递时,class变量以按址方式传递,而struct变量是以按值方式传递的。
- class实例由垃圾回收机制来保证内存的回收处理,而struct变量使用完后立即自动解除内存分配。
进程状态
现代的流媒体通信协议栈
流媒体协议详解
-
RTP (Real-Time Transport Protocol) 实时运输协议
-
RTSP (Real-Time Streaming Protocol) 实时流式协议
-
RTCP (RTP Control Protocol) 实时运输控制协议
-
RTMP (Real Time Message Protocol) 实时消息协议
-
SIP (Session Initiation Protocol) 会话初始协议
-
SDP (Session Description Protocol) 会话描述协议
-
WebRTC (Web Real-Time Communications)
-
WebSocket:WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
extern “C”
extern "C"
是C++特有的指令(C无法使用该指令),目的在于支持C++与C混合编程。
extern “C”
的作用是告诉C++编译器(如g++)用C规则编译指定的代码。
程序从编译到执行的过程
一个程序,从编写完代码,到被计算机运行,总共需要经历以下四步:
- 编译:编译器会将程序源代码编译成汇编代码。
- 汇编:汇编器会将汇编代码文件翻译成为二进制的机器码。
- 链接:链接器会将一个个目标文件和库文件链接在一起,成为一个完整的可执行程序。
- 载入:加载器会将可执行文件的代码和数据从硬盘加载到内存中,然后跳转到程序的第一条指令处开始运行。
进程、线程、协程
进程
进程是资源分配的最小单位,是最小的资源管理单元。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
当程序需要运行时,操作系统将代码和所有静态数据记载到内存和进程的地址空间(每个进程都拥有唯一的地址空间,见下图所示)中,通过创建和初始化栈(局部变量,函数参数和返回地址)、分配堆内存以及与IO相关的任务,当前期准备工作完成,启动程序,OS将CPU的控制权转移到新创建的进程,进程开始运行。
PCB(Processing Control Block):操作系统对进程的控制和管理通过,PCB通常是系统内存占用区中的一个连续存区,它存放着操作系统用于描述进程情况及控制进程运行所需的全部信息(进程标识号、进程状态、进程优先级、文件系统指针以及各个寄存器的内容等),进程的PCB是系统感知进程的唯一实体。
一个进程至少具有5种基本状态:初始态、执行状态、等待(阻塞)状态、就绪状态、终止状态。
- 初始状态:进程刚被创建需要申请一个空白PCB,向其中填写控制和管理进程的信息,完成资源分配。此时进程刚被创建,由于其他进程正占有CPU所以得不到执行,只能处于初始状态。
- 就绪状态:进程已经准备好,且已分配到所需资源,只要分配到cpu就能立即运行。
- 执行状态:进程处于就绪状态的经过调度才能到执行状态。任意时刻处于执行状态的进程只能有一个。
- 阻塞状态:正在执行的进程由于某些事件(IO请求,申请缓存区失败)而暂时无法运行,进程受到阻塞,在满足请求时进入就绪状态等待系统调用。
- 终止状态:进程结束或出现错误或系统终止,进入终止状态,无法再执行。
进程同步机制的主要任务是对多个相关的进程在执行次序上进行协调,使并发执行的诸多进程之间能够按照一定的规则共享系统资源,并能很好的相互合作,从而使程序之间的执行具有可再现性。
进程间的两种制约关系:
-
间接相互制约(互斥):因为进程在并发执行的时候共享临界资源而形成的相互制约的关系,需要对临界资源互斥地访问。
-
直接制约关系(同步):多个进程之间为完成同一任务而相互合作而形成的制约关系。
临界资源:同一时刻只允许一个进程可以访问的资源。各个进程之间采取互斥方式,实现对临界资源的共享。
临界区:进程中访问临界资源的那段代码。
同步机制应遵循的规则:
-
空闲让进:当临界区的“大门”敞开时,应当允许一个请求的进入临界区的进程立即进入临界区。
-
忙则等待:当临界区的“大门”关闭时,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问。
-
有限等待:对要求进入临界区的进程,应保证有限的时间能进入自已的临界区,以免陷入“死等” 状态。
-
让权等待:当进程不能进入自已的临界区时,应立即释放处理机,以免进程陷入“忙等”状态。
“忙等 ”和 “死等” 都是没能进入临界区,它们的区别如下:
- 死等: 对行死等的进程来说,这个进程可能是处于阻塞状态,等着别的进程将其唤醒(signal 原语),但是如果唤醒原语一直无法执行,对于阻塞的进程来说,就是一直处于死等的状态,是无法获得处理机的。
- 忙等:忙等状态比较容易理解,处于忙等状态的进程是一直占有处理机去不断的判断临界区是否可以进入,在此期间,进程一直在运行,这就是忙等状态。有一点需要注意的是,忙等是非常可怕的,因为处于忙等的进程会一直霸占处理机的,相当于陷入死循环了。 忙等的状态在单CPU 系统中是无法被打破的,只能系统重启解决。
线程
线程是CPU调度的最小单位,是最小的执行单元。 线程又叫做轻量级进程,线程从属于进程,是程序的实际执行者。
一个进程至少包含一个主线程,也可以有更多的子线程。多个线程共享所属进程的资源,同时线程也拥有自己的专属资源、拥有自己的栈空间。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
一个进程中的各个线程都有共享的资源而且是完全开放的,那么在进程运行中会出现多个线程访问同一个公共资源的问题。这种现象我们称之为线程之间产生了资源竞争,这种竞争会导致程序异常甚至崩溃。线程同步有四种方法:
- 互斥锁:互斥锁又称互斥体或互斥量,是最简单的一种线程同步机制。当一个线程访问的时候,就会把资源“锁”上,直到访问结束才会解锁,给其他线程访问。互斥锁本身就是一个全局变量:unlock 和 lock。unlock表示当前资源可以访问,第一个访问资源的线程将互斥锁修改为lock,访问完以后再修改为unlock;lock表示线程正在访问资源,其他线程需要等待互斥锁的值为unlock才能继续访问。该线程负责的加锁,解锁也需要该线程。
- 信号量:信号量控制同时访问公共资源的线程数量,取值范围必须>=0,信号量可以执行加一和减一的原子操作。当线程访问资源时,信号量减一,访问完成加一。信号量为0时候,其他访问线程需要等待,直到大于0。
- 条件变量:功能类似现实中的门,只有打开和关闭两种状态,一旦关闭,所有线程都不得访问该资源,一旦打开,那就恢复执行。条件变量的本质也是全局变量,它的功能是阻塞线程,直到收到条件成立的信号,被阻塞的程序才能继续执行。为了避免多线程抢资源的情况发生,条件变量必须和互斥锁搭配使用。
- 读写锁:如果很多线程只是进行读取操作,只有少部分是写操作(修改),可以使用读写锁。当有多个读线程的时候,他们可以同时访问,但是写线程就必须要等他们读完才能访问,反过来也是,只不过写线程必须要一个一个来访问。
线程通信就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。线程通信主要可以分为三种方式:
- 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信。
- 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。wait/notify等待通知方式、join方式。
- 管道流:管道输入/输出流的形式。
协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。一个线程可以拥有多个协程,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
如何实现一个信号与槽系统?
设计模式-观察者模式。
详见于:设计模式:观察者模式(Observer)
进程间的通信方式
-
管道(pipe):管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据。它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。它只能用于具有亲缘关系的进程之间的通信(父子进程或者兄弟进程之间)。它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
-
命名管道(named pipe):命名管道(FIFO)又叫有名管道,和无名管道的主要区别在于,命名管道有一个名字,命名管道的名字对应于一个磁盘索引节点,但没有数据块,有了这个文件名,任何进程有相应的权限都可以对它进行访问。命名管道也是半双工的通信方式,允许无亲缘关系进程间的通信。FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
-
信号(signal):信号是一种比较复杂的通信方式,是在软件层次上对中断机制的一种模拟,它是一种异步通信方式,也是进程通信中唯一一个异步的通信方式,用于通知进程有某事件发生。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它。如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
-
消息队列(message queue):一个保存在内核中消息的链表,存放在内核中并由消息队列标识符(即队列ID)标识。消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
-
共享内存(shared memory):指两个或多个进程共享一个给定的存储区,这段共享内存由一个进程创建,被两个或两个以上的进程映射至自身的地址空间中,都可以使用这个共享内存,从而实现了进程间的通信。共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。这种方式需要依靠某种同步操作,我们通常会用信号量来实现对共享内存同步访问控制。
-
信号量(semaphore):信号量本质上是一个计数器,它和管道有所不同,它不以传送数据为主要目的,主要作为进程之间及同一种进程的不同线程之间的同步和互斥手段,它常作为一种锁机制,可以用来控制多个进程对共享资源的访问,防止某进程正在访问共享资源时,其他进程也访问该资源,使得资源在一个时刻只有一个进程独享。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。因为信号量属于临界资源,为了保护临界资源,信号量进行PV操作时都为原子操作。
-
套接字(socket):套接字是一种通信机制,采用客户/服务器方式,可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。套接字的特性由3个属性决定:域、类型和协议。
详见于:《进程间的通信方式》