面试整理

tech2023-07-06  131

什么是协程?

进程:CPU分配资源的最小单位,进程拥有代码和打开的文件资源、数据资源、独立的内存空间。 进程的切换内容:页全局目录、内核栈、硬件上下文。线程是后面的第二个和第三个。协程只有硬件上下文。 线程:从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程,线程拥有自己的栈空间。 线程拥有5种状态:初始化、可运行、运行中、阻塞、销毁。 对操作系统来说,线程是最下的执行单元,进程是最小的资源管理单元。进程和线程都是操作系统所管理的。 协程不是被操作系统内核所管理,而完全是由程序员所控制的(在用户态执行)。这样带来的好处就是性能得到很大的提升,不会像线程那样需要上下文切换来耗费资源。 协程的好处: 缺点:无法利用多核资源、进行阻塞操作会阻塞掉整个程序。 跨平台、跨体系架构、无需线程上下文切换。无需原子操作锁定及同步开销。方便切换控制流、高并发+高拓展+低成本。

什么是0拷贝技术?

扣的别人博客上的图。当应用程序访问某块数据时,操作系统首先会检查,是不是最近访问过此文件,文件内容是否缓存在内核缓存区。如果是,OS直接根据read系统调用提供的buf地址,将内核缓存区的内容拷贝到buf所指定的用户空间中去。如果不是,操作系统则首先将磁盘上的数据拷贝到内核缓存区,这一步主要依靠DMA传输,然后再把内核缓冲区上的内容拷贝到用户缓存区中。wirte函数再把用户缓冲区的内容拷贝到网络堆栈相关的内核缓冲区中,最后socket再把内核缓冲区的内容发送到网卡上。

DMA用来处理与硬件的通讯,CPU仍然需要处理两次数据拷贝,与此同时,在用户态与内核态也发生了多次的上下文切换,加重了CPU的负担。

让数据传输不需要经过user space 使用mmap来代替read调用,可以减少一次读数据时候的拷贝,在文件很大的时候,是能够提高效率的。 当你的程序map了一个文件,但是当这个文件被另一个进程截断时,write系统调用会因为访问非法地址而被sigbus终止。 解决方式:为SIGBUS信号建立信号处理程序;使用文件租借锁,在mmap文件之前加锁,并且在操作完文件后解锁。使用sendfile 只能将文件传递到套接字上去,反之则不行。不仅减少了拷贝次数还减少了上下文切换,数据传送只发生在内核空间使用splice 该系统调用在两个文件描述符之间移动数据,而不需要在内核空间和用户空间来回拷贝,splice调用利用了Linux提出的管道缓冲区机制, 所以至少一个描述符要为管道。

TCP为什么要三次握手?

A:客户端 B:服务端 考虑一种正常的情况,A发送了一个请求连接,但因为连接请求报文丢失而未收到确认。于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕就释放了连接。A总共传了两个请求连接,第一个丢失,第二个到达了B。现在假定出现一种情况A发出了一个请求报文并没有丢失,而是在某些网络节点长时间滞留了,以至于延误到连接释放以后的某个时间才能够到达B。本来这是一个已经失效的连接。但B收到连接之后,误以为A又发起了一次新的连接。于是向A发出确认的报文段,同意建立连接。假定不采用三次握手,那么B只要发送了确认,新的连接就建立了。由于现在A并没有发出连接请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的连接已经建立了,并一直等待A发来数据。B的许多资源就这样浪费掉了。

补充问题:为什么A在time-wait必须等待2MSL?

为了保证A发送的最后一个ACK报文段能够到达B 因为A发送的ACK报文有可能会丢失,B就会重传FIN+ACK,A在2MSL(最长报文段寿命) 中收到之后,再重传一次确认,然后B进入到close状态。如果没有2MSl 的话,B就无法按照正常的步骤进入CLOSE状态。防止“已失效的连接请求报文”出现在本连接中。

说一说模板

模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。模板是创建泛型类或者函数的蓝图或者公式。 库容器,比如迭代器和算法,都是泛型编程的例子。您可以使用模板来定义函数和类。 类模板,type是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。

类模板,可以定义相同的操作,拥有不同数据类型的成员属性。函数模板。

C11的特性了解嘛?

auto 自动类型推导 函数返回值的占位符decltypefinal指定某个虚函数不能在子类中覆盖,或者某个类不能被子类继承override指的是一个虚函数覆盖另一个虚函数列表初始化 非静态成员、构造函数、return语句中的列表初始化。nullptr(重载的时候NULL会造成语义模糊)范围for语句Lambda 能够捕获作用域中的变量的无名函数对象。 [capture list] (params list) mutable exception-> return type { function body } 捕获外部变量列表,参数列表,指示是否可以修改外部变量,异常,返回值,函数体 有值捕获、引用捕获、隐式引用捕获,值捕获在函数体中不能改变变量的值,除非使用mutable变量。右值引用move将某一范围内的元素移动到一个新的位置 #include <iostream> #include <utility> #include <vector> #include <string> int main() { std::string str = "Hello"; std::vector<std::string> v; // 使用 push_back(const T&) 重载, // 表示我们将带来复制 str 的成本 v.push_back(str); std::cout << "After copy, str is \"" << str << "\"\n"; // 使用右值引用 push_back(T&&) 重载, // 表示不复制字符串;而是 // str 的内容被移动进 vector // 这个开销比较低,但也意味着 str 现在可能为空。 v.push_back(std::move(str)); std::cout << "After move, str is \"" << str << "\"\n"; std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n"; }

关于引用

引用必须被初始化为指代一个有效的对象或者函数,不存在void的引用,也不存在引用的引用。引用不是对象,它们不必占用存储,所以不存在引用的数组,不存在指向引用的指针,不存在引用的引用。引用坍塌:右值引用的右值引用坍塌成右值引用,所有其他组合均坍塌成左值引用左值引用可以建立既存对象的别名;可以在函数调用中实现按引用传递语义;当函数的返回值是左值引用时,函数调用表达式成为左值表达式右值引用可以用于为临时对象延长生存期。当函数同时具有右值引用和左值引用的重载是,右值引用绑定到右值,左值引用绑定到左值转发引用 被声明为同一函数模板的类型模板形参的无CV限定的右值引用,auto&& (但当其从花括号的初始化器列表推导时不是)垂悬引用 引用一旦初始化就指向一个对象或者函数,被指对象的生存期结束,但引用仍保持访问,访问这种引用时未定义行为。
最新回复(0)