后端面试常见题目及知识点(一)---Linux

最近正是求职季,自己也经历了多次面试,网上看了许多面经。整个过程中,记录了很多,学到了很多,开个博客,分享给大家,祝大家Offer多多!

该系列博客会从几个不同的大方向,对常见问题进行分类,同时内容为我日常整理方便自己快速回忆,复习,并且为了尽可能覆盖更多的知识点,所以每个问题只有简短关键的回答,如果对相关知识点不熟悉的,建议多查阅一些相关资料。

系列文章目录

  1. 操作系统(Linux)
  2. 计算机网络
  3. 数据库
  4. 其他

操作系统常见问题:线程进程相关知识,Linux常规命令等

Linux常用指令

查看所有进程

ps -ax | less

  • -a代表列出所有进程
  • x代表列出没有tty(控制终端)的进程
  • | linux指令里面的管道,利用管道将前一个程序的输出导入后一个程序(ps —> less)

拓展:ps是如何实现的?简述原理

Linux的/proc目录下面包含了所有进程的信息(ls /proc/),每个进程都有一个对应的文件夹,名字为进程号。所以实现ps基本原理就是遍历改目录,获取所有的进程信息

文档内查找内容

cat file.txt | grep test

如何查看操作系统是几位的

uname -m — x86_64 or x86

进程,线程,协程

  • 进程:进程是资源分配的最小单位(分配内存等)
    • 进程的fork都是先复制自身,然后再修改新进程的内容 —> init是所有进程的“老祖宗”
  • 线程:线程是CPU调度的最小单位(CPU “fetch”一个线程,然后执行)
  • 协程:“用户态的线程”,协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行(以同步的方式,在一个线程内执行任务)
  • 进程 v.s. 线程
    • 每个进程最少包含一个线程,每个线程必定属于某个进程
    • 进程要比线程消耗更多的计算机资源,不同进程间的隔离度也更高(i.e. 一个进程终止一般不会影响到另一个进程),但是一个线程终止可能会导致该线程所属的整个进程同时终止
    • 多进程:允许多个任务同时执行
    • 多线程:允许一个任务的不同部分同时执行(embarrassingly Parallel)
    • 进程内部所有线程共享内存空间
  • 线程 v.s. 协程
    • 线程切换:操作系统完成,overhead较大(e.g. 保存当前寄存器,flush,重新加载)
    • 协程切换:用户完成,overhead较小(还是在同一个线程内)

进程的几种状态

  • R(TASK_RUNNING)— 可执行状态
    • ready和running都属于这个状态
  • S(TASK_INTERRUPTIBLE) — 可中断的睡眠状态
    • 处于该状态的进程,通常由于等待某事件的发生(如socket),而被挂起(task_struct被放入对应事件的等待队列中);
    • 对应事件发生时,对应进程会被唤醒
    • 如果使用ps查看进程,会有很多进程处于该状态(CPU能承受多进程的原因!)
  • D(TASK_UNINTERRUPTIBLE) — 不可中断的睡眠状态
    • 存在的意义是有些系统操作是不可打断的,如操作系统对某些硬件进行交互的时候,需要用这个进行保护(kill信号无法杀死这个状态的进程)
  • T(TASK_STOPPED or TASK_TRACED) — 暂停状态或跟踪状态
    • 如断点调试
  • Z(TASK_ZOMBIE) — 退出状态,进程成为僵尸进程
    • 进程在退出过程中,所有资源都被回收,除了task_struct结构(和少数资源)外,此时线程处于僵尸进程
    • 父进程可以通过wait来使得僵尸进程退出
  • X(TASK_DEAD) — 退出状态,进程即将被销毁
    • 该状态通常很短暂(接下来就会销毁该进程),ps一般看不到

线程的几种状态

  • new — 创建
  • ready — 创建成功,可以被执行
  • running — 正在执行
  • waiting — 等待,由于IO或者有更高优先级的事情(如处理中断)
  • terminated — 结束,释放资源
    在这里插入图片描述

为什么需要协程?

  1. 节省CPU,协程是用户态的线程,不存在线程切换的开销;
  2. 节约内存,系统会给每个线程分配一定大小的栈内存(e.g. 8M)和堆内存(64M),线程数量有瓶颈;而协程的栈通常只有十几K,并且是从线程的堆里面分配出来的,数量受限小;
  3. 稳定性,不存在线程安全问题,因为协程是在一个线程内部,以同步的方式读写数据;
  4. 开发效率,可以利用协程将一些耗时操作异步化;

线程(进程)间的数据共享/通信

  • 线程间通信
    • 共享内存,共享变量,更需要考虑的是race condition
  • 进程间通信
    • 管道(pipe),单向通信,只能在具有亲属关系(多为父子关系)的进程间使用
    • 命名管道(FIFO),可在任意两个进程间使用
    • 信号量(semophore),一个计数器,多用于控制多进程访问共享资源
    • 消息队列(message queue),储存在内核中的一个消息链表
    • 信号(signal),类似于中断的概念
    • 共享内存(shared memory)
    • socket!!!(甚至可以做到分布式)

进程有哪些字段

  • PID
  • Stack
  • Heap
  • Execution context
    • Program counter (PC)
    • Stack pointer (SP)
    • 寄存器(Registers)
  • 代码
  • 数据
  • 独立内存空间

多线程如何做到thread-safe

  • thread local变量(直接避免了race condition)
  • 原子量(atomic)和原子操作(test-and-set,compare-and-swap)
  • 临界区(critical section)
  • 互斥锁(mutex)
  • 信号量(semaphore)
  • 事件对象(event)

活锁 V.S. 死锁

  • 死锁:两个或两个以上的线程,因争夺资源而陷入互相等待(阻塞),会一直持续下去
    • 可能是由于多个线程加锁的顺序不一样
  • 活锁:线程在执行,但是由于某些条件未满足,所以都是无用功
  • 形象的比喻,死锁是lock,活锁是try_lock(一直try),两者都是没有进度,但死锁是阻塞状态,活锁是运行状态
  • 饥饿:某个线程“长期”处于等待资源状态,比如由于优先级不够高,系统一直不执行

僵尸进程 V.S. 孤儿进程

  • 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用waitwaitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程
    • 任何一个进程退出后都会经历“僵尸进程”这个阶段,只有等到父进程调用wait或者waitpid之后,进程才会真正结束
    • 大量的僵尸进程可能会耗尽系统的进程号资源
  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(pid=1)所收养,并由init进程对它们完成状态收集工作
    • 孤儿进程不会有什么危害,因为孤儿进程会被init“收养”并正常退出
  • 如何避免僵尸进程?
    • 1)通过信号机制;子进程退出时会向父进程发送SIGCHILD信号,父进程可以捕捉这个信号进行wait处理
    • 2)fork两次(将子进程变为孤儿进程);先fork一次得到子进程1,然后在1里面再fork一次,成功后将1结束,从而使得子进程2变为孤儿进程,并被init进程“收养”

阻塞 V.S. 非阻塞,异步 V.S. 同步

  • 阻塞:指调用结果返回之前,调用者会进入阻塞状态(线程状态,此时该线程可能会进入wait状态)等待。只有在得到结果之后才会返回(e.g. socket的recv函数)
  • 非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回(e.g. socket的send函数,数据写到缓冲区之后立刻返回)
  • 同步:在发出一个同步调用时,在没有得到结果之前,该调用就不返回(程序状态,如调用一个函数,此时该线程还可以在CPU上运行)
  • 异步:在发出一个异步调用后,调用者不会立刻得到结果,该调用就返回了
  • 同步 V.S. 阻塞
    • 表面上看都是“卡住了”,但是阻塞指的是该线程被阻塞(CPU不再运行该线程);同步指的是程序/调用被阻塞,但是该线程可能还在CPU上面运行
  • 两两组合
    • 同步阻塞调用:得不到结果不返回,线程进入阻塞态等待;
    • 同步非阻塞调用:得不到结果不返回,线程不阻塞一直在CPU运行;
    • 异步阻塞调用:去到别的线程,让别的线程阻塞起来等待结果,自己不阻塞;
    • 异步非阻塞调用:去到别的线程,别的线程一直在运行,直到得出结果;

大端,小端和名字的由来

  • 名字由来 — 格列佛游记
  • 大端 — 数据的高字节保存在内存的低地址中
  • 小端 — 数据的低字节

字节对齐

  • 基本的数据类型对齐,int 4,char 1,double 8…
  • 结构体,类的长度对齐
  • 原因:CPU加载数据常常是从对齐地址开始加载的;CPU一次不是加载1byte,而是多byte

memcpy和memmove

  • memcpy比memmove效率更高,更快

  • memcpy不会检查有无地址有无重叠,所以可能会出错

  • memmove会检查地址有无重叠,若有重叠会把src先拷贝到tmp再拷贝到dst

  • 实现:

void *memcpy(void *dest, const void *src, size_t n) {
    // 注意要先转换为char*
    char * srcC = (char*) src;
    char * dstC = (char*) dest;
    while (n >= 0) {
        *srcC++ = *dstC++;
    }
    return dest;
}

堆 vs 栈

  • 分配方式不同 — 堆,程序申请,自行管理;栈,操作系统分配
  • 管理方式不同 — 堆,程序管理;栈,操作系统管理
  • 运行效率不同 — 栈的效率比堆高
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值