操作系统知识点

1、进程调度几种方式

1、先来先服务
2、基于优先级调度:抢占/非抢占
3、基于时间片调度,多级反馈队列调度算法
4、短作业(进程)优先调度算法
参考

3、进程同步和线程同步的区别

首先我们知道,linux下每个进程都有自己的独立进程空间,假设A进程和B进程各有一个互斥锁,这个锁放在进程的全局静态区,那么AB进程都是无法感知对方的互斥锁的。线程同步和进程同步的本质区别在于锁放在哪,放在私有的进程空间还是放在多进程共享的空间,并且看锁是否具备进程共享的属性。
Linux 下常见的进程同步方法有: 1、信号量 2、管程 3、 互斥量(基于共享内存的快速用户态 ) 4、文件锁(通过 fcntl设定,针对文件)针对线程(pthread)的还有 pthread_mutex 和 pthread_cond(条件变量)。
线程的同步方法:1、信号量 2、互斥量 3、临界区 4、事件

4、死锁相关

产生死锁的必要条件

  • 互斥:在同一段时间内资源只能被一个进程使用,如果其他进程申请该资源必须等待;
  • 请求与保持:一个线程在请求新资源时,其已经获得的资源并不释放,而是继续持有;
  • 不可抢占:分配给进程的资源,除非进程自己释放,其他进程不可抢夺资源使用;
  • 循环等待:发生死锁时,存在一个进程-资源-进程的等待循环链;
    预防死锁,只要破坏死锁的四个必要条件中的一个即可。
  • 互斥:不太可能,如打印机本身就不能同时访问。
  • 不可抢占:对已获取某些资源的进程如果获取不到新的资源则放弃自身所有的资源。–>增加系统开销,降低系统吞吐量
  • 请求与保持:可一次申请全部资源–>导致资源浪费
  • 循环等待:采用有序资源分配法–>限制新资源增加、资源浪费、编码复杂。
    死锁避免:在资源分配之前,计算分配是否能让系统处于安全状态[能否找到安全序列]。银行家算法。
    死锁检测:等死锁发生之后再进行,在单个实例化的资源类型中,如果系统中正在形成一个循环,那么肯定会出现死锁。
    死锁恢复
  • 简单终止一个或多个进程,打破循环等待。
  • 从一个或多个死锁进程那里抢占资源。
    可参考:https://www.jianshu.com/p/dae6ce64bbf9

5、文件名和文件权限是存在一块吗

innode不存文件名,存权限、访问日期、指向数据的指针等;
文件控制块(FCB)存储:文件的名字、地址、大小、结构、类型,FCB存放在目录块中。
在这里插入图片描述

6、MD5码

MD5码可以唯一地代表原信息的特征,通常用于密码的加密存储,数字签名,文件完整性验证等,更改文件名、扩展名、存放路径、其他属性都不会改变MD5值。

7、页置换的方法:

  • 最佳置换算法(OPT)(理想置换算法):从主存中移出永远不再需要的页面;如无这样的页面存在,则选择最长时间不需要访问的页面
  • 先进先出置换算法(FIFO)
  • 最近最久未使用(LRU)算法:当需要淘汰一个页面时,总是选择在最近一段时间内最久不用的页面予以淘汰。
    即淘汰最近最长时间未访问过的页面。
  • 时钟(CLOCK)置换算法:当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用位也被置为1。每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;如果在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换。如果所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并且停留在最初的位置上,替换该帧中的页。
  • 最不常用页面置换算法(NFU):用一个软件模拟LRU,该算法将每个页面与一个软件计数器相关联。计数器的初值为0。每次时钟中断时,由操作系统扫描内存中所有的页面,将每个页面的R位(它是0或1)加到它的计数器上。这个计数器大体上跟踪了各个页面被访问的频繁程度。发生缺页中断时,则置换计数器值最小的页面。

8、如何检测内存泄露:

包括静态分析技术和源代码插装技术,另一方面我们在写代码时可以添加内存申请和释放的统计功能,统计当前申请和释放的内存是否一致,以此来判断内存是否泄露。可参考:https://blog.csdn.net/weixin_41289588/article/details/88694305
Linux系统下内存泄漏的检测工具(valgrind)
valgrind --tool=memcheck ./test

9、生产者消费者模式

  • 所谓生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据。
  • 为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。
  • 如果共享数据区已满的话,阻塞生产者继续生产数据放置入内;
  • 如果共享数据区为空的话,阻塞消费者继续消费数据;
    在这里插入图片描述

10、进程间通信方法:

  • 无名管道:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  • 有名管道:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
  • 信号:信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  • 消息队列:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 套接字: 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

11、同步异步阻塞非阻塞

  • 异步同步反映的是执行者执行方式,同步表示执行者主动查询,异步表示自己不主动查询而是等待消息等机制通知。
  • 阻塞非阻塞反映的是执行者执行状态,阻塞表示执行者等待,非阻塞表示执行者不等做其他事情。
  • 参考

12、如何获得main函数返回的异常。

  • linux可以用wait和waitpid等函数捕获子进程的退出码。
  • windows上使用GetExitCodeProcess捕获。
  • 如果使用system执行程序的话,该程序的退出码就是system的返回值。
  • 如果没有考虑到进程的创建而是程序直接退出了,linux下可以用shell特殊变量$?获取。
  • 可以声明全局变量报告错误码,用atexit注册一个函数,退出时打印该错误码。

十二、linux文件系统根目录下有什么文件夹

在这里插入图片描述
在这里插入图片描述
参考

13、epoll触发的事件

1、listen fd,有新连接请求,触发EPOLLIN。
2、对端发送普通数据,触发EPOLLIN。
3、带外数据,只触发EPOLLPRI。
4、对端正常关闭(程序里close(),shell下kill或ctr+c),触发EPOLLIN和EPOLLRDHUP,但是不触发EPOLLERR和EPOLLHUP。
5、(server端)出错会触发EPOLLERR或者EPOLLHUP。
6、对端异常断开连接(只测了拔网线),没触发任何事件。

14、游戏服务器应该为每个用户开辟一个线程还是一个进程

游戏服务器应该为每个用户开辟一个进程。因为同一进程间的线程会相互影响,一个线程死掉会影响其他线程,从而导致进程崩溃。因此为了保证不同用户之间不会相互影响,应该为每个用户开辟一个进程

15、操作系统的内存管理

动态的是进程存在内存,静态的是进程存在外存。

16、静态变量什么时候初始化

  • 物理内存管理包括交换与覆盖,分页管理,分段管理和段页式管理等;
  • 虚拟内存管理包括虚拟内存的概念,页面置换算法,页面分配策略等;

17、协程

  • 协程的切换者是用户(编程者或应用程序),切换时机是用户自己的程序所决定的。协程的切换内容是硬件上下文,切换内存保存在用户自己的变量(用户栈或堆)中。协程的切换过程只有用户态,即没有陷入内核态,因此切换效率高。
  • 一个线程内的多个协程虽然可以切换,但是多个协程是串行执行的,只能在一个线程内运行,没法利用CPU多核能力,而且进程堵塞操作会堵塞掉整个程序。

18、线程池

  • 线程池是存储线程的容器,线程事先创建好后放入线程池,当有任务需要执行时,直接从线程池拿空闲线程使用,使用完毕后归还给线程池。
  • 简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建T1和销毁时间T3相比任务执行时间T2可以忽略不计,则没有必要使用线程池了。反之如果T1+T3>T2,那就很有必要使用线程池。

19、写者饥饿问题

解决写者饥饿问题,在读锁状态下当有写者请求写锁时,后面请求读锁的先等待,写锁释放后再加读锁。

20、惊群效应

定义:惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。
解决:

  • Linux 2.6 版本之后,通过引入一个标记位Q_FLAG_EXCLUSIVE,解决掉了 accept 惊群效应。所以wake_up函数中WQ_FLAG_EXCLUSIVE为真的时候,执行一次,就会跳出循环。只选择一个进程。
  • epoll 的惊群效应,包括epoll_create 在 fork 之前创建epoll_create 在 fork 之后创建两种情况。前者解决思路同accept且解决。后者可参考Nginx实现:当一个连接来的时候,此时每个进程的 epoll 事件列表里面都是有该 fd 的。抢到该连接的进程先释放锁,再 accept。没有抢到的进程把该 fd 从事件列表里面移除,不必再调用 accept,造成资源浪费。

21、文件系统布局

文件系统存放在磁盘上。多数磁盘划分为一个或多个分区,每个分区中有一个独立的文件系统。磁盘的0号扇区称为主引导记录(Master Boot Record,MBR),用来引导计算机。在MBR的结尾是分区表。该表给出了每个分区的起始和结束地址,表中的一个分区被标记为活动分区。在计算机被引导时,BIOS读入并执行MBR。MBR做的第一件事就是确定活动分区,读入它的第一个块,称为引导块,并执行之。引导块中的程序将装载该分区中的操作系统。为统一起见,每个分区都从一个启动块开始,即使它不含有一个可启动的操作系统。
在这里插入图片描述
在文件系统中,用来分配空间的基本单位是逻辑块,亦即文件系统所在磁盘设备上若干连续的物理块。下图是Linux的磁盘分区和文件系统布局。
在这里插入图片描述

22、关于数据(用户数据)和元数据区别

  • 任何文件系统中的数据都分为数据和元数据。数据是指普通文件中的实际数据,而元数据指的是描述一个文件特征的系统数据(各种属性),比如访问权限、文件拥有者、文件数据分布信息等。
  • 在Linux中,元数据中的inode号(inode是元数据的一部分,但其不包含文件名)才是文件的唯一而非文件名。文件名是为了方便人们的记忆和使用,系统或程序通过inode号寻找正确的文件数据块。

23、inode节点

  • 文件数据都存放在block中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为“索引节点”。
  • inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
  • 每个inode都有一个号码,操作系统用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或绰号。
  • 用户通过文件名,打开文件。实际上,系统内部这个过程分为三步:系统找到这个文件名对应的inode号码,通过inode号码,获取inode信息,根据inode信息,找到文件数据所在的block,读出数据。

24、硬链接与软链接的区别与联系

硬连接:

  • 在Linux操作系统中,若一个inode号对应多个文件名,则称这些文件为硬链接。换言之,硬链接就是同一个文件使用多个别名。通过stat命令就可以查看到一个文件的inode号以及硬链接数。
  • 文件有相同的inode和data block;
  • 只能对已存在的文件进行创建;
  • 删除一个硬链接文件并不影响其他有相同inode号的文件。同一个文件的所有硬链接地位平等。

软连接:

  • 软连接指向的是文件名;
  • 软链接有自己的文件属性和权限等;
  • 可对不存在的文件或者目录创建软链接;
  • 删除软链接并不会影响被指向的文件,但若被指向的源文件被删除,则相关软链接被称为死链接,若被指向路径文件被重新创建,死链接可回复为正常的软链接。

25、扇区和块

  • 文件储存在硬盘上,硬盘的最小存储单位叫做“扇区”。每个扇区能储存512字节(相当于0.5KB)
  • 操作系统在读取硬盘的时候,不会一个个扇区的读取,这样效率太低,而是一次性连续读多个扇区,即一次性读取一“块”(block)。这种由多个扇区组成的“块”,是文件存取的最小单位。“块”的大小,最常见的是4kb,即连续八个sector组成一个block。

26、磁盘冗余

RAID(独立磁盘冗余阵列)技术主要是为了改善磁盘的存储容量,读写速度,增强磁盘的可用性和容错能力。目前服务器级别的计算机都支持插入多块磁盘(8块或者更多),通过使用RAID技术,实现数据在多块磁盘上的并发读写和数据备份。
在这里插入图片描述

27、多进程和多线程的适用场景

  • 多进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。 但是切换成本比较高,开销大。
  • 多线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。
  • 单线程的选择:对于处理时间短的服务或者启动频率高的要用单线程,相反用多线程。某此操作允许并发而且该操作有可能阻塞时, 用多线程。例如SOCKET, 磁盘操作。

28、系统调用过程

  • 将处理机状态由用户态转为系统态;之后,由硬件和内核程序进行系统调用的一般性处理,即首先保护被中断进程的CPU环境,将处理机状态字PSW、程序计数器PC、系统调用号、用户找指针以及通用寄存器内容等压入堆栈;然后,将用户定义的参数传送到指定的地方保存起來。
  • 分析系统调用类型,转入相应的系统调用处理子程序。为使不同的系统调用能方便地转向相应的系统调用处理子程序,在系统中配置了一张系统调用入口表。表中的每个表目都对应一条系统调用,其中包含该系统调用自带参数的数目、系统调用处理子程序的入口地址等。内核可利用系统调用号去查找该表,即可找到相应处理子程序的入口地址而转去执行它。
  • 在系统调用处理子程序执行完后,恢复被中断的或设置新进程的CPU现场,然后返冋被中断进程或新进程,继续往下执行。

29、buffer和cache区别

  • Cache:因为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而 Cache保存着CPU刚用过的数据或循环使用的部分数据,这时从Cache中读取数据会更快,减少了CPU等待的时间,提高了系统的性能。Cache并不是缓存文件的,而是缓存块的(块是I/O读写最小的单元)。一般会用在I/O请求上,如果多个进程要访问某个文件,可以把此文件读入Cache中,这样下一个进程获取CPU控制权并访问此文件直接从Cache读取,提高系统性能。
  • Buffer:缓冲区,用于存储速度不同步的设备或优先级不同的设备之间传输数据;通过buffer可以减少进程间通信需要等待的时间,当存储速度快的设备与存储速度慢的设备进行通信时,存储慢的数据先把数据存放到buffer,达到一定程度存储快的设备再读取buffer的数据,在此期间存储快的设备CPU可以干其他的事情。 一般是用在写入磁盘的,例如:某个进程要求多个字段被读入,当所有要求的字段被读入之前已经读入的字段会先放到buffer中。
  • cache 是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到加快访问速度的作用。而 buffer 的主要目的进行流量整形,把突发的大数量较小规模的 I/O 整理成平稳的小数量较大规模的 I/O,以减少响应次数

30、页的大小为什么是4k

  • 假设内存一定的话,页面大小越大,管理页面占用的内存也越小。现在内核中每个页面假设是4K的话,这4K不是全都可用,还有一部分用作struct page。
  • 每次访问内存的时候,都要将虚拟地址转换为物理地址,如果每次都访问页表的话,消耗比较大。因此,通常使用TLB来加速这个过程。但是TLB的可以直接转换的地址范围是有限的(具体就是项数乘以页面大小),一旦出现TLBmiss,这时就必须去页表中查找。所以,如果是大页面的话,同样TLB项数的情况下,可以跟踪更大的内存
  • 最大的问题就是内存浪费,而且这个问题非常严重。比如这时要分配的内存是4M+1byte,这时需要两个页面才能满足分配的需要,这个时候浪费的内存为4M-1byte。
  • 页面太大,会导致大量的内存碎片。如果是小页面的话,内存的利用会比较紧凑,分配页面时需要的连续内存块的大小不像大页面那样需要的那么大。

31、线程池线程数设置

CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务。
IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数。

32、软中断和硬中断区别

  • 软中断:通常是硬中断服务程序对内核的中断;为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。
  • 硬中断:由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。对CPU进行中断。

33、虚拟内存和swap分区

  • windows即使物理内存没有用完也会去用到虚拟内存,而Linux不一样 Linux只有当物理内存用完的时候才会去动用虚拟内存(即swap分区)。
  • swap类似于windows的虚拟内存,不同之处在于,Windows可以设置在windows的任何盘符下面,默认是在C盘,可以和系统文件放在一个分区里。而linux则是独立占用一个分区,方便由于内存需求不够的情况下,把一部分内容放在swap分区里,待内存有空余的情况下再继续执行,也称之为交换分区,交换空间是其中的部分。
  • windows的虚拟内存是电脑自动设置的。
  • Linux的swap分区是你装系统的时候分好的。

34、进程的状态转换图在这里插入图片描述

35、子进程继承父进程的什么内容

  • 会继承大部分内容,包括父进程的地址空间和文件描述符。
  • 不会继承内存锁、文件锁、某些定时器。

36、什么是管程

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

37、生产者消费者问题

为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。
注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等待下,不会释放锁,消费者因此也会永远等待下去。

#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void producer() {
	while(TRUE) {
		int item = produce_item();
		down(&empty);
		down(&mutex);
		insert_item(item);
		up(&mutex);
		up(&full);
	}
}
void consumer() {
	while(TRUE) {
		down(&full);
		down(&mutex);
		int item = remove_item();
		consume_item(item);
		up(&mutex);
		up(&empty);
	}
}

38、读写者问题

允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。
一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量 data_mutex 用于对读写的数据加锁。

typedef int semaphore;
semaphore count_mutex = 1;
semaphore data_mutex = 1;
int count = 0;
void reader() {
	while(TRUE) {
		down(&count_mutex);
		count++;
		if(count == 1) down(&data_mutex); // 第一个读者需要对数据进行加锁,防止写进程访问
		up(&count_mutex);
		read();
		down(&count_mutex);
		count--;
		if(count == 0) up(&data_mutex);
		up(&count_mutex);
		}
	}
	void writer() {
	while(TRUE) {
		down(&data_mutex);
		write();
		up(&data_mutex);
	}
}

39、共享内存生存期

进程结束后被自动释放。

40、进程挂了怎么查看

  • 查看系统日志配置情况: /etc/syslog.conf
  • 默认系统日志在/var/log/messages里,在这个文件中以进程名或进程ID查找,一般都能找到进程生死历史信息。

41、两级页表

为了解决一级页表需要占连续4m空间而设置,转换成了1024*1024。一级页表是20位页号,12位偏移。因为并不是进程的每一个页面都要调入内存,所以只有部分页面有对应内存的物理块号,所以物理块号的大小( 2^12 )会小于页号大小( 2^20 )。
在这里插入图片描述
参考
参考

42、可执行文件怎么变成进程

创建一个进程,然后装载相应的可执行文件并且执行。最开始只需要做三件事情:
①创建一个独立的虚拟地址空间。主要是分配一个页目录(Page Directory)。
②读取可执行文件的头,并且建立虚拟空间和可执行文件的映射关系。主要是把可执行文件映射到虚拟地址空间,即做虚拟页和物理页的映射,以便“缺页”时载入。
③将CPU的指令寄存器设置成可执行文件的入口地址,启动运行。从ELF文件中的入口地址开始执行程序。

43、进程组、会话、守护进程

  • 进程组是多个进程的组合,子进程和父进程属于一个进程组。每个进程组有一个组长。所有进程结束之后进程组才会结束。
  • 会话是多个进程组的组合,包括前后台进程组。组长不能新建会话,新建会话的进程为组长进程。
  • 守护进程(daemon)是一类在后台运行的特殊进程。参考

43、进程异常退出共享内存会销毁吗

Linux中通过API函数shmget创建的共享内存一般都是在程序中使用shmctl来释放的,但是有时为了调试程序,开发人员可能通过Ctrl + C等方式发送中断信号来结束程序,此时程序申请的共享内存就不能得到释放,当然如果程序没有改动的话,重新运行程序时仍然会使用上次申请的共享内存,但是如果我们修改了程序,由于共享内存的大小不一致等原因会导致程序申请共享内存错误。因此,我们总是希望每次结束时就能释放掉申请的共享内存。
有两种方法可以用来释放共享内存:
第一种:如果总是通过Crtl+C来结束的话,可以做一个信号处理器,当接收到这个信号的时候,先释放共享内存,然后退出程序。
第二种:不管你以什么方式结束程序,如果共享内存还是得不到释放,那么可以通过linux命令ipcrm shm shmid来释放,在使用该命令之前可以通过ipcs -m命令来查看共享内存。

44、为什么线程没有进程安全

  • 线程可以通过内存地址访问到其他线程的栈空间,可能会导致别的线程出现崩溃。
  • 严格的说没有“线程崩溃”,只是触发了SIGSEGV (Segmentation Violation/Fault)。如果没有设置对应的Signal Handler操作系统就自动终止进程(或者说默认的Signal Handler就是终止进程)

45、CAS

  • CAS的Comple And Swap的缩写,简单翻译过来就是比较并且覆盖。

  • 在CAS机制中存在三个基本操作值,V内存值,A旧预期值,B预期值。
    在这里插入图片描述

  • 线程一进入内存值V为10,然后,老预期值A为10,预期值B为11,然后进行重新赋值V=11;

  • B与A同时进入线程,所以同样获取老预期值B为10,(此时线程一已经操作完成,并且把主内存的值更新为11),然后进行加一操作,此时会把V内存值(此时已经被线程一更改为11)跟旧预期值比较,如果一致,则进行更改内存值。假如内存值跟老预期值不一致,则进行循环操作,再次获取内存值,然后设置老预估值,然后进行加一计算出预估值,然后进行内存值与老期待值进行对标(V与A对比,如果通过对比),覆盖内存V的值。

缺点:

  • CPU开销较大
  • 不能保证代码块的原子性
  • ABA

参考

46、优先级反转

  • 高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上来看,好像是中优先级的任务比高优先级任务具有更高的优先权。
  • 具体来说:当高优先级任务正等待信号量(此信号量被一个低优先级任务拥有着)的时候,一个介于两个任务优先之间的中等优先级任务开始执行——这就会导致一个高优先级任务在等待一个低优先级任务,而低优先级任务却无法执行类似死锁的情形发生。
  • 解决方法,优先级继承:优先级继承就是为了解决优先级反转问题而提出的一种优化机制。其大致原理是让低优先级线程在获得同步资源的时候(如果有高优先级的线程也需要使用该同步资源时),临时提升其优先级。以前其能更快的执行并释放同步资源。释放同步资源后再恢复其原来的优先级。

47、栈往下增长原因

在这里插入图片描述

48、多线程地址分布空间

在这里插入图片描述

49、如何实现无锁队列

主要用死循环+CAS实现,参考

50、进程锁

  • 修改同步对象的属性为PTHREAD_PROCESS_SHARED
  • 在进程的特殊内存区域–共享内存中创建同步对象,参考

51、fork和vfork

  1. fork ():子进程拷贝父进程的数据段,代码段
    vfork ( ):子进程与父进程共享数据段
  2. fork ()父子进程的执行次序不确定
    vfork 保证子进程先运行,在调用exec 或exit 之前与父进程数据是共享的,在它调用exec或exit之后父进程才可能被调度运行。

52、微内核和宏内核

  • 微核心的设计理念,是将系统服务的实作,与系统的基本操作规则区分开来。它实作的方式,是将核心功能模组化,划分成几个独立的程序,各自运行,这些程序被称为服务。所有的服务程序,都运行在不同的地址空间。只有需要绝对特权的程序,才能在具特权的执行模式下运行,其余的程序则在使用者空间运行。
  • 宏内核简单的说就是把整个内核设计成一个大程序,它的所有功能都集中在一个层次,对外提供一个完整的内核界面,即系统调用。内核中的各种函数可以相互直接调用,汇编程序和C程序可以相互跳转和调用,用一个整体的大程序来实现内核功能,没有微内核的分层结构。宏内核的好处是简单,便于理解和实现。Linux之所以很快的流行,也在于它采用宏内核,设计简单。相对于微内核来说,宏内核的效率略高。基本上每个系统调用只需要经过一个函数调用就可以实际作用于硬件层,速度很快。

53、零拷贝

  • 零拷贝指的是在进行操作时,避免CPU从一处存储拷贝到另一处存储。在Linux中,我们可以减少数据在内核空间和用户空间的来回拷贝实现,比如通过调用mmap()来代替read调用。
  • 用程序调用mmap(),磁盘上的数据会通过DMA被拷贝的内核缓冲区,接着操作系统会把这段内核缓冲区与应用程序共享,这样就不需要把内核缓冲区的内容往用户空间拷贝。应用程序再调用write(),操作系统直接将内核缓冲区的内容拷贝到socket缓冲区中,这一切都发生在内核态,最后,socket缓冲区再把数据发到网卡去。

54、mmap和write、read的区别

  • 常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。
  • 使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。

55、RCU

  • RCU(Read-Copy Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它,但写者在访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值