面经:计算机操作系统

(1)基本特征

1. 并发

并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。

2. 共享

共享是指系统中的资源可以被多个并发进程共同使用。

有两种共享方式:互斥共享和同时共享。互斥共享的资源称为临界资源

3. 虚拟

虚拟技术把一个物理实体转换为多个逻辑实体。

主要有两种虚拟技术:时(时间)分复用技术和空(空间)分复用技术。

多个进程能在同一个处理器上并发执行使用了时分复用技术

虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间。

4. 异步

异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进(未知性)

系统调用

如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。

中断分类

1. 硬件中断

包括外中断和内中断,内中断包括由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。外中断是由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。

2. 软件中断

是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态(Kernel Mode/Ring 0)的子例程,它常被用作实现系统调用(System call)。

进程与线程

1. 进程

进程是资源分配的基本单位。

2. 线程

线程是CPU独立调度的基本单位。一个进程中可以有多个线程,它们共享进程资源。共享资源和内存空间。

3. 区别

Ⅰ 拥有资源

进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。

Ⅱ 调度

在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。进程切换耗费资源,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,但是线程切换没那么复杂(如果待切换的线程是同一进程中的)。而线程切换时只需保存和设置少量寄存器内容,开销很小。

Ⅲ 系统开销

由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。

Ⅳ 通信方面

进程间通信的方式:

进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。

1.管道:

1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端

2)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

它具有以下限制:

  • 只支持半双工通信(单向交替传输);
  • 只能在父子进程或者兄弟进程中使用。

2. FIFO

  • 也称为命名管道,去除了管道只能在父子进程中使用的限制。

2. 系统IPC:

2.1 消息队列

消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标记。 (消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点)具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;

特点:

1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。

2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。

3)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

2.2 信号量semaphore

信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

特点:

1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。

2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。

3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。

2.3 信号signal

信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

2.4 共享内存(Shared Memory)

它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等

3.套接字SOCKET:

socket也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之间的进程通信。

 请你说一说线程间的同步方式,最好说出具体的系统调用

参考回答:

临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据

信号量

信号量是一种特殊的变量,可用于线程同步。它只取自然数值,并且只支持两种操作:

P(SV):如果信号量SV大于0,将它减一;如果SV值为0,则挂起该线程。

V(SV):如果有其他进程因为等待SV而挂起,则唤醒,然后将SV+1;否则直接将SV+1。

互斥量

当进入临界区      时,需要获得互斥锁并且加锁;当离开临界区时,需要对互斥锁解锁,以唤醒其他等待该互斥锁的线程。

条件变量

条件变量,又称条件锁,用于在线程之间同步共享数据的值。通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作条件变量提供一种线程间通信机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的一个/多个线程。即,当某个共享变量等于某个值时,调用 signal/broadcast。此时操作共享变量时需要加锁。其主要的系统调用如下:

请你说一说有了进程,为什么还要有线程?

参考回答:

线程产生的原因:

进程可以使多个程序能并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:

进程在同一时间只能干一件事

进程在执行的过程中如果阻塞,整个进程就会挂起,即使进程中有些工作不依赖于等待的资源,仍然不会执行。

因此,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时空开销,提高并发性。

请你说一下多线程和多进程的不同

参考回答:

进程是资源分配的最小单位,而线程时CPU调度的最小单位。多线程之间共享同一个进程的地址空间,线程间通信简单,同步复杂,线程创建、销毁和切换简单,速度快,占用内存少,适用于多核分布式系统,但是线程间会相互影响,一个线程意外终止会导致同一个进程的其他线程也终止,程序可靠性弱。

而多进程间拥有各自独立的运行地址空间,进程间不会相互影响,程序可靠性强,但是进程创建、销毁和切换复杂,速度慢,占用内存多,进程间通信复杂,但是同步简单,适用于多核、多机分布。

 请你说一下多进程和多线程的使用场景

参考回答:

多进程模型的优势是CPU,多进程模型,适用于CPU密集型。同时,多进程模型也适用于多机分布式场景中,易于多机扩展。

多线程模型主要优势为线程间切换代价较小,因此适用于I/O密集型的工作场景,因此I/O密集型的工作场景经常会由于I/O阻塞导致频繁的切换线程。同时,多线程模型也适用于单机多核分布式场景。

进程状态的切换

  • 就绪状态(ready):等待被调度
  • 运行状态(running)
  • 阻塞状态(waiting):等待资源

请你说一说进程状态转换图,动态就绪,静态就绪,动态阻塞,静态阻塞

参考回答:

1、进程的五种基本状态:

1)创建状态:进程正在被创建

2)就绪状态:进程被加入到就绪队列中等待CPU调度运行

3)执行状态:进程正在被运行

4)等待阻塞状态:进程因为某种原因,比如等待I/O,等待设备,而暂时不能运行。

5)终止状态:进程运行完毕

2、交换技术

当多个进程竞争内存资源时,会造成内存资源紧张,并且,如果此时没有就绪进程,处理机会空闲,I/0速度比处理机速度慢得多,可能出现全部进程阻塞等待I/O。

针对以上问题,提出了两种解决方法:

1)交换技术:换出一部分进程到外存,腾出内存空间。

2)虚拟存储技术:每个进程只能装入一部分程序和数据。

在交换技术上,将内存暂时不能运行的进程,或者暂时不用的数据和程序,换出到外存,来腾出足够的内存空间,把已经具备运行条件的进程,或进程所需的数据和程序换入到内存。

从而出现了进程的挂起状态:进程被交换到外存,进程状态就成为了挂起状态。

3、活动阻塞,静止阻塞,活动就绪,静止就绪

1)活动阻塞:进程在内存,但是由于某种原因被阻塞了。

2)静止阻塞:进程在外存,同时被某种原因阻塞了。

3)活动就绪:进程在内存,处于就绪状态,只要给CPU和调度就可以直接运行。

4)静止就绪:进程在外存,处于就绪状态,只要调度到内存,给CPU和调度就可以运行。

从而出现了:

活动就绪 ——  静止就绪        (内存不够,调到外存)

活动阻塞 ——  静止阻塞        (内存不够,调到外存)

执行     ——  静止就绪         (时间片用完)

4. 管程

管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。

管程引入了 条件变量 以及相关的操作:wait() 和 signal() 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。

计算机操作系统 - 死锁

必要条件

  • 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 请求与保持:已经得到了某个资源的进程可以再请求新的资源。
  • 不可剥夺条件:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
  • 循环等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。

处理方法

  • 死锁检测与死锁恢复
  • 死锁预防
  • 死锁避免

死锁检测与死锁恢复

不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。

资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。

死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。

3. 死锁恢复

  • 利用抢占恢复(资源)
  • 利用回滚恢复
  • 通过杀死进程恢复

死锁预防

在程序运行之前预防发生死锁

1. 破坏互斥条件

2. 破坏占有和等待条件

一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。

3. 破坏不可抢占条件

4. 破坏环路等待

给资源统一编号,进程只能按编号顺序来请求资源。

死锁避免

在程序运行时避免发生死锁。

银行家算法

进行预分配,如果存在安全状态,则可以分配。

(2)计算机操作系统 - 内存管理

虚拟内存

虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。

每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。

分页系统地址映射

内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。

一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。

请你说一说操作系统中的页表寻址

参考回答:

页式内存管理,内存分成固定长度的一个个页片。操作系统为每一个进程维护了一个从虚拟地址到物理地址的映射关系的数据结构,叫页表,页表的内容就是该进程的虚拟地址到物理地址的一个映射。页表中的每一项都记录了这个页的基地址。通过页表,由逻辑地址的高位部分先找到逻辑地址对应的页基地址,再由页基地址偏移一定长度就得到最后的物理地址,偏移的长度由逻辑地址的低位部分决定。一般情况下,这个过程都可以由硬件完成,所以效率还是比较高的。页式内存管理的优点就是比较灵活,内存管理以较小的页为单位,方便内存换入换出和扩充地址空间。

Linux最初的两级页表机制:

两级分页机制将32位的虚拟空间分成三段,低十二位表示页内偏移,高20分成两段分别表示两级页表的偏移。

* PGD(Page Global Directory): 最高10位,全局页目录表索引

* PTE(Page Table Entry):中间10位,页表入口索引

当在进行地址转换时,结合在CR3寄存器中存放的页目录(page directory, PGD)的这一页的物理地址,再加上从虚拟地址中抽出高10位叫做页目录表项(内核也称这为pgd)的部分作为偏移, 即定位到可以描述该地址的pgd;从该pgd中可以获取可以描述该地址的页表的物理地址,再加上从虚拟地址中抽取中间10位作为偏移, 即定位到可以描述该地址的pte;在这个pte中即可获取该地址对应的页的物理地址, 加上从虚拟地址中抽取的最后12位,即形成该页的页内偏移, 即可最终完成从虚拟地址到物理地址的转换。从上述过程中,可以看出,对虚拟地址的分级解析过程,实际上就是不断深入页表层次,逐渐定位到最终地址的过程,所以这一过程被叫做page talbe walk。

请你说一说操作系统中的缺页中断

参考回答:

缺页中断:在请求分页系统中,可以通过查询页表中的状态位来确定所要访问的页面是否存在于内存中。每当所要访问的页面不在内存是,会产生一次缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。

缺页本身是一种中断,与一般的中断一样,需要经过4个处理步骤:

1、保护CPU现场

2、分析中断原因

3、转入缺页中断处理程序进行处理

4、恢复CPU现场,继续执行

但是缺页中断是由于所要访问的页面不存在于内存时,由硬件所产生的一种特殊的中断,因此,与一般的中断存在区别:

1、在指令执行期间产生和处理缺页中断信号

2、一条指令在执行期间,可能产生多次缺页中断

3、缺页中断返回是,执行产生中断的一条指令,而一般的中断返回是,执行下一条指令。

页面置换算法

在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。

页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。

1. 最佳

OPT, Optimal replacement algorithm

所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。

是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。

2. 最近最久未使用

LRU, Least Recently Used

LRU 将最近最久未使用的页面换出。

缺陷:缓存颠簸

3. 最近未使用

NRU, Not Recently Used

思想:如果数据过去被访问多次,那么将来被访问的频率也更高。

实现:每个数据块一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。每次淘汰队尾数据块。

开销:排序开销。

弊端:缓存颠簸。

每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:

  • R=0,M=0
  • R=0,M=1
  • R=1,M=0
  • R=1,M=1

当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。

NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。

4. 先进先出

FIFO, First In First Out

选择换出的页面是最先进入的页面。

该算法会将那些经常被访问的页面换出,导致缺页率升高。

6. 时钟

Clock

第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。

分段

虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。

分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同

段页式

程序的地址空间划分成多个拥有独立地址空间的段每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。

分页与分段的比较

  • 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。

  • 地址空间的维度:分页是一维地址空间,分段是二维的。

  • 大小是否可以改变:页的大小不可变,段的大小可以动态改变。

  • 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。

(3)计算机操作系统 - 设备管理

磁盘调度算法

读写一个磁盘块的时间的影响因素有:

  • 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)
  • 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)
  • 实际的数据传输时间

其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短

1. 先来先服务

FCFS, First Come First Served

按照磁盘请求的顺序进行调度。

优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。

#2. 最短寻道时间优先

SSTF, Shortest Seek Time First

优先调度与当前磁头所在磁道距离最近的磁道。

虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。

#3. 电梯算法

SCAN

电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。

电梯算法(扫描算法)和电梯的运行过程类似,总是按一个方向来进行磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。

因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。

(4)计算机操作系统 - 链接

这个过程大致如下:

  • 预处理阶段:处理以 # 开头的预处理命令;
  • 编译阶段:翻译成汇编文件;
  • 汇编阶段:将汇编文件翻译成可重定位目标文件;
  • 链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。

静态链接

静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务:

  • 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。
  • 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。

#目标文件

  • 可执行目标文件:可以直接在内存中执行;
  • 可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件;
  • 共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接;

#动态链接

静态库有以下两个问题:

  • 当静态库更新时那么整个程序都要重新进行链接;
  • 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。

共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点:

  • 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中;
  • 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。

请问MySQL的端口号是多少,如何修改这个端口号

参考回答:

查看端口号:

使用命令show global variables like 'port';查看端口号 ,mysql的默认端口是3306。(补充:sqlserver默认端口号为:1433;oracle默认端口号为:1521;DB2默认端口号为:5000;PostgreSQL默认端口号为:5432)

修改端口号:

修改端口号:编辑/etc/my.cnf文件,早期版本有可能是my.conf文件名,增加端口参数,并且设定端口,注意该端口未被使用,保存退出。

请问线程需要保存哪些上下文,SP、PC、EAX这些寄存器是干嘛用的

参考回答:

线程在切换的过程中需要保存当前线程Id、线程状态、堆栈、寄存器状态等信息。其中寄存器主要包括SP PC EAX等寄存器,其主要功能如下:

SP:堆栈指针,指向当前栈的栈顶地址

PC:程序计数器,存储下一条将要执行的指令

EAX:累加寄存器,用于加法乘法的缺省寄存器

请你讲述一下互斥锁(mutex)机制,以及互斥锁和读写锁的区别

参考回答:

1、互斥锁和读写锁区别:

互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。

读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。

互斥锁和读写锁的区别:

1)读写锁区分读者和写者,而互斥锁不区分

2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

2、Linux的4种锁机制:

互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒

读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。 注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。

自旋锁:spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费CPU资源。

RCU:即read-copy-update,在修改数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后,再将老数据update成新的数据。使用RCU时,读者几乎不需要同步开销,既不需要获得锁,也不使用原子指令,不会导致锁竞争,因此就不用考虑死锁问题了。而对于写者的同步开销较大,它需要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操作。在有大量读操作,少量写操作的情况下效率非常高。

A* a = new A; a->i = 10;在内核中的内存分配上发生了什么?

参考回答:

1、程序内存管理:

一个程序本质上都是由BSS段、data段、text段三个组成的。可以看到一个可执行程序在存储(没有调入内存)时分为代码段、数据区和未初始化数据区三部分。

BSS段(未初始化数据区):通常用来存放程序中未初始化的全局变量和静态变量的一块内存区域。BSS段属于静态分配,程序结束后静态变量资源由系统自动释放。

数据段:存放程序中已初始化的全局变量的一块内存区域。数据段也属于静态内存分配

代码段:存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量

text段和data段在编译时已经分配了空间,而BSS段并不占用可执行文件的大小,它是由链接器来获取内存的。

bss段(未进行初始化的数据)的内容并不存放在磁盘上的程序文件中。其原因是内核在程序开始运行前将它们设置为0。需要存放在程序文件中的只有正文段和初始化数据段。

data段(已经初始化的数据)则为数据分配空间,数据保存到目标文件中。

数据段包含经过初始化的全局变量以及它们的值。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段的后面。当这个内存进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。

可执行程序在运行时又多出两个区域:栈区和堆区。

栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。

堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的 malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

2、A* a = new A; a->i = 10:

1)A *a:a是一个局部变量,类型为指针,故而操作系统在程序栈区开辟4/8字节的空间(0x000m),分配给指针a。

2)new A:通过new动态的在堆区申请类A大小的空间(0x000n)。

3)a = new A:将指针a的内存区域填入栈中类A申请到的地址的地址。即*(0x000m)=0x000n。

4)a->i:先找到指针a的地址0x000m,通过a的值0x000n和i在类a中偏移offset,得到a->i的地址0x000n + offset,进行*(0x000n + offset) = 10的赋值操作,即内存0x000n + offset的值是10。

给你一个类,里面有static,virtual,之类的,来说一说这个类的内存分布

参考回答:

1、static修饰符

1)static修饰成员变量

对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当做是类的成员,无论这个类被定义了多少个,静态数据成员都只有一份拷贝,为该类型的所有对象所共享(包括其派生类)。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新。

因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以它不属于特定的类对象,在没有产生类对象前就可以使用。

2)static修饰成员函数

它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,只能调用其他的静态成员函数。

Static修饰的成员函数,在代码区分配内存。

请你回答一下软链接和硬链接区别

参考回答:

为了解决文件共享问题,Linux引入了软链接和硬链接。除了为Linux解决文件共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。若1个inode号对应多个文件名,则为硬链接,即硬链接就是同一个文件使用了不同的别名,使用ln创建。若文件用户数据块中存放的内容是另一个文件的路径名指向,则该文件是软连接。软连接是一个普通文件,有自己独立的inode,但是其数据块内容比较特殊。

● 请问什么是大端小端以及如何判断大端小端

参考回答:

大端是指低字节存储在高地址;小端存储是指低字节存储在低地址。我们可以根据联合体来判断该系统是大端还是小端。因为联合体变量总是从低地址存储。

 请你说一说用户态和内核态区别

参考回答:

用户态和内核态是操作系统的两种运行级别,两者最大的区别就是特权级不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常和中断

windows消息机制知道吗,请说一说

参考回答:

当用户有操作(鼠标,键盘等)时,系统会将这些时间转化为消息。每个打开的进程系统都为其维护了一个消息队列,系统会将这些消息放到进程的消息队列中,而应用程序会循环从消息队列中取出来消息,完成对应的操作。

请你说一说内存溢出和内存泄漏

参考回答:

1、内存溢出

指程序申请内存时,没有足够的内存供申请者使用。内存溢出就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误

内存溢出原因:

内存中加载的数据量过于庞大,如一次从数据库取出过多数据

集合类中有对对象的引用,使用完后未清空,使得不能回收

代码中存在死循环或循环产生过多重复的对象实体

使用的第三方软件中的BUG

启动参数内存值设定的过小

2、内存泄漏

内存泄漏是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

内存泄漏的分类:

1、堆内存泄漏 (Heap leak)。对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete 删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak。

2、系统资源泄露(Resource Leak)。主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。

3、没有将基类的析构函数定义为虚函数。当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露。

系统调用是什么,你用过哪些系统调用

参考回答:

1)概念:

在计算机中,系统调用(英语:system call),又称为系统呼叫,指运行在使用者空间的程序向操作系统内核请求需要更高权限运行的服务。系统调用提供了用户程序与操作系统之间的接口(即系统调用是用户程序和内核交互的接口)。

操作系统中的状态分为管态(核心态)和目态(用户态)。大多数系统交互式操作需求在内核态执行。如设备IO操作或者进程间通信。特权指令:一类只能在核心态下运行而不能在用户态下运行的特殊指令。不同的操作系统特权指令会有所差异,但是一般来说主要是和硬件相关的一些指令。用户程序只在用户态下运行,有时需要访问系统核心功能,这时通过系统调用接口使用系统调用。

 请你来手写一下fork调用示例

参考回答:

1、概念:

Fork:创建一个和当前进程映像一样的进程可以通过fork( )系统调用:

成功调用fork( )会创建一个新的进程,它几乎与调用fork( )的进程一模一样,这两个进程都会继续运行。在子进程中,成功的fork( )调用会返回0。在父进程中fork( )返回子进程的pid。如果出现错误,fork( )返回一个负值。

请你来说一说用户态到内核态的转化原理

参考回答:

1)用户态切换到内核态的3种方式

1、系统调用

这是用户进程主动要求切换到内核态的一种方式,用户进程通过系统调用申请操作系统提供的服务程序完成工作。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的ine 80h中断。

2、异常

当CPU在执行运行在用户态的程序时,发现了某些事件不可知的异常,这是会触发由当前运行进程切换到处理此。异常的内核相关程序中,也就到了内核态,比如缺页异常。

3、外围设备的中断

当外围设备完成用户请求的操作之后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条将要执行的指令,转而去执行中断信号的处理程序,如果先执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了有用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

2)切换操作

从出发方式看,可以在认为存在前述3种不同的类型,但是从最终实际完成由用户态到内核态的切换操作上来说,涉及的关键步骤是完全一样的,没有任何区别,都相当于执行了一个中断响应的过程,因为系统调用实际上最终是中断机制实现的,而异常和中断处理机制基本上是一样的,用户态切换到内核态的步骤主要包括:

1、从当前进程的描述符中提取其内核栈的ss0及esp0信息。

2、使用ss0和esp0指向的内核栈将当前进程的cs,eip,eflags,ss,esp信息保存起来,这个过程也完成了由用户栈找到内核栈的切换过程,同时保存了被暂停执行的程序的下一条指令。

3、将先前由中断向量检索得到的中断处理程序的cs,eip信息装入相应的寄存器,开始执行中断处理程序,这时就转到了内核态的程序执行了。

● 请你说一下源码到可执行文件的过程

参考回答:

1)预编译

主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下

2)编译

把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。

1、词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。

2、语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。

3、语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。

4、优化:源代码级别的一个优化过程。

5、目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。

6、目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。

3)汇编

将汇编代码转变成机器可以执行的指令(机器码文件)。 汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Windows下)、xxx.obj(Linux下)。

4)链接

将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链接:

1、静态链接:

函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。

空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;

更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

2、动态链接:

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;

更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。

性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

请你说一下僵尸进程

参考回答:

1)正常进程

正常情况下,子进程是通过父进程创建的,子进程再创建新的进程。 当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。

2)孤儿进程

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

3)僵尸进程

一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。

僵尸进程是一个进程必然会经过的过程:这是每个子进程在结束时都要经过的阶段。

如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。

如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

危害:

如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。

外部消灭:

通过kill发送SIGTERM或者SIGKILL信号消灭产生僵尸进程的进程,它产生的僵死进程就变成了孤儿进程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源

内部解决:

1、子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。

2、fork两次,原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。

请你来介绍一下5种IO模型

参考回答:

1.阻塞IO:调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作
2.非阻塞IO:非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事。
3.信号驱动IO:信号驱动IO:linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件。
4.IO复用/多路转接IO:linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数
5.异步IO:linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。

两个进程访问临界区资源,会不会出现都获得自旋锁的情况?

参考回答:

单核cpu,并且开了抢占可以造成这种情况。

server端监听端口,但还没有客户端连接进来,此时进程处于什么状态?

参考回答:

这个需要看服务端的编程模型,如果如上一个问题的回答描述的这样,则处于阻塞状态,如果使用了epoll,select等这样的io复用情况下,处于运行状态

● 请问如何设计server,使得能够接收多个客户端的请求

参考回答:

多线程,线程池,io复用

参考文献:文献1

文献2

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统与计算机基础面试八股文主要包括以下内容: 1. 计算机基础:包括计算机网络、数据结构、排序算法、操作系统、数据库等方面的知识。例如,计算机网络中的协议结构可以采用五层体系结构,其中应用层对应 OSI 的上三层,下四层和 OSI 相同。\[2\] 数据结构中的二叉树有不同的种类和遍历方式。排序算法可以包括冒泡排序、插入排序、选择排序等。 2. 操作系统:涉及操作系统的基本概念、进程管理、内存管理、文件系统等内容。例如,操作系统中的死锁及条件可以使用银行家算法来解决。\[1\] 3. 计算机语言:包括 C/C++、Java、Python 等编程语言的相关知识。例如,C++中的 struct 和 class 的区别,Java中的线程安全和多线程编程等。 4. 网络安全:涉及网络安全攻击的种类和防范措施。例如,安全攻击可以包括网络钓鱼、拒绝服务攻击、SQL 注入等。\[3\] 综上所述,操作系统与计算机基础面试八股文主要涵盖计算机基础知识、操作系统、计算机语言和网络安全等方面的内容。 #### 引用[.reference_title] - *1* [计算机基础面试备考知识点(八股文)](https://blog.csdn.net/weixin_44934424/article/details/115066562)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [计算机网络面经八股文](https://blog.csdn.net/XiaoFengsen/article/details/125940951)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值