网络和操作系统面试题
网络和操作系统面试题
进程和线程的区别?
- 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。
- 切换:线程上下文切换比进程上下文切换要快得多。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。
- 系统开销:创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O 设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。
协程与线程的区别?
- 线程和进程都是同步机制,而协程是异步机制。
- 线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
- 一个线程可以有多个协程,一个进程也可以有多个协程。
- 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的 CPU 资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。
- 协程能保留上一次调用时的状态。
并发和并行有什么区别?
并发就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程 A 和 B,A 运行一个时间
片之后,切换到 B,B 运行一个时间片之后又切换到 A。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。并行就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。
进程与线程的切换流程?
进程切换分两步:
1、切换页表以使用新的地址空间,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。
2、切换内核栈和硬件上下文。
对于 linux 来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1 步是不需要做的,第2 步是进程和线程切换都要做的。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。
为什么虚拟地址空间切换会比较耗时?
进程都有自己的虚拟地址空间,把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用 Cache 来缓存常用的地址映射,这样可以加速页表查找,这个 Cache 就是 TLB(translation Lookaside Buffer, TLB本质上就是一个 Cache,是用来加速页表查找的)。由于每个进程都有自己的虚拟地址空间,那么显然每个进程都有自己的页表,那么当进程切换后页表也要进行切换,页表切换后 TLB就失效了,Cache 失效导致命中率降低,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致 TLB失效,因为线程无需切换地址空间,因此我们通常说线程切换要比较进程切换块,原因就在这里。
进程间通信方式有哪些?
管道:管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
管道可以分为两类:匿名管道和命名管道。匿名管道是单向的,只能在有亲缘关系的进程间通信;命名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
信号:信号是一种比较复杂的通信方式,信号可以在任何时候发给某一进程,而无需知道该进程的状态。
Linux 系统中常用信号:
- 1)SIGHUP:用户从终端注销,所有已启动进程都将收到该进程。系统缺省状态下对该信号的处理是终止进程。
- 2)SIGINT:程序终止信号。程序运行过程中,按 Ctrl+C 键将产生该信号。
- 3)SIGQUIT:程序退出信号。程序运行过程中,按 Ctrl+\键将产生该信号。(4)SIGBUS和 SIGSEGV:进程访问非法地址。
- 5)SIGFPE:运算中出现致命错误,如除零操作、数据溢出等。
- 6)SIGKILL:用户终止进程执行信号。shell 下执行 kill -9 发送该信号。
- 7)SIGTERM:结束进程信号。shell 下执行 kill 进程 pid 发送该信号。
- 8)SIGALRM:定时器信号。
- 9)SIGCLD:子进程退出信号。如果其父进程没有忽略该信号也没有处理该信号,则子进程退出后将形成僵尸进程。
- 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 消息队列:消息队列是消息的链接表,包括 Posix 消息队列和 System V 消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- Socket:与其他通信机制不同的是,它可用于不同机器间的进程通信。
优缺点:
- 管道:速度慢,容量有限;
- Socket:任何进程间都能通讯,但速度慢;
- 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题;
- 信号量:不能传递复杂消息,只能用来同步;
- 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。
进程间同步的方式有哪些?
临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
优点:保证在某一时刻只有一个线程能访问数据的简便办法。缺点:虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
互斥量:
为协调共同对一个共享资源的单独访问而设计的。互斥量跟临界区很相似,比临界区复杂,互斥对象只有一个,只有拥有互斥对象的线程才具有访问资源的权限。优点:使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。
缺点:
- 互斥量是可以命名的,也就是说它可以跨越进程使用,所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。
- 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号量对象可以说是一种资源计数器。
信号量:为控制一个具有有限数量用户资源而设计。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。互斥量是信号量的一种特殊情况,当信号量的最大资源数=1就是互斥量了。优点:适用于对 Socket(套接字)程序中线程的同步。
缺点:
- 信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点;
- 信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难,加重了程序员的编码负担;
- 核心操作 P-V分散在各用户程序的代码中,不易控制和管理,一旦错误,后果严重,且不易发现和纠正。
事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
优点:事件对象通过通知操作的方式来保持线程的同步,并且可以实现不同进程中的线程同步操作。
线程同步的方式有哪些?
临界区:当多个线程访问一个独占性共享资源时,可以使用临界区对象。拥有临界区的线程可以访问被保护起来的资源或代码段,其他线程若想访问,则被挂起,直到拥有临界区的线程放弃临界区为止,以此达到用原子方式操作共享资源的目的。
事件:事件机制,则允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。
互斥量:互斥对象和临界区对象非常相似,只是其允许在进程间使用,而临界区只限制与同一进程的各个线程之间使用,但是更节省资源,更有效率。
信号量:当需要一个计数器来限制可以使用某共享资源的线程数目时,可以使用“信号量”对象。
区别:
互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说互斥量可以跨越进程使用,但创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。
互斥量,信号量,事件都可以被跨越进程使用来进行同步数据操作。
线程的分类?
从线程的运行空间来说,分为用户级线程(user-level thread, ULT)和内核级线程(kernel-level, KLT)
- 内核级线程:这类线程依赖于内核,又称为内核支持的线程或轻量级进程。无论是在用户程序中的线程还是系统进程中的线程,它们的创建、撤销和切换都由内核实现。比如英特尔 i5-8250U是4 核8 线程,这里的线程就是内核级线程
- 用户级线程:它仅存在于用户级中,这种线程是不依赖于操作系统核心的。应用进程利用线程库来完成其创建和管理,速度比较快,操作系统内核无法感知用户级线程的存在。
什么是临界区,如何解决冲突?
每个进程中访问临界资源的那段程序称为临界区,一次仅允许一个进程使用的资源称为临界资源。
解决冲突的办法:
- 如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入,如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待;
- 进入临界区的进程要在有限时间内退出。
- 如果进程不能进入自己的临界区,则应让出 CPU,避免进程出现“忙等”现象。
什么是死锁?死锁产生的条件?
什么是死锁:在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是两个或多个进程无限期的阻塞、相互等待的一种状态。
死锁产生的四个必要条件:(有一个条件不成立,则不会产生死锁)
- 互斥条件:一个资源一次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得资源保持不放
- 不剥夺条件:进程获得的资源,在未完全使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系如何处理死锁问题:
通过破除死锁四个必要条件之一,可以防止死锁产生。
进程调度策略有哪几种?
先来先服务:非抢占式的调度算法,按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。另外,对 I/O 密集型进程也不利,因为这种进程每次进行 I/O 操作之后又得重新排队。
短作业优先:非抢占式的调度算法,按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
最短剩余时间优先:最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
时间片轮转:将所有就绪进程按 FCFS的原则排成一个队列,每次调度时,把 CPU时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU时间分配给队首的进程。
时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。而如果时间片过长,那么实时性就不能得到保证。
- 优先级调度:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
进程有哪些状态?
进程一共有5 种状态,分别是创建、就绪、运行(执行)、终止、阻塞。
- 运行状态就是进程正在 CPU上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
- 就绪状态就是说进程已处于准备运行的状态,即进程获得了除 CPU之外的一切所需资源,一旦得到 CPU即可运行。
- 阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待 I/O 完成。即使 CPU空闲,该进程也不能运行。
运行态→阻塞态:往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。
阻塞态→就绪态:则是等待的条件已满足,只需分配到处理器后就能运行。
运行态→就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。
就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。
什么是分页?
把内存空间划分为大小相等且固定的块,作为主存的基本单位。因为程序数据存储在不同的页面中,而页面又离散的分布在内存中,因此需要一个页表来记录映射关系,以实现从页号到物理块号的映射。访问分页系统中内存数据需要两次的内存访问(一次是从内存中访问页表,从中找到指定的物理块号,加上页内偏移得到实际物理地址;第二次就是根据第一次得到的物理地址访问内存取出数据)。
什么是分段?
分页是为了提高内存利用率,而分段是为了满足程序员在编写代码的时候的一些逻辑需求(比如数据共享,数据保护,动态链接等)。分段内存管理当中,地址是二维的,一维是段号,二维是段内地址;其中每个段的长度是不一样的,而且每个段内部都是从0 开始编址的。由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。
分页和分段有什区别?
- 分页对程序员是透明的,但是分段需要程序员显式划分每个段。
- 分页的地址空间是一维地址空间,分段是二维的。
- 页的大小不可变,段的大小可以动态改变。
- 分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
什么是交换空间?
操作系统把物理内存(physical RAM)分成一块一块的小内存,每一块内存被称为页(page)。当内存资源不足时,Linux 把某些页的内容转移至硬盘上的一块空间上,以释放内存空间。硬盘上的那块空间叫做交换空间(swap space),而这一过程被称为交换(swapping)。物理内存和交换空间的总容量就是虚拟内存的可用容量。
用途:
- 物理内存不足时一些不常用的页可以被交换出去,腾给系统。
- 程序启动时很多内存页被用来初始化,之后便不再需要,可以交换出去。
页面替换算法有哪些?
在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
包括以下算法:
- 最佳算法:所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。这是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
- 先进先出:选择换出的页面是最先进入的页面。该算法将那些经常被访问的页面也被换出,从而使缺页率升高。
- LRU:虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。 LRU将最近最久未使用的页面换出。为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。因为每次访问都需要更新链表,因此这种方式实现的 LRU代价很高。
- 时钟算法:时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。它将整个环形链表的每一个页面做一个标记,如果标记是0,那么暂时就不会被替换,然后时钟算法遍历整个环,遇到标记为1 的就替换,否则将标记为0 的标记为1。
什么是缓冲区溢出?有什么危害?
缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
危害有以下两点:
- 程序崩溃,导致拒绝额服务
- 跳转并且执行一段恶意代码造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。
什么是虚拟内存?
虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存。
讲一讲 IO 多路复用?
IO多路复用是指内核一旦发现进程指定的一个或者多个 IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
- 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用 I/O 复用。
- 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
- 如果一个 TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到 I/O 复用。
- 如果一个服务器即要处理 TCP,又要处理 UDP,一般要使用 I/O 复用。
- 如果一个服务器要处理多个服务或多个协议,一般要使用 I/O 复用。
- 与多进程和多线程技术相比,I/O 多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
硬链接和软链接有什么区别?
- 硬链接就是在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。删除任意一个条目,文件还是存在,只要引用数量不为0。但是硬链接有限制,它不能跨越文件系统,也不能对目录进行链接。
- 符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows的快捷方式。当源文件被删除了,链接文件就打不开了。因为记录的是路径,所以可以为目录建立符号链接。
中断的处理过程?
- 保护现场:将当前执行程序的相关数据保存在寄存器中,然后入栈。
- 开中断:以便执行中断时能响应较高级别的中断请求。
- 中断处理
- 关中断:保证恢复现场时不被新中断打扰
- 恢复现场:从堆栈中按序取出程序数据,恢复中断前的执行状态。
中断和轮询有什么区别?
- 轮询:CPU对特定设备轮流询问。中断:通过特定事件提醒 CPU。
- 轮询:效率低等待时间长,CPU利用率不高。中断:容易遗漏问题,CPU利用率不高。
请详细介绍一下 TCP 的三次握手机制,为什么要三次握手?
在讲三次握手之前首先要介绍 TCP 报文中两个重要的字段:一个是序号字段,另一个是确认号字段,这两个字段将在握手阶段以及整个信息传输过程起到重要作用。
第一步:客户端 TCP 向服务端的 TCP发送一个不带额外数据的特殊 TCP 报文段,该报文段的 SYN 标志位会被置1,所以把它称为 SYN 报文段。这时客户端会选取一个初始序列号(假设为 client_num),并将此编号放置在序号字段中。该报文段会被封装在一个 IP 数据报中发送给服务器。
第二步:服务器接收到 SYN 报文段后,会为该 TCP 分配缓存和变量,并发送允许连接的确认报文。在允许连接的报文中, SYN 标志位仍被置为1,确认号字段填的是 client_num +1 的值。最后服务端也会选取一个 server_num 存放到序号字段中,这个报文段称为 SYNACK 报文段。
第三步:在接收到 SYNACK 报文段后,客户端最后也要向服务端发送一个确认报文,这个报文和前两个不一样, SYN 标志位置0,在确认号字段中填上 server_num +1 的值,并且这个报文段可以携带数据。一旦完成这3 个步骤,客户端和服务器之间就可以相互发送包含数据的报文了。如果不是三次握手,二次两次的话,服务器就不知道客户端是否接收到了自己
的 SYNACK 报文段,从而无法建立连接;四次握手就显得多余了。
讲一讲 SYN 超时,洪泛攻击,以及解决策略
什么 SYN 是洪泛攻击?在 TCP 的三次握手机制的第一步中,客户端会向服务器发送 SYN 报文段。服务器接收到 SYN 报文段后会为该 TCP分配缓存和变量,如果攻击分子大量地往服务器发送 SYN 报文段,服务器的连接资源终将被耗尽,导致内存溢出无法继续服务。
解决策略:当服务器接受到 SYN 报文段时,不直接为该 TCP 分配资源,而只是打开一个半开的套接字。接着会使用 SYN 报文段的源 Id,目的 Id,端口号以及只有服务器自己知道的一个秘密函数生成一个 cookie,并把 cookie 作为序列号响应给客户端。
如果客户端是正常建立连接,将会返回一个确认字段为 cookie +1 的报文段。接下来服务器会根据确认报文的源 Id,目的 Id,端口号以及秘密函数计算出一个结果,如果结果的值+1 等于确认字段的值,则证明是刚刚请求连接的客户端,这时候才为该 TCP 分配资源
这样一来就不会为恶意攻击的 SYN 报文段分配资源空间,避免了攻击。
详细介绍一下 TCP 的四次挥手机制,为什么要有 TIME_WAIT 状态,为什么需要四次握手?服务器出现了大量 CLOSE_WAIT 状态如何解决?
当客户端要服务器断开连接时,客户端 TCP 会向服务器发送一个特殊的报文段,该报文段的 FIN 标志位会被置1,接着服务器会向客户端发送一个确认报文段。然后服务器也会客户端发送一个 FIN 标志位为1 的终止报文段,随后客户端回送一个确认报文段,服务器立即断开连接。客户端等待一段时间后也断开连接。其实四次挥手的过程是很容易理解的,由于 TCP 协议是全双工的,也就是说客户端和服务端都可以发起断开连接。两边各发起一次断开连接的申请,加上各自的两次确认,看起来就像执行了四次挥手。
为什么要有 TIME_WAIT 状态?因为客户端最后向服务器发送的确认 ACK 是有可能丢失的,当出现超时,服务端会再次发送 FIN 报文段,如果客户端已经关闭了就收不到了。还有一点是避免新旧连接混杂。
大量 CLOSE_WAIT 表示程序出现了问题,对方的 socket 已经关闭连接,而我方忙于读或写没有及时关闭连接,需要检查代码,特别是释放资源的代码,或者是处理请求的线程配置。