面试汇总(七):操作系统常见面试总结(三):操作系统其它问题

前言

  前两篇文章我们分别介绍了在面试中操作系统有关线程和进程系统中的一些问题常见的面试题。这篇文章我们继续给大家介绍常见的问题。这篇文章将给大家介绍操作系统中系统相关的问题。

面试题及参考答案

1、请你说一说操作系统中的结构体对齐,字节对齐

  • 1、原因:
    1)平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
    2)性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
  • 2、规则
    1)数据成员对齐规则: 结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma
    pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
    2)结构(或联合)的整体对齐规则: 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma
    pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
    3)结构体作为成员: 如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
  • 3、定义结构体对齐
      可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是指定的“对齐系数”。

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

  • 1、互斥锁和读写锁区别:   互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。
      读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。
    注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。
      互斥锁和读写锁的区别: 1)读写锁区分读者和写者,而互斥锁不区分
    2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。
  • 2、Linux的4种锁机制:

  互斥锁:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒
  读写锁:rwlock,分为读锁和写锁。处于读操作时,可以允许多个线程同时获得读操作。但是同一时刻只能有一个线程可以获得写锁。其它获取写锁失败的线程都会进入睡眠状态,直到写锁释放时被唤醒。
注意:写锁会阻塞其它读写锁。当有一个线程获得写锁在写时,读锁也不能被其它线程获取;写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)。适用于读取数据的频率远远大于写数据的频率的场合。
  自旋锁:spinlock,在任何时刻同样只能有一个线程访问对象。但是当获取锁操作失败时,不会进入睡眠,而是会在原地自旋,直到锁被释放。这样节省了线程从睡眠状态到被唤醒期间的消耗,在加锁时间短暂的环境下会极大的提高效率。但如果加锁时间过长,则会非常浪费CPU资源。
  RCU:即read-copy-update,在修改数据时,首先需要读取数据,然后生成一个副本,对副本进行修改。修改完成后,再将老数据update成新的数据。使用RCU时,读者几乎不需要同步开销,既不需要获得锁,也不使用原子指令,不会导致锁竞争,因此就不用考虑死锁问题了。而对于写者的同步开销较大,它需要复制被修改的数据,还必须使用锁机制同步并行其它写者的修改操作。在有大量读操作,少量写操作的情况下效率非常高。

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

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

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

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

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

  • 1)预编译    主要处理源代码文件中的以“#”开头的预编译指令。处理规则见下: 1、删除所有的#define,展开所有的宏定义。
    2、处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
    3、处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件。
    4、删除所有的注释,“//”和“/**/”。 5、保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once
    是为了防止有文件被重复引用。 6、添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号。
  • 2)编译    把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。
    1、词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。
    2、语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。
    3、语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。
    4、优化:源代码级别的一个优化过程。 5、目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。
    6、目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。
  • 3)汇编    将汇编代码转变成机器可以执行的指令(机器码文件)。 汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Windows下)、xxx.obj(Linux下)。
  • 4)链接    将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。链接分为静态链接和动态链接:
    1、静态链接:   函数和数据被编译进一个二进制文件。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件。
      空间浪费:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,会出现同一个目标文件都在内存存在多个副本;
      更新困难:每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。
      运行速度快:但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
    2、动态链接:   动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
      共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;
      更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。
      性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

6、请问GDB调试用过吗,什么是条件断点

1、GDB调试
  GDB 是自由软件基金会(Free Software Foundation)的软件工具之一。它的作用是协助程序员找到代码中的错误。如果没有GDB的帮助,程序员要想跟踪代码的执行流程,唯一的办法就是添加大量的语句来产生特定的输出。但这一手段本身就可能会引入新的错误,从而也就无法对那些导致程序崩溃的错误代码进行分析。
  GDB的出现减轻了开发人员的负担,他们可以在程序运行的时候单步跟踪自己的代码,或者通过断点暂时中止程序的执行。此外,他们还能够随时察看变量和内存的当前状态,并监视关键的数据结构是如何影响代码运行的。
2、条件断点
  条件断点是当满足条件就中断程序运行,命令:break line-or-function if expr。

7、请你说一说异步编程的事件循环

   事件循环就是不停循环等待时间的发生,然后将这个事件的所有处理器,以及他们订阅这个事件的时间顺序依次依次执行。当这个事件的所有处理器都被执行完毕之后,事件循环就会开始继续等待下一个事件的触发,不断往复。当同时并发地处理多个请求时,以上的概念也是正确的,可以这样理解:在单个的线程中,事件处理器是一个一个按顺序执行的。即如果某个事件绑定了两个处理器,那么第二个处理器会在第一个处理器执行完毕后,才开始执行。在这个事件的所有处理器都执行完毕之前,事件循环不会去检查是否有新的事件触发。在单个线程中,一切都是有顺序地一个一个地执行的!

8、请你回答一下为什么要有page cache,操作系统怎么设计的page cache

   加快从磁盘读取文件的速率。page cache中有一部分磁盘文件的缓存,因为从磁盘中读取文件比较慢,所以读取文件先去page cache中去查找,如果命中,则不需要去磁盘中读取,大大加快读取速度。在 Linux 内核中,文件的每个数据块最多只能对应一个 Page Cache 项,它通过两个数据结构来管理这些 Cache项,一个是radix tree,另一个是双向链表。Radix tree 是一种搜索树,Linux内核利用这个数据结构来通过文件内偏移快速定位Cache 项。

9、什么是页式存储?

   主存被等分成大小相等的片,称为主存块,又称为实页。 当一个用户程序装入内存时,以页面为单位进行分配。页面的大小是为2n,通常为1KB、2KB、2n KB等

10、 Python锁

   Python中的各种锁:

  • 一、全局解释器锁(GIL)
    1、什么是全局解释器锁
      每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。GIL的设计简化了CPython的实现,使的对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。
    2、全局解释器锁的好处
    1)、避免了大量的加锁解锁的好处
    2)、使数据更加安全,解决多线程间的数据完整性和状态同步
    3、全局解释器的缺点
      多核处理器退化成单核处理器,只能并发不能并行。
    4、GIL的作用:
      多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。
  • 二、同步锁
    1、什么是同步锁?
      同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。
    2、为什么用同步锁?
      因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程  序结果的完整性。
    3、怎么使用同步锁?
      只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。
    4、同步锁的所用:
      为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。
  • 三、死锁
    1、什么是死锁?
      指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。
    2、死锁产生的必要条件?
      互斥条件、请求和保持条件、不剥夺条件、环路等待条件
    3、处理死锁的基本方法?
      预防死锁、避免死锁(银行家算法)、检测死锁(资源分配)、解除死锁:剥夺资源、撤销进程
  • 四、什么是递归锁?
      在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。递归锁分为可递归锁与非递归锁。
  • 五、什么是乐观锁?
      假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
  • 六、什么是悲观锁?
      假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
  • 七、python常用的加锁方式?
      互斥锁、可重入锁、迭代死锁、互相调用死锁、自旋锁。

总结

  由于操作系统面试的内容较多,因此前面两篇文章和本篇文章对面试中常见的操作系统问题进行了简单的总结,分别从操作系统中的线程、进程系统的相关问题以及本篇文章是关于其他的相关问题。本人之所以花三篇文章的篇幅来介绍操作系统相关问题,一方面是为了方便自己以后面试的复习,另外也是给大家再次面试相关岗位的时候提供复习方向以及思路解答。这里就需要我们对操作系统有一个较为深层次的理解。于是,我们在准备的时候,首先就应该夯实基础,只有这样才能在众多的面试者中脱颖而出。另外,作为在计算机行业工作的从事者,掌握一些基础的操作系统的知识是很有必要的,也是我们的基本素养。最后希望大家不断进步,都能尽早拿到自己比较满意的offer!!!!继续加油,未来可期!!!!

猜你喜欢

转载自blog.csdn.net/Oliverfly1/article/details/108568677