Linux内核相关面试题

1.组成原理相关

冯诺依曼结构,哈佛结构

单片机为了存储器管理的方便(便于支持操作系统),一般采用指令、数据空间统一编码的冯·诺依曼结构。 DSP为了提高数据吞吐的速度,基本上都是指令、数据空间独立的哈佛结构。

因为ARM7系列采用冯·诺依曼体系结构,而ARM9~ARM11采用哈佛体系机构

 

ROM,RAM

RAM又被称作“随机存储器”,有片内ram,也有片外ram,

片内ram用来存放中断处理handler、RTOS调度器、任务上下文切换、内存分配释放等使用频率最高的代码和中断堆栈这种读写频率极高的内存区,如果有多余的部分也可以放一些经常被引用到的全局变量。

片外RAM一般就是用来存储全局变量,bss,以及我们常用到的malloc所分配的堆空间等。

 

ROM一般是有两种,一种是指集成在CPU芯片内部的一块只读存储区域,一般是几K到几十K字节大小,用来存储系统刚上电时对cpu和一些核心外设(如时钟,串口,MMU、DRAM、Flash等)进行初始化的代码,它在程序运行中也是不可写的,要对它执行写操作只能使用硬件烧写器进行,也就是一般所说的下载程序,这部分的代码在芯片测试阶段可以进行编程器下载更新,量产后一般就会固化,不能做任何修改的;

ROM另一种指的就是flash。首先需要说明的是,很多做嵌入式应用开发的同学一直把flash比作PC上的硬盘,其实它们指的是Nand flash,而对于很多小型的嵌入式系统,就只有一个2M或者4M的Nor Flash,它和硬盘有一个显著的区别:flash里存放的代码是可以由CPU直接取指并执行的,而PC上硬盘里的程序都需要加载到内存里才能运行。flash并不是绝对的运行时不可写,有时候应用程序需要保存一些配置信息到flash里,类似于PC程序的配置文件,以保证掉电了之后它的内容不会丢失,下次开机时可以直接从flash读取到。

 

 

首先考虑一下,有没有什么东西必须放在ROM里? 当然有,引导程序(系统的初始化代码)就必须放到ROM里。在CPU刚上电时,只能去一个默认的地址去取第一条指令,开始干活,这个地址都是映射到片内的ROM里,原因很简单,此时,作为外设的flash和DDR等都还没有初始化,CPU根本无法从它们那里读写数据,片内ROM里的这些代码就需要完成这些模块的初始化。另外,一个项目的处理器和主要外设确定了以后,这部分初始化代码在很长的时间里,都不需要做任何修改的。   

 

 

用户空间与内核通信方式有哪些?

 1)首先想到的是系统调用,用户空间进程通过系统调用进入内核空间,访问指定的内核空间数据;

 2).其次是驱动程序,用户空间进程可以使用封装后的系统调用接口访问驱动设备节点,以和运行在内和空间的驱动程序通信;

 3).共享内存mmap,在代码中调用接口,实现内核空间与用户空间的地址映射,在实时性要求很高的项目中为首选,省去拷贝数据的时间等资源,但缺点是不好控制;

 4).最后,copy_to_user()、copy_from_user(),是在驱动程序中调用接口,实现用户空间与内核空间的数据拷贝操作,应用于实时性要求不高的项目中。

 

 

系统调用read()/write(),内核具体做了哪些事情

 用户空间read()-->内核空间sys_read()-->scull_fops.read-->scull_read();

该过程分为两个部分:用户空间的处理和核心空间的处理。在用户空间中通过 0x80 中断的方式将控制权交给内核处理,内核接管后,经过6个层次的处理最后将请求交给磁盘,由磁盘完成最终的数据拷贝操作。在这个过程中,调用了一系列的内核函数

 

内核态与用户态的区别

    系统态(也称为管态或核心态),操作系统在系统态运行——运行操作系统程序

    用户态(也称为目态),应用程序只能在用户态运行——运行用户程序

     当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。Ring3状态不能访问Ring0的地址空间,包括代码和数据;当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。

    用户运行一个程序,该程序创建的进程开始时运行自己的代码,处于用户态。如果要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,这些系统调用会调用内核的代码。进程会切换到Ring0,然后进入3G-4G中的内核地址空间去执行内核代码来完成相应的操作。内核态的进程执行完后又会切换到Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。这说的保护模式是指通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。

 

 

系统的启动过程

整个系统的加载启动任务就完全由BootLoader来完成。BootLoader就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。

 

启动顺序:bootloader->linuxkernel->rootfile

后者需要前者提供功能支持,前者的目的就是启动后者。

嵌入式Linux系统启动过程Linux引导的整个过程:当系统首次引导时,或系统被重置时,处理器会执行一个位于Flash/ROM中的已知位置处的代码,Bootloader就是这第一段代码。它主要用来初始化处理器及外设,然后调用Linux内核。Linux内核在完成系统的初始化之后需要挂载某个文件系统作为根文件系统(RootFilesystem),然后加载必要的内核模块,启动应用程序。(一个嵌入式Linux系统从软件角度看可以分为四个部分:引导加载程序(Bootloader),Linux内核,文件系统,应用程序。)

嵌入式Linux系统通过Bootloader引导,一上电,就要执行Bootloader来初始化系统。在完成对系统的初始化任务之后,它会将非易失性存储器(通常是Flash或DOC等)中的Linux内核拷贝到RAM中去,然后跳转到内核的第一条指令处继续执行,从而启动Linux内核。Bootloader和Linux内核有着密不可分的联系。

 

 

 

进程相关

IPC(进程间通信)机制中最快的,共享内存,只需要两次拷贝而已

 

 

内核抢占

内核抢占:如果进程正在执行内核函数时(即它在内核态运行时),允许发生内核切换(被替换的进程是正在执行内核函数的进程),这个内核就是抢占的。

运行在内核态的进程可以自动放弃cpu,称为计划性进程切换,抢占式内核中,进程被迫放弃CPU,称为强制性进程切换。抢占内核的主要特点是:一个内核态运行的进程,可能在执行内核函数期间被另一个进程取代。
 

 

 Linux内核同步方式总结 

https://blog.csdn.net/yyf_it/article/details/52416994

为什么自旋锁不能睡眠 而在拥有信号量时就可以

自旋锁禁止处理器抢占;而信号量不禁止处理器抢占。

基于这个原因,如果自旋锁在锁住以后进入睡眠,由于不能进行处理器抢占,其他系统进程将都不能获得CPU而运行,因此不能唤醒睡眠的自旋锁,因此系统将不响应任何操作(除了中断或多核的情况,下面会讨论)。而信号量在临界区睡眠后,其他进程可以用抢占的方式继续运行,从而可以实现内存拷贝等功能而使得睡眠的信号量程序由于获得了等待的资源而被唤醒,从而恢复了正常的代码运行。

 

内存模块相关

简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?画出CPU核、cache、MMU、内存之间的关系示意图加以说明。

现代操作系统普遍采用虚拟内存管理(Virtual Memory Management) 机制,这需要MMU(Memory Management Unit,内存管理单元) 的支持。有些嵌入式处理器没有MMU,则不能运行依赖于虚拟内存管理的操作系统。

也就是说:操作系统可以分成两类,用MMU的、不用MMU的。用MMU的是:Windows、MacOS、Linux、Android;不用MMU的是:FreeRTOS、VxWorks、UCOS……与此相对应的:CPU也可以分成两类,带MMU的、不带MMU的。带MMU的是:Cortex-A系列、ARM9、ARM11系列;不带MMU的是:Cortex-M系列……(STM32是M系列,没有MMU,不能运行Linux,只能运行一些UCOS、FreeRTOS等等)。

CPU通过地址来访问内存中的单元,如果CPU没有MMU,或者有MMU但没有启动,那么CPU内核在取指令或者访问内存时发出的地址(此时必须是物理地址,假如是虚拟地址,那么当前的动作无效)将直接传到CPU芯片的外部地址引脚上,直接被内存芯片(物理内存)接收,这时候的地址就是物理地址;

如果CPU启用了MMU(一般是在bootloader中的eboot阶段的进入main()函数的时候启用),CPU内核发出的地址将被MMU截获,这时候从CPU到MMU的地址称为虚拟地址,而MMU将这个VA翻译成为PA发到CPU芯片的外部地址引脚上,也就是将VA映射到PA中。

MMU将VA映射到PA是以页(page)为单位的,对于32位的CPU,通常一页为4k,物理内存中的一个物理页面称页为一个页框(page frame)。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame)。页和页框的大小必须相同。

 

 

1.CPU内核(图1中的ARM)发出VA请求读数据,TLB(translation lookaside buffer)接收到该地址,那为什么是TLB先接收到该地址呢?因为TLB是MMU中的一块高速缓存(也是一种cache,是CPU内核和物理内存之间的cache),它缓存最近查找过的VA对应的页表项,如果TLB里缓存了当前VA的页表项就不必做translation table walk了,否则就去物理内存中读出页表项保存在TLB中,TLB缓存可以减少访问物理内存的次数;

2.页表项中不仅保存着物理页面的基地址,还保存着权限和是否允许cache的标志。MMU首先检查权限位,如果没有访问权限,就引发一个异常给CPU内核。然后检查是否允许cache,如果允许cache就启动cache和CPU内核互操作;

3.如果不允许cache,那直接发出PA从物理内存中读取数据到CPU内核;

4.如果允许cache,则以VA为索引到cache中查找是否缓存了要读取的数据,如果cache中已经缓存了该数据(称为cache hit)则直接返回给CPU内核,如果cache中没有缓存该数据(称为cache miss),则发出PA从物理内存中读取数据并缓存到cache中,同时返回给CPU内核。但是cache并不是只去CPU内核所需要的数据,而是把相邻的数据都取上来缓存,这称为一个cache line。ARM920T的cache line是32个字节,例如CPU内核要读取地址0x30000134~0x3000137的4个字节数据,cache会把地址0x30000120~0x3000137(对齐到32字节地址边界)的32字节都取上来缓存

 

 

 

 

 

中断和异常

答:中断是异步事件,异常是同步事件。

1)同步中断是由cpu内部的电信号产生的中断,其特点为当前执行的指令结束后才转而产生中断,由于有cpu主动产生,其执行点必然是可控的。

2)异步中断是由cpu的外设产生的电信号引起的中断,其发生的时间点不可预期。

3)同步中断也称为异常,要么是代码错误引起的,此时cpu通过发送相关的信号来处理异常(通常可能是杀死进程的信号);要么是cpu必须处理的一些异常条件,此事cpu执行异常处理函数来恢复异常。

中断又可以分为可屏蔽中断和非可屏蔽中断,异常又分为故障、陷阱和异常中止3种,

1)平常所说的屏蔽中断是不包括异常的,即异常不会因为CPU的IF位被清(关中断,指令:cli)而受影响,比如缺页异常,即使关了中断也会触发CPU的处理。

2)通常说的int 80h这种系统调用使用的中断方式实际上硬件上是理解为异常处理的,因此也不会被屏蔽掉,这也很好理解,int 80h这种中断方式是程序里主动触发的,对于CPU来说属于同步事件,因此也就属于异常的范畴。

中断怎么发生,中断处理大概流程

https://blog.csdn.net/wangzhen199009/article/details/38677075