C++面试题合集.操作系统系列

1:谈谈内存分配中的(.data)段和(.bss)段的区别?为什么要分.data段和.bss段?

data段:
存放已经初始化的全局变量和静态变量。在给已经初始化的全局变量和静态变量分配内存空间,数据保存在目标文件中。
Bss段
Bss(block started by Symbol)存放没有初始化的全局变量和静态变量,在编译器编译的时候不会给数据分配空间,只是记录数据所需空间大小。程序执行的时候在分配内存并清零。
为什么要分.data段和.bss段
在程序编译的时候,不会给.bss段的数据分配空间,只是记录数据所需空间的大小。在程序执行的时候,才会给.bss段中的数据分配内存。通过这种方式,可以节省一部分内存空间,进一步缩减可执行程序的大小。

44.c++编译过程:请你来说一下一个C++源文件从文本到可执行文件经历的过程?

对于C++源文件,从文本到可执行文件一般需要四个过程:
预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。
编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件
汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件
链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件

详解版:

  • 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、动态链接:
    动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
    共享库:就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;
    更新方便:更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标。
    性能损耗:因为把链接推迟到了程序运行时,所以每次执行程序都需要进行链接,所以性能会有一定损失。

58.请你说一说Linux虚拟地址空间

为了防止不同进程同一时刻在物理内存中运行而对物理内存的争夺和践踏,才用了虚拟内存。
虚拟内存技术使得不同进程在运行过程中,它所看到的是自己独自占有了当前系统的4G内存。所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即把虚拟内存对应位置的程序数据和代码拷贝到物理内存中,只是建立好虚拟内存和磁盘文件间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常来拷贝数据。
还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过实现请求实现内存与外存的信息置换。
虚拟内存的好处:
1.扩大地址空间
2.内存保护:每个进程运行在各自的虚拟内存地址空间,相互不干扰对方。虚存还对特定的内存地址提供写保护,可以防止代码或数据被恶意篡改。
3.公平内存分配。采用了虚存之后,每个进程都相当于有同样大小的虚存空间。
4.当进程通信时,可采用虚存共享的方式实现。
5.当不同的进程使用同样的代码时,比如库文件的代码,物理内存可以只存储一份代码,不同进程只需要把自己的虚拟内存映射过去就可以了,节省内存。
6.虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。在内存中可以保留多个进程,系统并发度提高。
7.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

虚拟内存的代价:
1.虚拟内存的管理需要建立很多数据结构,这些数据结构需要占用额外的内存
2.虚拟地址到物理地址的转换,增加了指令的执行时间
3.页面的换入换出需要磁盘I/O,这是很耗时的。
4.如果一页中只有一部分数据,会浪费内存。
60.说说说说操作系统中的缺页中断
Malloc和mmap等内存分配函数,在分配时只是建立了进程虚拟地址空间,并没有分配虚拟内存对应的物理内存。当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常。
缺页中断:
在请求分页系统中,可以通过查询页表中的状态位来确定所要访问得页面是否存在内存中。每当所要访问得页面不在内存时,会产生一次缺页中断,此时操作系统会根据页表中得外存地址在外存中找到所缺得一页,将其调入内存。
缺页本身是一种中断,与一般得中断一样,需要经过4个处理步骤:
1.保护CPU现场
2.分析中断原因
3.转入缺页中断处理程序进行处理
4.恢复CPU现场,继续执行
但是缺页中断时由于所要访问得页面不存在内存时,由硬件所产生的一种特殊的中断,因此与一般的中断存在区别:
1.在指令执行期间产生和处理缺页中断信号
2.一条指令在执行期间,可能产生多次缺页中断
3.缺页中断返回时,执行产生中断的那一条指令;而一般中断返回时,执行下一条指令。

87.请回答一下软连接和硬链接的区别

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

90.请你说说用户态和内核态的区别

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

125.epoll实现原理

Linux epoll机制是通过红黑树和双向链表实现的。 首先通过epoll_create()系统调用在内核中创建一个eventpoll类型的句柄,其中包括红黑树根节点和双向链表头节点。然后通过epoll_ctl()系统调用,向epoll对象的红黑树结构中添加、删除、修改感兴趣的事件,返回0标识成功,返回-1表示失败。最后通过epoll_wait()系统调用判断双向链表是否为空,如果为空则阻塞。当文件描述符状态改变,fd上的回调函数被调用,该函数将fd加入到双向链表中,此时epoll_wait函数被唤醒,返回就绪好的事件。

193.请你说一说epoll原理

调用顺序:
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
首先创建一个epoll对象,然后使用epoll_ctl对这个对象进行操作,把需要监控的描述添加进去,这些描述如将会以epoll_event结构体的形式组成一颗红黑树,接着阻塞在epoll_wait,进入大循环,当某个fd上有事件发生时,内核将会把其对应的结构体放入到一个链表中,返回有事件发生的链表。

epoll select poll区别

214.进程和线程的区别,你都使用什么线程模型
1)进程和线程区别
1、一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
2、进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)
3、进程是资源分配的最小单位,线程是CPU调度的最小单位。
4、系统开销: 由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销。
5、通信:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。在有的系统中,线程的切换、同步和通信都无须操作系统内核的干预 。
6、进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂。
7、进程间不会相互影响;线程一个线程挂掉将导致整个进程挂掉。
8、进程适应于多核、多机分布;线程适用于多核。
2、常用线程模型
1、Future模型
该模型通常在使用的时候需要结合Callable接口配合使用。
Future是把结果放在将来获取,当前主线程并不急于获取处理结果。允许子线程先进行处理一段时间,处理结束之后就把结果保存下来,当主线程需要使用的时候再向子线程索取。
Callable是类似于Runnable的接口,其中call方法类似于run方法,所不同的是run方法不能抛出受检异常没有返回值,而call方法则可以抛出受检异常并可设置返回值。两者的方法体都是线程执行体。
2、fork&join模型
该模型包含递归思想和回溯思想,递归用来拆分任务,回溯用合并结果。可以用来处理一些可以进行拆分的大任务。其主要是把一个大任务逐级拆分为多个子任务,然后分别在子线程中执行,当每个子线程执行结束之后逐级回溯,返回结果进行汇总合并,最终得出想要的结果。
这里模拟一个摘苹果的场景:有100棵苹果树,每棵苹果树有10个苹果,现在要把他们摘下来。为了节约时间,规定每个线程最多只能摘10棵苹树以便于节约时间。各个线程摘完之后汇总计算总苹果树。
3、actor模型
actor模型属于一种基于消息传递机制并行任务处理思想,它以消息的形式来进行线程间数据传输,避免了全局变量的使用,进而避免了数据同步错误的隐患。actor在接受到消息之后可以自己进行处理,也可以继续传递(分发)给其它actor进行处理。在使用actor模型的时候需要使用第三方Akka提供的框架。
4、生产者消费者模型
生产者消费者模型都比较熟悉,其核心是使用一个缓存来保存任务。开启一个/多个线程来生产任务,然后再开启一个/多个来从缓存中取出任务进行处理。这样的好处是任务的生成和处理分隔开,生产者不需要处理任务,只负责向生成任务然后保存到缓存。而消费者只需要从缓存中取出任务进行处理。使用的时候可以根据任务的生成情况和处理情况开启不同的线程来处理。比如,生成的任务速度较快,那么就可以灵活的多开启几个消费者线程进行处理,这样就可以避免任务的处理响应缓慢的问题。
5、master-worker模型
master-worker模型类似于任务分发策略,开启一个master线程接收任务,然后在master中根据任务的具体情况进行分发给其它worker子线程,然后由子线程处理任务。如需返回结果,则worker处理结束之后把处理结果返回给master。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haimianjie2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值