当前位置: 首页 > news >正文

Linux操作系统面试题记录

一、进程与线程

并发和并行的区别?

并发:一个cpu处理器处理多个任务;
并行:多个cpu处理器处理多个任务;

进程和线程是什么?区别?何时用线程何时用进程?

Linux中其实没有进程线程之分,fork和pthraed_create都会do_fork来创建,只是参数不同,pthread的参数会指定多个进程之间共享某些资源
进程:资源分配;独立的地址空间;进程之间的通信要用进程间通信;创建开销大,切换效率低,但资源管理和保护好
线程:程序执行;共享进程的地址空间和资源,但拥有独立的栈;线程通信方便(全局变量);创建开销小,切换效率高,适合频繁切换

为什么要用多线程?

解决负载均衡问题,提高CPU利用率,同时干几件事;

进程控制块PCB?

PCB在linux中就是task_srtuct,包含了pid,uid,状态,优先级,资源分配清单(进程地址空间,打开文件列表,IO设备信息)mm_struct,cpu各个寄存器的值;

PCB的组织方式:双向链表!把具有相同状态的进程PCB链在一起,组成各种队列,就绪链表,阻塞链表等

进程的创建过程?

创建进程:创建PCB(task_struct)、进程地址空间(mm_struct)、创建页表,将PCB插入任务链表,将进程的代码和数据载入内存;

进程的创建终止过程?

找到PCB、终止执行让出CPU、如果有子进程,将子进程给给init领养、进程资源还给os,将PCB从所在队列中删除;

僵尸进程?孤儿进程?

僵尸进程:子进程退出,但父进程没有getwait,为它清理;
孤儿进程:父进程退出,子进程还没退出,子进程将会被init领养;

守护进程?怎么实现?

守护进程:运行在后台的进程,只有需要才唤醒;
创建时有意将父进程退出,被init收养,进行setsid,chdir,umask,成为守护进程;

僵尸进程的危害?

子进程退出之后,PCB会维护退出信息,如果子进程退出越来越多,但父进程没wait清理,将会占用越来越多的内存,造成内存浪费,甚至会程序崩溃;

进程阻塞和唤醒进程?

找到PCB,保护/恢复现场,设置状态为就绪/阻塞,将pcb插入就绪/阻塞队列;

进程状态?转换图?

进程的状态有:创建,就绪,运行,阻塞,结束

linux系统通常将阻塞状态的进程,换出到硬盘,等到再次需要运行的时候再重新加载进内存;

进程上下文切换

进程是由内核管理和调度的,进程上下文切换发生在内核态;
进程上下文不仅包括:用户空间的虚拟内存,栈,全局变量,还包括内核空间的内核堆栈,寄存器等;通常会将保存的信息存储在PCB中;
详细:用户:代码,数据,堆栈,共享区;寄存器;系统:PCB,内存管理信息,内核栈;
情况:时间片耗尽,系统资源不足阻塞,主动sleep休眠,被高优先级抢占,硬件中断(执行内核中的中断服务程序)

线程上下文切换

同一个进程中的多个线程可以共享代码段和数据段(地址空间),打开的文件等,但每个线程有独立的寄存器和栈;

当进程中的一个线程崩溃时,会导致其所属进程的所有线程崩溃(这里是针对 C/C++ 语言)
解释:进程的崩溃是当非法访问内存(向只读写,访问无权限地址,访问不存在内存如空指针)通过发送kill信号退出进程,java不会崩溃是因为JVM对信号定义了自己的处理函数,恢复了进程的运行;

中断上下文

硬件传递的变量和参数+内核需要保存用户进程的寄存器和变量(被打断的进程环境)

用户态和内核态切换?

系统调用的模式切换相比起进程的切换会简单很多,最主要的任务只是切换寄存器上下文;
硬件通过出发信号,导致内核调用中断处理函数,进入内核空间;
将硬件的一些变量和参数传递给内核,内核通过这些参数进行中断处理;
中断时,内核不代表任何进程运行,它一般只访问系统空间,而不会访问进程空间,内核在中断上下文中执行时一 般不会阻塞。

线程共享的资源是什么?

代码段,数据段,地址空间和文件资源;
当两个线程不是属于同一个进程,则切换的过程就跟进程上下文切换一样;
当两个线程是属于同一个进程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的临时数据、寄存器等不共享的数据;

用户线程和内核线程?

用户线程:用户空间实现的线程,基于线程库而不由内核管理;用户级的线程库完成线程的管理,包括创建终止调度;
优点:线程切换不需要内核态用户态切换,切换速度快,创建销毁的开销小,线程管理更加灵活;
缺点:
同一进程中只能同时有一个线程在运行,如果有一个线程使用了系统调用而阻塞,那么整个进程都会被挂起。时间片是分给进程,多线程的时候,每个线程的时间片更少,执行会比较慢;

用户级线程的系统内,CPU调度还是以进程为单位,有内核支持线程的系统内,CPU调度则以线程为单位;

内核线程:内核中实现的线程,由内核进行管理;
优点:内核线程系统调用阻塞,不影响其他内核线程;更多的执行时间;
缺点:线程的创建和销毁切换开销更大(系统调用核态切换)

 

调度原则和调度算法?

CPU利用率(I/O),系统吞吐量(长短任务),周转时间(运行+就绪+阻塞),等待时间(就绪时间),响应时间(提交到第一次响应)
先来先服务:

最短作业:(吞吐量up,单位时间内运行的任务更多)
高响应比:

时间片轮转:

最高优先级:
非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程;
抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行;
完全公平调度:静态优先级和动态优先级,红黑树;

多级反馈队列:
【多级】表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
【反馈】表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;
如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;


可以发现,对于要办理短业务的客户来说,可以很快的轮到并解决。对于要办理长业务的客户,一下子解决不了,就可以放到下一个队列,虽然等待的时间稍微变长了,但是轮到自己的办理时间也变长了,也可以接受,不会造成极端的现象,可以说是综合上面几种算法的优点。

内核进程调度过程?

linux中有个queue,分为活动队列和过期队列,内核从优先级高到低遍历活动queue,找到进程运行,运行完这个优先级的进程,继续往后遍历,直到全部进程都运行完毕,活动队列和过期队列的指针内容交换,又有新的一批活动队列;

进程的地址空间按低地址到高地址系统地讲述一下,为什么要用进程地址空间?

代码区,bss段,data段,堆,共享区,栈,环境变量命令行参数,内核空间;
进程虚拟空间地址和页表互相独立,不会出现多进程之间地址冲突的问题;

写时拷贝?为什么要写时拷贝?

fork之前的代码由父进程执行,之后的代码父子进程都可以执行,可以由if分流,父子进程代码共享,但数据各自开辟空间,使用写时拷贝; 父子进程一开始使用共同的代码和数据,子进程要修改数据的时候,会将父进程数据拷贝过去; 写时分配用到的时候再分配内存,减少内存浪费,高效使用内存;

请问就绪状态的进程在等待什么?

等待被调度占用CPU使用权

空闲的进程和阻塞的进程状态会不会在唤醒的时候误判?

不会,内核通过不同的链表来管理不同状态的进程PCB

一个进程最多能创建多少个线程?

32 位系统,用户态的虚拟空间只有 3G,如果创建线程时分配的栈空间是 10M,那么一个进程最多只能创建 300 个左右的线程。虚拟空间上限限制;
64 位系统,用户态的虚拟空间大到有 128T,理论上不会受虚拟内存大小的限制,而会受系统的参数或cpu性能限制

优先级如何修改?

PRI和NI加起来才是真正执行的优先级,修改优先级只需要修改top + r 修改NICE值就可以了;

二、进程间通信方式

进程间有几种通信方式?

1.管道,匿名管道:父子间通信;管道是内核里的缓存,是特殊的文件,不在文件系统里面;匿名管道的生命周期,是随进程的创建而建立,随进程的结束而销毁。

有名管道:创建了管道的设备文件,可以在不相关的进程之间进行通信;(无格式的字节流
2.消息队列;保存在内核的消息链表中,有固定的消息体大小(双态数据拷贝开销,消息体大小有限制
3.共享内存;不同进程分配一段虚拟内存空间,映射到相同的物理内存中;
4.信号量;不用于缓冲数据,而用于进程之间的同步和互斥;
5.信号;唯一的异步通知机制;进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号
5.套接字;不同主机之间的通信;

Socket编程

共享内存怎么实现?如果两个进程同时读写这块内存怎么办?

 共享内存:进程中拿出一段虚拟地址,映射道相同的物理内存中,不需要拷贝;
多个进程共同读写共享内存,内存容易混乱,需要借助其他的进程间通信方式,实现进程的同步;

三、同步与互斥

线程安全是什么?怎么实现?

线程安全就是多线程同时执行的时候和单个线程执行的时候,结果和变量不变,那就是线程安全的;
线程安全问题都是有全局变量和静态变量引起的,我们可以采取的方法有1.加锁互斥访问;2.线程本地化,为共享变量创建本地副本;3.非阻塞同步,没有其他线程争用才能操作成功;

同步与互斥?线程同步方法?

线程同步指的是多个线程安装预定的执行顺序去执行;
互斥指的是同一时间只能有一个线程去访问共享资源;
同步:信号量,事件通知;
互斥:互斥锁,临界区;

生产者消费者问题

哲学家就餐问题

读者写者问题

四、死锁

死锁?必要条件?如何排查?

多个进程/线程由于资源竞争而产生的互相等待导致无法继续推进下去的情况叫死锁;
必要条件:
①互斥
②持有并等待
③不可剥夺
④环路等待
排查:pstack + gdb
在定位死锁问题时,我们可以多次执行 pstack 命令查看线程的函数调用过程,多次对比结果,确认哪几个线程一直没有变化,且是因为在等待锁,那么大概率是由于死锁问题导致的。

info thread--> thread 2 -->bt --> frame3 -->p mutexA -->mutex B;
打印了所有的线程信息;切换到第 2 个线程;打印线程的调用栈信息;
打印调用栈中的第三个帧的信息,可以看到线程 B 函数,在获取互斥锁 A 的时候阻塞了;
打印互斥锁 A 对象信息,可以看到它被 LWP 为 87747(线程 A) 的线程持有着;
打印互斥锁 B 对象信息,可以看到他被 LWP 为 87748 (线程 B) 的线程持有着;

如何预防死锁?

①顺序上锁,破坏环路等待;
②限时加锁,在一定时间内没有获得所需要的锁,就回退释放已获得的锁;破坏不可剥夺

自旋锁和信号量是什么?有什么区别?

自旋锁加锁失败后,线程会忙等待(自旋),直到它拿到锁;(不释放cpu资源)如果线程A持有自旋锁时间过长, 显然会浪费处理器的时间,降低了系统性能;自旋锁只适合短期持有;

互斥锁加锁失败后,线程会释放 CPU ,给其他线程;(释放cpu资源)对于互斥锁加锁失败而阻塞的现象,是由操作系统内核实现的;适用于持有时间较长的锁;

两次线程上下文切换的成本;
上下切换的耗时有大佬统计过,大概在几十纳秒到几微秒之间,如果你锁住的代码执行时间比较短,那可能上下文切换的时间都比你锁住的代码执行时间还要长。
所以,如果你能确定被锁住的代码执行时间很短,就不应该用互斥锁,而应该选用自旋锁,否则使用互斥锁。

自旋锁和信号量可以睡眠吗?为什么?

自旋锁不可以;禁止处理器抢占;
信号量可以;不禁止处理器抢占;

自旋锁和信号量可以用于中断中吗?

中断一般不睡眠;自旋锁不会导致中断睡眠,可以在中断中使用;信号量会导致中断睡眠,不在中断中使用;

读写锁是什么?

写锁是独占锁,因为任何时刻只能有一个线程持有写锁,类似互斥锁和自旋锁,而读锁是共享锁,因为读锁可以被多个线程同时持有。读写锁在读多写少的场景,能发挥出优势
根据偏袒读方还是写方,可以分为读优先锁写优先锁,读优先锁并发性很强,但是写线程会被饿死,而写优先锁会优先服务写线程,读线程也可能会被饿死,那为了避免饥饿的问题,于是就有了公平读写锁,它是用队列把请求锁的线程排队,并保证先入先出的原则来对线程加锁,这样便保证了某种线程不会被饿死,通用性也更好点。

五、堆和栈

堆和栈的区别?堆的作用?等

(见freertos章节)

六、内存

什么是虚拟内存?虚拟内存有什么作用?

虚拟内存是虚假的连续内存,需要通过mmu才能转换成真实的物理内存;
优点:
①扩大内存的地址空间,可以超过真实的物理内存;
②每个进程拥有自己独立的地址空间和独立的页表,相互独立,减少了多进程之间地址冲突的问题;
③页表项里记录了属性标志,例如读写权限,为os提供更好的安全性;
缺点:页表占内存,换入换出消耗时间,虚拟地址转换也消耗时间;

内存管理方式有几种?

主要有分块,分段,分页;和它们之间的结合
分块:把内存分为一块一块的,需要了就分配一大块;
分段:根据程序的逻辑,分成不同属性的段,栈段、堆段、数据段、代码段等
分页:分成大小固定的页,linux是4kb;

为什么要分页分段?

分块(内存碎片严重)——>分段(外部内存碎片和内存交换效率低)——>分页(维护页表数据结构占用内存大,页内部碎片)
每个段的长度不固定,所以多个段未必能恰好使用所有的内存空间;
硬盘读写比内存慢,如果读写大段的内存会导致卡顿;

分页的话一次性写入磁盘的也只有少数的一个页或者几个页,效率高很多;
分页我们可以不用一次性将程序加载入内存,我们建立完映射关系之后,可以等到用到的时候再加载到内存中去;

为什么使用多级页表?

页表一定要覆盖全部物理地址空间,如果单级页表就要100多万个页表项,而二级页表就只需要1024的项;当一级页表没有建立映射关系的时候,对应的二级页表项是可以不用建立的;
假设只有 20% 的一级页表项被用到了,4KB(一级页表) + 20% * 4MB(二级页表)= 0.804MB
节约内存!

解释下虚拟地址,逻辑地址,线性地址,物理地址?

程序在使用的地址,还未经段式内存管理单元映射的叫逻辑地址;
通过段式内存管理单元的映射的地址叫线性地址也是,虚拟地址;
在通过页式内存管理单元映射之后变成真实的物理地址;
补充:Linux 内核所采取的办法是使段式映射的过程实际上不起什么作用。linux每个段都是从0地址开始的整个4GB虚拟空间,相当于屏蔽了逻辑地址;

进程从上往下的地址空间是怎么样的?

内核空间,用户空间(env,栈,共享映射区,堆,bss,data,代码段)详见进程部分;

虚拟内存怎么转换为物理内存的?

①当CPU需要进行地址转换时,它会首先检查快表(TLB),如果找到了对应的映射关系,就不需要访问页表;
②否则就去查页表,但并不是所有虚拟页都有对应的物理内存,找不到会触发缺页中断,os会加载所需要的物理内存,并更新页表;
③然后用MMU转换为物理地址;

内存置换算法?

最佳(最来最长时间不被访问的页面),LRU(最不常用的页面),FIFO(最先加载进来的页面),clock(循环链表和指针,扫描访问位如果是0,置换,如果是1,变成0)

缺页中断?

malloc和mmap的时候只是申请的虚拟地址空间,并没有分配虚拟内存对应的物理内存;当进程访问没有建立映射关系的虚拟内存时,处理器自动触发缺页异常;然后操作系统会建立虚拟内存和物理内存之间的映射关系。

当访问的页面不在内存中时,会产生一次缺页中断;具体过程是:页表状态位,是否在内存里--》不在--》缺页中断--》如果内存没满,直接将对应的页调入内存(如果内存满了,同时还要调出一个页回收内存)

系统调用是什么?和库函数有什么区别?

系统调用:是通向操作系统内核的接口,面向底层硬件的;可以从用户态进入内核态;
库函数:把函数放到库里,方便其他人使用;
区别:①可移植性,依赖平台与否;②运行的用户地址空间还是内核地址空间;③开销大小;

malloc的底层是什么?

malloc分配的是虚拟内存;

1.通过brk()从堆分配 < 128k

malloc 通过 brk( 方式申请的内存,free 释放内存的时候,并不会把内存归还给操作系统,而是缓存在malloc 的内存池中,待下次使用:

为什么不全用brk?如果只使用brk,很容易产生越来越多的不可用碎片,导致内存泄漏;

2.通过mmap()从文件映射区分配 >128k

malloc 通过 mmap( 方式申请的内存,free 释放内存的时候,会把内存归还给操作系统,内存得到真正的释放。

为什么不只用mmap?全都用mmap的话每次申请都需要进行核态的变化和缺页中断,cpu消耗大;brk有内存池,直接取出可以减少核态变化和缺页中断的次数;

free() 函数只传入一个内存地址,为什么能知道要释放多大的内存?

在1G内存的计算机中能否malloc(1.2G)?为什么?

可以的,malloc仅与虚拟内存相关;malloc传入的参数是unsigned的,表示范围是超过1.2g的;

malloc能申请多大的内存?

32位的理论上能申请3g,但堆和映射区不是用户空间的全部,也受其他的影响;(还可以再精细)
64位可以申请很大,128tb,即使物理内存只有4GB,申请8GB也是没问题的;有swap分区的话能正常运行,否则会被oom;

内存满了会怎么样?

后台内存回收(kswapd):异步,不阻碍进程执行
直接内存回收(direct reclaim):同步
OOM (Out of Memory)机制:杀死占用物理内存比较高的进程

两类内存可以被回收:文件页和匿名页;
文件页:回收干净页直接释放内存,回收脏页写回磁盘再释放内存;
匿名页:linux的swap机制;
linux用活跃链表和非活跃链表来管理这些内存页,越尾的就越不常访问,优先回收不活跃的内存;

linux如何避免内存碎片?

伙伴系统;相邻的内存进行合并;

你自己编程的时候如何减少?

①使用智能指针;②避免频繁的小块内存分配和释放;③使用内存池技术;

设计一个内存池?

在对象使用非常频繁的情况下,我们可以预先在堆中实例化一些对象,在需要用的时候,我们直接在内存池里面拿,而不重新构造,释放的时候是返回给内存池,而不是给OS,这样就不用频繁的分配和回收内存,可以提升效率;

给你一个类,有static,virtual,讲讲这个类的内存分布?

static修饰的成员变量,在全局数据区分配内存
static修饰的成员函数,在代码区分配内存 多态分为静态多态,编译的时候确定;
动态多态执行的时候动态绑定;
如果一个类是局部变量则该类数据存储在栈区,如果一个类是通过new/malloc动态申请的,则该类数据存储在堆区。

为什么要有page cache?操作系统怎么设计的page cache?

减少IO操作,加快读取文件的速度;先从page cache(有一部分磁盘的缓存)里面找,命中了就不用去磁盘里面找了;
文件的每个数据块对应一个page cache项,利用双向链表和搜索树两种数据结构来管理,通过文件内偏移快速定位Cache项;

七、IO复用

++i是否原子操作?

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 国内可以使用的ChatGPT服务【9月持续更新】
  • 机器之心 | 阿里云Qwen2.5发布!再登开源大模型王座,Qwen-Max性能逼近GPT-4o
  • 【研发日记】嵌入式处理器技能解锁(六)——ARM的Cortex-M4内核
  • 新书速览|NestJS全栈开发解析:快速上手与实践
  • 数据结构之二叉树查询
  • JetLinks物联网学习(前后端项目启动)
  • HarmonyOS开发者基础认证考试试题
  • 生信初学者教程(七):数据库
  • 【pytorch】RuntimeError: cuDNN error: CUDNN_STATUS_EXECUTION_FAILED 报错
  • 【Unity踩坑】UI Image的fillAmount不起作用
  • Spring中的Web Service消费者集成(应该被淘汰的技术)
  • 【学习笔记】STM32F407探索者HAL库开发(五)F407时钟系统配置
  • MySQL高阶1907-按分类统计薪水
  • Codeforces Round 784 (Div. 4) Kotlin
  • 滚雪球学SpringCloud[4.1讲]: Spring Cloud Gateway详解
  • Linux后台研发超实用命令总结
  • October CMS - 快速入门 9 Images And Galleries
  • php ci框架整合银盛支付
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • QQ浏览器x5内核的兼容性问题
  • Vue UI框架库开发介绍
  • 深入浅出webpack学习(1)--核心概念
  • 鱼骨图 - 如何绘制?
  • 中文输入法与React文本输入框的问题与解决方案
  • kubernetes资源对象--ingress
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • ​iOS安全加固方法及实现
  • ​VRRP 虚拟路由冗余协议(华为)
  • $.proxy和$.extend
  • (12)Linux 常见的三种进程状态
  • (27)4.8 习题课
  • (C语言)字符分类函数
  • (二)linux使用docker容器运行mysql
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (十) 初识 Docker file
  • (数据大屏)(Hadoop)基于SSM框架的学院校友管理系统的设计与实现+文档
  • (转)负载均衡,回话保持,cookie
  • (转载)Google Chrome调试JS
  • **PHP二维数组遍历时同时赋值
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET CLR Hosting 简介
  • .net dataexcel 脚本公式 函数源码
  • .net mvc部分视图
  • .Net(C#)自定义WinForm控件之小结篇
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .Net6使用WebSocket与前端进行通信
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .net网站发布-允许更新此预编译站点
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • .考试倒计时43天!来提分啦!
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • @Slf4j idea标红Cannot resolve symbol ‘log‘
  • [.NET 即时通信SignalR] 认识SignalR (一)