百度的面试,你觉得难度怎么样?

面经详解

操作系统处理死锁的方法

首先我们来了解一下什么是死锁:

死锁(Deadlock):是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。称此时系统处于死锁状态或系统产生了死锁。

产生死锁的必要条件

  • 互斥条件
  • 不可剥夺条件
  • 占有并请求条件
  • 循环等待条件

死锁的处理策略

  1. 预防死锁。破坏死锁产生的四个必要条件中的一个或几个。
  2. 避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)。
  3. 死锁的检测和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。

操作系统内存管理的方法

操作系统的内存管理方法主要包括以下几种:

  1. 分区分配

固定大小分区:内存被划分为若干固定大小的分区,每个分区分配给一个进程。

  • 优点:实现简单,分区大小固定。
  • 缺点:内存利用率低,可能导致内部碎片。

可变大小分区:根据进程需求动态分配内存分区。

  • 优点:提高内存利用率,减少内部碎片。
  • 缺点:容易产生外部碎片,需要定期进行内存紧凑。
  1. 页式存储管理

分页:将内存和进程地址空间分成固定大小的页(page)和页框(page frame)。

  • 优点:消除外部碎片,提高内存利用率。
  • 缺点:可能产生内部碎片,页表的管理需要额外的内存和处理时间。
  1. 段式存储管理

分段:将进程的逻辑地址空间划分为若干段(segment),每段有一个段号和偏移量。

  • 优点:方便进程模块化管理,保护不同段的内存。
  • 缺点:可能产生外部碎片,段表管理复杂。
  1. 段页式存储管理

段页结合:将分页和分段结合使用。先将进程分成若干段,再将每段分成若干页。

  • 优点:结合了分页和分段的优点,减少了碎片,提高了灵活性。
  • 缺点:段表和页表管理复杂,增加了内存开销。
  1. 虚拟内存

需求分页:进程只有在需要时才将相应的页调入内存。

  • 优点:减少内存使用,提高内存利用率。
  • 缺点:需要页表和页面置换算法,可能产生页面抖动(thrashing)。

页面置换算法:在内存不足时,决定哪些页面需要换出内存。

  • FIFO(先进先出):最先调入内存的页最先被换出。
  • LRU(最近最少使用):换出最近最少使用的页面。
  • OPT(最佳置换):换出将来最长时间不被访问的页面。
  • Clock(时钟):一种近似LRU的算法,使用一个环形缓冲区来管理页面。
  1. 内存紧凑和垃圾回收

内存紧凑:将内存中所有的空闲块集中到一起,消除外部碎片。

  • 优点:提高内存利用率。
  • 缺点:需要大量的移动操作,开销大。

垃圾回收:自动回收不再使用的内存块。

  • 优点:简化内存管理,防止内存泄漏。
  • 缺点:增加了系统开销,可能影响性能。

redis持久化

RDB持久化(全量备份)

RDB持久化是指在指定时间间隔内将内存中的数据集快照写入磁盘。实际上fork子线程,先将数据集写入临时文件,写入成功后,在替换之前的文件,用二进制压缩文件,RDB是Redis默认的持久化方式,会在对应目录下生产一个dump.rdb文件,重启会通过加载dump.rdb文件恢复数据

RDB优点:

  1. 方便持久化:只有一个dump.rdb文件;
  2. 容灾性好:一个文件可以保存到安全的磁盘;
  3. 性能好:fork子线程来完成写操作,主线程继续处理命令;
  4. 效率高:如何数据集偏大,RDB启动效率比AOF高

RDB缺点:

  1. 数据安全性低:因为RDB是每隔一段时间进行持久化,可能会造成数据丢失。
  2. 由于RDB是通过fork子线程协助完成数据持久化工作的,因此如果数据集较大时,可能会导致整个服务停止服务几百毫秒,甚至一分钟。

AOF持久化(增量备份)

AOF持久化是以日志的形式记录记录每一个增删操作然后追加到文件中。AOF的出现是为了弥补RDB备份的不足(数据不一致性)。

与RDB持久化相比,AOF的持久化实时性更好。

AOF的备份策略:Redis的配置文件中存在三种不同的AOF持久化方式:

  1. appendfsync always:每次有数据修改发生时都会同步。
  2. appendfsync everysec:每秒同步一次
  3. appendsync no:让操作系统决定何时进行同步。

AOF优点:

  1. AOF实时性哈好,数据安全性更高;
  2. AOF通过append模式写文件,即使中途服务器宕机,也可以通过redis-check-aof工具解决数据一致性问题。
  3. AOF机制的rewrite模式(文件过大会对命令进行合并重写),可以删除其中某些命令(比如误操作的命令)

AOF缺点:

  1. AOF文件比RDB文件大,且恢复慢;
  2. 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。

两者结合

将 RDB 和 AOF 合体使用,这个方法是在 Redis 4.0 提出的,该方法叫混合使用 AOF 日志和内存快照,也叫混合持久化。

混合持久化工作在 AOF 日志重写过程。

当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。

加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。

Memcache知道吗

Memcache 是一种高性能的分布式内存对象缓存系统,主要用于加速动态 web 应用程序,通过减少数据库负载来提高响应速度。它将数据存储在内存中,从而能够快速访问和检索数据。

Memcache 的主要特点和功能

  1. 缓存机制:Memcache 通过将经常访问的数据存储在内存中,可以显著减少对数据库的查询次数,从而提高应用程序的响应速度。

  2. 分布式架构:Memcache 支持分布式部署,允许多个服务器共同维护一个缓存系统,从而提升缓存容量和访问速度。

  3. 简单的数据模型:Memcache 使用键值对的形式存储数据,每个缓存项都有一个唯一的键和对应的值。键是字符串,值可以是任意数据。

  4. 内存管理:Memcache 自动管理内存,包括处理过期数据和根据内存限制逐出(evict)旧数据。

  5. 高并发性:Memcache 设计为支持高并发访问,适用于需要快速读写大量数据的场景。

  6. 支持多种编程语言:Memcache 提供了多种客户端库,支持包括 PHP、Python、Java、Ruby、C# 等多种编程语言,方便开发者集成使用。

使用场景

  1. Web 缓存:用于缓存数据库查询结果、API 响应、计算结果等,从而减少服务器负载和响应时间。

  2. 会话存储:Memcache 可以用来存储会话数据,提高会话访问速度,特别适用于分布式环境。

  3. 数据处理:在需要频繁读取的数据处理场景下,使用 Memcache 可以显著提高处理效率。

工作原理

  1. 存储数据:数据以键值对的形式存储在 Memcache 中,键是唯一的标识符,值是存储的数据。

  2. 数据访问:客户端通过键来访问对应的值,如果键存在,则直接返回存储的值;如果键不存在,则需要从数据库或其他存储介质中获取数据并缓存到 Memcache 中。

  3. 数据失效:每个缓存项可以设置失效时间,超过失效时间后,缓存项将被删除。此外,当 Memcache 达到内存限制时,旧的缓存项可能会被自动移除,以腾出空间存储新的缓存项。

使用shell统计文件中top3的ip地址

要使用 Shell 脚本统计文件中出现次数最多的前三个 IP 地址,可以使用以下步骤和命令:

  1. 读取文件内容:使用 cat 命令读取文件内容。
  2. 提取 IP 地址:使用 awk 提取 IP 地址。
  3. 统计出现次数:使用 sortuniq 统计每个 IP 地址出现的次数。
  4. 排序:按照出现次数排序。
  5. 取前 3 名:使用 head 取前 3 名。

磁盘满了写不进去,排查命令;如何查找大文件

当磁盘满了,写不进数据时,可以通过以下步骤和命令来排查问题:

1. 检查磁盘使用情况

使用 df 命令查看文件系统的磁盘使用情况。

df -h
  • -h 选项表示以人类可读的格式显示结果。

2. 查找大文件

使用 du 命令查找文件和目录的磁盘使用情况,并找出占用大量空间的文件。

du -ah / | sort -rh | head -n 20
  • -a 显示所有文件和目录的磁盘使用情况。
  • -h 以人类可读的格式显示结果。
  • sort -rh 根据文件大小进行排序,并按降序排列。
  • head -n 20 显示前 20 个占用空间最大的文件或目录。

3. 检查特定目录的磁盘使用情况

有时系统目录可能会占用大量空间,如 /var/home 等。可以针对这些目录进行检查:

du -sh /var/*
  • -s 显示每个目录的总大小。
  • -h 以人类可读的格式显示结果。

4. 查找大文件

使用 find 命令查找特定大小以上的文件:

find / -type f -size +100M -exec ls -lh {} \; | awk '{ print $9 ": " $5 }'
  • find / -type f -size +100M 查找大于 100MB 的文件。
  • -exec ls -lh {} \; 列出这些文件的详细信息。
  • awk '{ print $9 ": " $5 }' 显示文件名和大小。

说一下ES和Redis的区别

Elasticsearch (ES) 和 Redis 是两种非常不同的技术,各自有不同的用途和特点:

1. 定位和用途

Elasticsearch (ES):

  • 用途:Elasticsearch 是一个分布式搜索和分析引擎,主要用于处理和搜索大量的文本数据。
  • 应用场景:全文搜索、日志和事件数据分析、实时数据监控、复杂查询和分析。
  • 数据存储:以 JSON 文档的形式存储数据,支持复杂的查询和过滤操作。

Redis

  • 用途:Redis 是一个高性能的键值存储数据库,常用于缓存、会话管理和实时数据处理。
  • 应用场景:缓存、消息队列、会话存储、排行榜、实时统计、分布式锁。
  • 数据存储:以键值对的形式存储数据,支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。

2. 数据模型

Elasticsearch (ES):

  • 数据模型:文档存储。数据以 JSON 文档形式存储,每个文档属于一个索引,类似于关系数据库中的表。
  • 查询语言:Elasticsearch 使用其特有的查询 DSL(Domain Specific Language)来进行复杂查询和分析。

Redis

  • 数据模型:键值存储。数据以键值对形式存储,支持多种数据结构。
  • 查询语言:Redis 使用简单的命令行语法来进行数据操作,支持基本的 CRUD 操作和高级数据结构操作。

3. 持久化

Elasticsearch (ES):

  • 持久化:默认情况下,数据持久化到磁盘,并支持分布式存储和索引。Elasticsearch 的数据持久化机制确保了高可用性和数据的可靠性。

Redis

  • 持久化:支持多种持久化机制,如 RDB(快照)和 AOF(追加文件)。可以配置为无持久化模式,主要用于缓存和临时数据存储。

4. 性能

Elasticsearch (ES):

  • 性能:在处理和搜索大量文本数据时性能非常优越,特别适合需要复杂查询和全文搜索的场景。
  • 延迟:相对于 Redis 来说,Elasticsearch 的查询可能有较高的延迟,特别是在处理复杂查询和分析时。

Redis

  • 性能:作为内存数据库,Redis 提供非常高的读写性能和低延迟,非常适合需要快速响应的场景。
  • 延迟:通常在亚毫秒级别,非常适合高频率的读写操作。

5. 扩展性

Elasticsearch (ES):

  • 扩展性:Elasticsearch 通过分片(sharding)和副本(replication)机制实现横向扩展,能够处理大规模的数据存储和搜索需求。
  • 集群:支持分布式集群,能够动态添加和移除节点。

Redis

  • 扩展性:Redis 可以通过 Redis Cluster 实现水平扩展,支持分片,但在扩展和管理上相对于 Elasticsearch 更复杂。
  • 集群:支持主从复制和哨兵模式来提高可用性,但原生集群模式管理相对复杂。

6. 使用场景

Elasticsearch (ES):

  • 场景:日志分析(如 ELK 堆栈中的 "E")、全文搜索、实时数据分析、业务监控。
  • 优势:擅长处理海量文本数据和复杂查询需求。

Redis

  • 场景:缓存、会话管理、消息队列、实时计数、排行榜、临时数据存储。
  • 优势:高性能、低延迟的键值存储和多种数据结构支持。

总结

  • Elasticsearch:适用于需要处理和分析大量文本数据的场景,提供强大的全文搜索和分析能力。
  • Redis:适用于需要高性能和低延迟的数据访问场景,提供丰富的数据结构和简单的操作命令。

编程题

两个goroutine交替打印1-10 
//G1 1 
//G2 2 
//G1 3 
//G2 4 
//… 
//G1 9 
//G2 10

先别看示例代码,自己写一下试试

~

~

~

~

~

~

示例代码:

package main

import (
    "fmt"
    "sync"
)

func goroutine1(num chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 10; i += 2 {
        fmt.Println("G1", i)
        num <- i
        <-num
    }
}

func goroutine2(num chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 2; i <= 10; i += 2 {
        <-num
        fmt.Println("G2", i)
        num <- i
    }
}

func main() {
    num := make(chan int)
    var wg sync.WaitGroup
    wg.Add(2)

    go goroutine1(num, &wg)
    go goroutine2(num, &wg)

    wg.Wait()
}

欢迎关注 ❤

我的文章都首发在同名公众号:王中阳

需要简历优化或者就业辅导,可以直接加我微信:wangzhongyang1993

posted @ 2024-07-03 10:28  王中阳Go1  阅读(95)  评论(0编辑  收藏  举报