面试:stl、操作系统等查漏补缺(四)

stl扩容:

vector相关:

vector扩容,linux下是2倍率扩容,vs是1.5倍扩容。扩容的时候,会将原先的数据区给释放掉,开辟一段新的内存区域,将所有的值复制到另一端内存当中。会造成迭代器失效的问题。1.5倍扩容机制有一个好处就是能够,复用之前释放的内存。

map与unordered_map的扩容机制:

在 C++ 中,std::map 是基于**红黑树(Red-Black Tree)**实现的关联容器,其底层数据结构与哈希表(如 std::unordered_map)不同。因此,std::map 的“扩容”机制与哈希表的扩容有本质区别。

std::map 的实现基于平衡二叉搜索树(红黑树),其特性如下:

  • 动态节点分配:每个键值对作为一个树节点(Node动态分配在堆内存中。

  • 自动平衡:插入或删除操作后,红黑树通过旋转和重新着色维持平衡,确保操作时间复杂度为 O(log N)

  • 无需显式扩容:树的节点按需分配,没有固定容量的概念,也不会像哈希表那样触发“扩容-重新哈希”的过程。

std::unordered_map扩容:

  1. 触发条件
    当插入元素后,元素数量超过 负载因子(load factor) × 桶数量(bucket_count) 时触发扩容。

    • 默认负载因子为 1.0,可通过 max_load_factor(float) 调整。

  2. 扩容过程

    • 分配新的更大的桶数组(通常为原大小的两倍左右)。

    • 将所有元素重新哈希到新桶中(时间复杂度 O(N))。

  3. 优化手段

    • 提前调用 reserve(n) 预分配足够的桶,避免多次扩容。

对比:

特性std::map(红黑树)std::unordered_map(哈希表)
底层结构平衡二叉搜索树桶(数组) + 链表/红黑树(解决冲突)
动态调整方式节点动态分配,插入时调整树结构桶数组扩容,重新哈希所有元素
时间复杂度插入/删除/查找:O(log N)平均 O(1),最坏 O(N)(哈希冲突严重时)
内存连续性节点分散在堆内存中桶数组连续存储,链表节点分散
扩容触发条件无显式扩容,按需分配节点元素数量超过 负载因子 × 桶数量
扩容代价每次插入可能触发树的再平衡(局部调整)触发全局重新哈希,代价较高

private跟protected的区别

private:访问范围限定于成员类的内部。子类无法访问父类的private成员。外部类也不能访问。实现完全封装,隐藏实现的细节。

protected:定义在该成员的类内部。所有子类都可以访问。允许子类扩展父类的功能。

特性privateprotected
类内部✔️✔️
子类✔️
同一包的其他类✔️(如 Java/C#)
不同包的其他类❌(除非是子类)
封装性完全隐藏部分暴露给子类和同一包
  • 用 private:隐藏实现细节(如敏感数据、工具方法)。

  • 用 protected:允许子类重写或扩展,但不向外界公开(如模板方法模式)。

 map与unordered插入元素之后,迭代器会失效嘛?

map:红黑树结构,他的插入操作通过旋转和调整结点指针来维护平衡,不会改变已有节点的内存地址。即使插入的元素重新平衡了,迭代器也不会失效。唯一失效的场景就是删除元素(被删除元素的迭代器会失效)。

unorderedmap:底层结构是hash+桶或链表的方式。如果插入元素的时候导致扩容机制,那么所有的迭代器会失效。hash表会分配新桶并重新散列所有的元素,原有的布局会被破坏。如果没触发rehash的话,所有的迭代器都会有效,除了新元素所在的桶可能会失效

特性std::mapstd::unordered_map
插入后迭代器失效不会失效仅当触发 rehash 时失效
底层结构红黑树哈希表
内存稳定性高(节点地址固定)低(rehash 后地址全变)
避免失效的策略无需特殊处理预分配桶(reserve())避免 rehash

总结

  • std::map 的插入操作是迭代器安全的。

  • std::unordered_map 的插入操作仅在触发 rehash 时使迭代器失效,可通过预留桶容量规避。

  • 在需要高频插入且依赖迭代器稳定性的场景中,优先选择 std::map

uniqueptr move到sharedptr会出现什么?

会导致所有权的转移和内存管理方式的改变。

unique_ptr :独占资源所有权,不可复制,只能移动。

shared_ptr: 可以共享资源(通过计数器的方式)

移动后:unique_ptr的资源被转移到了shared_ptr当中,unique_ptr不再占有资源,被置为nullptr。变成有shared_ptr占有资源,并且有计数器管理生命周期。

将unique_ptr的资源转移到shared_ptr的话会导致,管理资源的成本变高,比如拷贝、赋值构造等等(因为有原子计数等等操作)。

一个注意点:转移的过程只能通过move进行转发。若直接复制的话,会报错!

应用场景

  • 动态切换所有权模式
    初始使用 unique_ptr 管理资源,后期需共享所有权时转为 shared_ptr

  • 工厂函数返回值扩展
    工厂函数返回 unique_ptr,调用者根据需要转为 shared_ptr

行为/特性unique_ptr → shared_ptr 的转移
所有权unique_ptr 释放所有权,shared_ptr 接管
unique_ptr 状态变为 nullptr
删除器自动继承(若有自定义删除器)
合法性合法且安全
风险点误用转移后的 unique_ptr(需立即停止操作)
适用场景需要从独占所有权切换到共享所有权的场景

 vector的resize和reverse有什么区别?

resize:调整容器的大小。若新的大小>现有大小,那么新增的元素会被默认初始化(例如初始化为0),若新的大小<现有大小,多余的元素就会被删掉(超过新的大小的元素,触发析构函数)。总的来说可能会触发内存的重新分配。

reverse:预留内存空间,预先分配足够的内存(capacity),避免后续插入元素的时候多次扩容。如果参数》当前capacity的话,则会分配新内存,但是不创建新的元素(size大小不变)。若参数《当前capacity的话,通常无效果。只改变capacity,不改变实际的size大小。

注意:你通过resize分配内存后,通过index直接访问扩容位置的元素的时候,你是可以访问的(一般是初始化值)。那么如果你通过reverse去分配capacity去访问预定位置的元素的话,你会触发未定义事件,就报错。

特性resize(n)reserve(n)
修改对象size()capacity()
新增元素初始化新增元素不创建元素
内存分配可能触发(若超过当前容量)可能触发(若参数 > 当前容量)
缩小操作销毁多余元素通常无效果
访问新空间可直接访问(vec[i] 合法)需通过插入元素(如 push_back
典型用途需要立即拥有固定数量元素优化性能,减少扩容次数

vector是存储在堆上还是栈上?

分两种情况,一种是通过new分配的对象,一种是不通过new分配的。

通过new分配的:不管是vector对象还是具体的资源都分配在堆上。

不通过new分配的:vector对象在栈上,但他的资源部分分配在堆上。

why?

因为栈的地址是不断增加的,但是你要分配资源的话,分配资源的这个动作是动态的,元素可能增加也可能减少,那么就不能满足栈的出栈和入栈的有序性。

  • 动态扩容需求
    栈内存大小固定且有限,无法支持 vector 运行时动态扩容(如 push_back 时容量倍增)。

  • 灵活性
    堆内存允许按需分配和释放,适合存储不确定数量的元素。

简化的vector示意图:

栈(Stack)                          堆(Heap)

+----------------+                    +----------------+

| vector 对象 |                    | 元素存储的数组 |

| - 指针 -------|    ------->      | [元素1][元素2]... | 

| - 大小(size) |                 +----------------+ 

| - 容量(capacity)|            +----------------+

情景vector 对象位置元素存储位置
局部变量(非 new
动态分配(new

 

如何防止DNS劫持?

什么是dns劫持:攻击者通过篡改DNS解析过程,将用户引导至恶意网站而非目标网站。

dns劫持的类型:

  1. 本地劫持

    • 恶意软件修改设备DNS设置或Hosts文件,强制使用攻击者的DNS服务器。

  2. 路由器劫持

    • 攻击者入侵路由器(如利用默认密码),篡改其DNS配置,影响所有连接设备。

  3. 中间人攻击(MITM)

    • 在用户与DNS服务器间拦截并伪造响应,常见于不安全的公共WiFi。

  4. DNS缓存投毒

    • 向DNS服务器注入虚假记录,污染缓存,导致后续查询返回错误IP。

危害:1.诱导用户输入敏感信息(账户密码账号等等)。2.恶意软件传播(下载病毒或者勒索软件)3.广告欺诈(插入30秒复活的广告牟利)4.流量监控(窃取用户数据)

防范错书:

1.使用加密的dns服务(DNS over HTTPS (DoH) 或 DNS over TLS (DoT))加密dns查询,防止监听篡改。

2.启动DNS安全扩展(DNSSEC)通过数字签名验证响应真实性,防止缓存投毒

3.保护路由器安全:更改默认管理员密码,定期更新固件,禁用远程管理功能。

4.安装杀毒软件:定期检查C:\Windows\System32\drivers\etc\hosts    hosts文件是否被修改。

什么是僵尸进程和孤儿进程,操作系统怎么处理这些进程?

僵尸进程:子进程终止,父进程还没有通过wait,waitpid系统调用读取他的退出状态的时候,这个子进程就会变成僵尸进程。

特点:进程描述符(PID)还存在于系统进程表当中,但不占用内存或者cpu。内核还会保存他的状态码等信息,直到父进程读取。

操作系统如何处理:操作系统会暂时保留僵尸进程的进程项,确保父进程能获得紫禁城的退出状态。

长期僵尸进程的风险:如果长期占用有限的PID号,大量的僵尸进程可能会导致PID号枯竭,导致系统无法创建新的进程。

如何避免:

父进程主动调用wait或者waitpid回收子进程资源。

父进程注册SIIGCHLD信号处理函数,在信号中调用wait

通过双fork技巧,让子进程fork实际任务进程并退出,让实际任务变成孤儿进程(交给初始进程收养)。

孤儿进程:当一个父进程先于子进程终止,子进程失去父进程,变为孤儿进程。

特点:孤儿进程仍在运行,但不再有父进程管理。操作系统会将孤儿进程的父进程重置为 init 进程(PID 1)。

操作系统如何处理:操作系统自动将孤儿进程的父进程设置为 init。init 进程会周期性地调用 wait() 回收孤儿进程的退出状态,避免其变为僵尸进程。孤儿进程终止后,会由 init 进程完成资源回收。

孤儿进程:通常无害,因为init进程会接管并且会调用wait回收孤儿进程的资源。但是孤儿进程长期运行,可能会占用资源,需要手动管理。

什么是init进程

init进程是类Unix操作系统(如Linux)中第一个用户空间进程(PID=1),由内核在系统启动时直接创建。它是所有其他进程的祖先,负责初始化系统环境、启动关键服务和管理系统运行状态,直到系统关闭。在linux当中一般书systemd,一般你想在开机初始化某个服务的话,你可以把你的脚本交由这个初始化进程管理。例如:systemctl enable httpd

(1) 系统初始化
  • 启动系统服务:根据配置文件(如 /etc/inittab 或 systemd的单元文件),按顺序或并行启动系统关键服务(如网络、日志、SSH等)。

  • 设置运行级别(Runlevel):传统SysV init通过运行级别(如0-6)定义系统状态(如多用户模式、图形界面、关机等),决定启动哪些服务。

(2) 进程管理
  • 孤儿进程收养:当某个进程的父进程终止后,init进程会接管其子进程(孤儿进程),确保它们正常退出并回收资源。

  • 僵尸进程清理:通过调用wait()回收已终止子进程的状态,防止僵尸进程堆积。

(3) 系统关闭与重启
  • 协调关机流程:终止所有用户进程,卸载文件系统,同步磁盘数据。

  • 发送信号(如SIGTERMSIGKILL)确保服务有序停止。

假如在某次远过程服务调用(并不一定是http)中,对方服务返回了connection refused,怎么排查这个问题?会用到那个命令?

在远程服务调用中出现 Connection refused 错误时,通常意味着客户端尝试连接目标服务的端口时,目标主机明确拒绝了连接请求。

那么可以从以下几个维度自查。

1.确认目标服务是不是在运行状态

可能的原因:目标服务未启动或者未指定监听端口。

# 查看服务进程是否运行(如Nginx、MySQL等)
systemctl status <service-name>

# 查看目标端口是否被监听(Linux)
netstat -tulnp | grep <端口号>
# 或使用更现代的 ss 命令
ss -tuln | grep <端口号>

# 检查特定进程是否监听端口(如Java服务)
lsof -i :<端口号>

 如果服务未运行,启动服务;如果端口未被监听,检查服务配置(如配置文件、绑定的IP地址等)。

2.检查防火墙和安全组规则

可能是目标主机的防火墙或云平台安全组规则阻止了连接。

如果是这种错误的话,你可以关闭防火墙的相关规则。

3.网络连通性

可能是网络路由问题或中间设备(如路由器、负载均衡器)阻断了连接。

那么首先使用

tracert 检查整个路由的链路是否可达。

然后执行telenet 《目标ip》 《端口号》检查目标端口的连通性

4.检查dns解析:也可能是dns解析错误导致连到错误的ip了

# 查看域名解析结果
dig <域名>
nslookup <域名>

 5.检查日志:

可能是服务端或中间件(如代理、负载均衡器)日志中可能有拒绝连接的记录。

命令表:

场景命令示例
检查端口监听ss -tuln | grep <端口>
测试端口连通性nc -zv <IP> <端口>
查看防火墙规则iptables -L -n -v
跟踪路由路径traceroute <IP>
检查DNS解析dig <域名>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值