https://blog.csdn.net/kai_zone/article/details/82021233 (驱动面试题)

1、系统调用read()/write(),内核具体做了那些事情?

参考:https://my.oschina.net/haomcu/blog/468656
答:该过程包括两个部分:用户空间的处理、内核空间的处理。用户空间处理部分主要是系统调用从用户态切换到内核态的过程。当调用发生时,库函数在保存read 系统调用号以及参数后,通过汇编指令int $0x80来引发软件中断。这时Read 系统调用在用户空间中的处理就完成了。进入内核空间后,系统调用中枢处理代码根据传递的系统调用号和静态表分别执行不同的函数。例如read系统调用,0x80 中断处理程序接管执行后,先检察系统调用号,然后根据系统调用号查找系统调用表,并从系统调用表中得到处理 read 系统调用的内核函数sys_read ,最后传递参数并运行 sys_read 函数。(sys_read 是 read 系统调用的内核入口)。

补:read系统调用在内核空间的处理层次模型

答:对于磁盘的一次读请求,首先经过虚拟文件系统层(vfs layer),其次是具体的文件系统层(例如 ext2),接下来是 cache 层(page cache 层)、通用块层(generic block layer)、IO 调度层(I/O scheduler layer)、块设备驱动层(block device driver layer),最后是物理块设备层(block device layer)。

(1)虚拟文件系统VFS 作用:屏蔽下层具体文件系统操作的区别,为上层操作提供一个统一的接口。(正是因为这个层次,所以设备可以被抽象成文件,使得操作设备就像操作文件一样简单)
(2)具体文件系统层:不同的文件系统具有不同的操作过程
(3)增加cache层的目的是为了提高Linux操作系统对磁盘的访问性能。(cache层缓存部分数据,并直接将数据传递给用户程序,避免频繁访问底层磁盘)
(4)通用块层:接收上层发出的磁盘请求,并发出IO请求。(该层隐藏了底层硬件块设备的特性)
(5)IO调度层:接收上层发出的IO请求,缓存请求并试图合并相邻的请求,并根据设置好的调度算法,处理具体的IO请求
(6)物理块设备驱动层:从上层获取IO请求,并根据 IO 请求中指定的信息,通过向具体块设备的设备控制器发送命令的方式,来操纵设备传输数据。
(7)物理块设备层:接受来自驱动层的请求,完成实际的数据拷贝工作。同时规定了一系列规范,驱动程序必须按照这个规范操作硬件。

2、系统调用的作用?

答:系统调用是内核给用户空间提供的一个可以访问内核资源的一个接口。
控制硬件:系统调用往往作为硬件资源和用户空间的抽象接口,比如读写文件时用到的write/read调用。
设置系统状态或读取内核数据:因为系统调用是用户空间和内核的唯一通讯手段,所以用户设置系统状态,比如开/关某项内核服务(设置某个内核变量),或读取内核数据都必须通过系统调用。比如getpgid、getpriority、setpriority、sethostname
进程管理:用来保证系统中进程能以多任务在虚拟内存环境下得以运行。比如 fork、clone、execve、exit等

3、内核态和用户态的区别

答:操作系统运行在内核态,应用程序只能在用户态运行。当一个进程执行用户自己的代码时处于用户运行态,此时特权最低,为Ring3,不能访问Ring0的地址空间。如果该进程要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,这些系统调用会调用内核的代码,进程切换到Ring0,然后进入3G-4G中的内核地址空间去执行内核代码完成相应操作,进程执行完后又会切换回用户态,这样用户态的程序就不能随便操作内核地址空间,具有一定的保护作用。同时,这保证了进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。

补: 字符设备和块设备的区别,请分别列举一些实际的设备说出它们是属于哪一类设备

答:字符设备是个能够像字节流一样被顺序访问的设备,由字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少实现open,close,read和write系统调用。字符终端、串口、鼠标、键盘、摄像头、声卡和显卡等就是典型的字符设备。
块设备是指那些可以用随机方式,以整块数据为单位来访问的设备,如硬盘、SD卡、磁盘等。字符设备和块设备的区别在于内核内部管理数据的方式。

补:字符型驱动设备怎么创建设备文件?

答:①手动创建:mknod /dev/led c 250 0 其中dev/led 为设备节点 c 代表字符设备 250代表主设备号 0代表次设备号
UDEV/MDEV自动创建设备文件的方式,UDEV/MDEV是运行在用户态的程序,可以动态管理设备文件,包括创建和删除设备文件,运行在用户态意味着系统要运行之后。在 /etc/init.d/rcS 脚本文件中会执行 mdev -s 自动创建设备节点。

何为设备文件?
Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。

补:常见驱动安全性问题

答: (1)未初始化指针
(2)恶意用户程序
(3)缓冲区溢出
(4)竞争状态

4、了解Linux内核吗,说说内核的组成?

答:整个linux系统由内核、shell、文件系统和应用程序组成。
内核:由进程管理、内存管理、虚拟文件系统、网络接口、进程间通信五部分子系统组成。
shell:是系统的用户界面,提供了用户和内核进行交互的一种接口。是一个命令解释器,用于接收用户输入的命令并把它送入内核去执行。
文件系统:是文件存放在磁盘等存储设备上的组织方法,常用EXT2、EXT3、FAT、FAT32等。
应用程序:标准的linux系统都有一套称为应用程序的程序集,包括文本编辑器、编程语言和数据库。

补:Linux内核启动过程概述

答:①内核起始运行代码是被压缩zImage(由vmlinux压缩得到)的前面那段未被压缩的解压代码,解压代码运行时,先将zimage后段的内核解压开,再去调用真正的内核入口。
②kernel的链接脚本不是直接提供的,而是提供了一个汇编文件vmlinux.lds.S,然后编译的时候先把这个汇编文件编译获得真正的链接脚本vmlinux.lds。因为:.lds文件不能用条件编译,但kernel中链接脚本确实需要条件编译。

启动流程
③内核启动:通过链接脚本(vmlinux.lds)找到启动入口函数ENTRY(stext)(linux/arch/arm/kernel/head.S),然后在stext中,先运行汇编代码:用来做一些内核启动前的检测:__lookup_processor_type 检测内核是否支持当前CPU ID号、__lookup_machine_type检测是否支持当前板子的机器码,__vet_atags校验传参args的格式,并且__create_page_tables 创建页表(两步:先段式页表、再细页表),__enable_mmu使能MMU,复制数据段,清除bss段,通过 b start_kernel跳转到c语言阶段。
④start_kernel进行大量初始化,比如local_irq_disable(); //关闭当前CPU中断;boot_cpu_init();//设置活跃CPU核;page_address_init();//初始化页地址;setup_arch(&command_line);//确定当前机器;进程调度器初始化sched_init();等。最后调用rest_init()函数,在rest_init()函数中调用kernel_thread函数启动两个内核线程,分别是kernel_init、kthread。同时rest_init()函数调用schedule函数开启内核调度系统,此时linux系统开始运转起来。最后,rest_init()函数调用cpu_idle函数结束整个内核的启动。
进程0:idle进程,即空闲进程;进程1:kernel_init函数,即init进程;进程2:kthread函数,是linux内核的守护进程(保证内核正常运行)。

补:简述MCU的启动流程

答:x210内置了一块96KB大小的SRAM(叫iRAM),同时还有一块内置的64KB大小的NorFlash(叫iROM)。
210的启动过程大致是:
第一步:CPU上电后先从内部IROM中读取预先设置的代码(BL0),执行。这一段IROM代码首先做了一些基本的初始化(CPU时钟、关看门狗···);然后这一段代码会判断我们选择的启动模式(我们通过硬件跳线可以更改板子的启动模式),然后从相应的外部存储器去读取第一部分启动代码(BL1,大小为16KB)到内部SRAM。
第二步:从IRAM去运行刚上一步读取来的BL1(16KB),然后执行。BL1负责初始化NandFlash,然后将BL2读取到IRAM(剩余的80KB)然后运行
第三步:从IRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束。

补:BL0做了哪些任务

答:①关看门狗;②初始化cache指令;③初始化堆、栈;④初始化块设备复制函数;⑤初始化SOC时钟;⑥复制BL1到IRAM(16KB)
⑦检查BL1校验和,并跳转到BL1去执行。

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

答:(1)系统调用 ——syscall(system call),用户进程是不能访问内核的,因此Linux内核设置了可被调用访问linux内核数据和函数的接口,这些接口被称为系统调用。
(2)驱动程序—使用封装后的系统调用接口访问驱动设备节点,比如copy_to_user()、copy_from_user(),用于用户空间与内核空间的数据拷贝。

补: copy_to_user()和copy_from_user()主要用于实现什么功能?一般用于file_operations结构的哪些函数里面?
答: 由于内核空间和用户空间是不能互相访问的,如果需要访问就必须借助内核函数进行数据读写。copy_to_user():完成内核空间到用户空间的复制,copy_from_user():是完成用户空间到内核空间的复制。一般用于file_operations结构里的read,write,ioctl等内存数据交换作用的函数。当然,如果ioctl没有用到内存数据复制,那么就不会用到这两个函数。

(3)共享内存mmap—实现内核空间与用户空间的地址映射,实时性要求高的项目中首选,是跨进程的IPC机制中速度最快的,省去了拷贝数据的时间资源,但是不好控制。
简而言之mmap就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映到内核空间,同样,内核空间对这段区域的修改也直接反映用户空间。
注:实现文件磁盘地址进程虚拟地址空间中一段虚拟地址的一一对映关系,建立映射时并没有数据拷贝,此时和物理内存无关。只有操作指针进行数据读写的时候,才会把数据读写到物理内存。


使用mmap需要注意:mmap的addr与offset必须对齐一个内存页面大小的边界,即内存映射往往是页面大小的整数倍,否则maaped_file_size%page_size内存空间将被闲置浪费。映射时,指定offset最好是内存页面大小的整数倍

(4)伪文件系统:proc、sysfs—用于Linux内核和用户空间的数据交换,是linux内核信息的抽象文件接口,内核中的信息以及可调参数被作为常规文件映射到对应目录树中,这样就可以通过echo/cat文件操作获取内核系统信息。procfs 历史最早,最初就是用来跟内核交互的唯一方式,用来获取处理器、内存、设备驱动、进程等各种信息。sysfs 跟 kobject 框架紧密联系,而 kobject 是为设备驱动模型而存在的,所以 sysfs 是为设备驱动服务的。
(5)ioctl:ioctl函数是设备驱动程序中对设备的I/O通道进行管理的函数,即通过ioctl函数控制设备的一些特性,如串口波特率。
(6)netlink:Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,可以使用标准的套接字API来创建。socket(), bind(), sendmsg(), recvmsg() 和 close()很容易地应用到 netlink socket。

6、linux的用户态和内核态的转换方法?

答:从用户进程切换到内核进程只有两种方式,一种就是系统调用,另一种是中断。
(1)系统调用,如fork()实际上是执行了一个创建新进程的系统调用,但核心是用汇编指令int $0x80来引发软件中断实现。
(2)异常和外围设备中断,异常/中断会触发当前进程切换到处理此异常/中断对应的处理程序。

7、bootloader、内核、根文件系统的关系?

答:(1)启动顺序:bootloader > 内核 > 根文件系统
(2)前者的目的是启动后者,后者需要前者提供功能支持
(3)Linux系统启动过程:系统上电后,处理器会执行一个位于Flash/ROM中的已知位置代码(BootLoader),BootLoader在完成CPU与外设的初始化任务后,把FLASH中的Linux内核拷贝到RAM中,然后跳转到内核的第一条指令处执行内核,内核完成系统的初始化以后需要挂载某个文件系统作为根文件系统,然后加载内核模块,启动应用程序。

8、介绍BootLoader启动过程的两个阶段以及作用。

答:第一个阶段:(汇编)①构建异常向量表、②设置CPU为SVC模式,ARM状态,同时关必FIQ、IRQ、③设置CP15协处理器,关MMU和dcache(icache可关可不关)、④第一次设置栈,调用lowlevel_init.s(⑤关看门狗、⑥供电锁存、⑦初始化时钟、串口、DDR)⑧重定位,把整个uboot加载到DDR中、⑨建立映射表开启MMU、⑩清除bss段,为C语言准备环境;跳转到第二阶段
第二阶段:(C语言)①板级初始化——开发板硬件设备(led、uart等)通过一个函数指针数组init_sequence初始化定时器、环境变量、波特率、控制台等。②检测系统的内存映射、③加载内核、文件系统到DDR、④设置内核的启动参数

补:单片机程序为什么禁止看门狗?

答:设置看门狗定时器的时候,必须先关闭它才能设置,否则在开启状态下设置看门狗定时器可能会出现问题。看门狗其实就是一个定时器,这个定时器溢出是会产生一个中断响应,这个中断响应对应的中断服务就是跳回程序开头,也就是复位。启动看门狗后,要定时在程序中把看门狗的计数器清0,以防止它进入中断然后复位,这就叫做喂狗。看门狗定时器常用在程序很容易跑飞的情况下(比如等待超时,意外进入死循环等)——在启动看门狗定时器的前提下,如果程序跑飞,等计数器WTCNT溢出时,程序会自动复位。但在一般的程序中是不太可能会跑飞的,所以多数情况下我们关闭看门狗,是因为引入了看门狗之后就多了定时计数值处理的一些代码,这增加了程序的复杂性。

补:什么是重定位?和重定位相关的概念

答:1、链接地址和运行地址。
①运行地址,顾名思义就是程序运行时候的地址,也就是用工具将代码下载到RAM的那个地址,也叫加载地址。
②链接地址,由链接脚本指定的地址。为什么需要链接脚本指定地址呢?你想一下,在c语言编程中,当我们需要调用一个A函数的时候,编译器是怎么找到这个A函数?编译器肯定是知道它被放在哪里才可以找到它。那就是链接脚本的作用,链接脚本其实在程序被执行之前都已经指定A函数一个地址编号,以后所有的函数调用我们都会去这个地址编号那里寻找A函数。有点类似于c语言的指针变量。

2、位置有关码与位置无关码。
①位置有关码,就是这句代码的执行正确与否还需要取决于当前的地址,也就是说跟地址已经绑定了的,例如:ldr PC, _main,就是PC指针必须跳转到_main(函数名就是一个地址)这个地址去,代码执行成功与否就相当于受到了这个地址的约束,假如这个地址的内容不存放_main这个函数,就会出错了。
②位置无关码,就是这句代码在哪里运行都可以的,跟所处的地址无关,跟位置有关码相反。

3、链接地址跟运行地址不同的情况下会出现什么情况?
答:以上面举的函数A为例,当链接地址跟运行地址不同的时候,假如链接地址是0x1000,运行地址(加载地址)是0x0000,链接脚本指定函数A将来是要存放到(基地址+偏移量)=0x1000+0x0001=0x1001地址的,但是程序在下载的时候却把这个程序下载到0x0000,所以函数A的地址实际上是存放在(基地址+偏移量)=0x0000+0x0001=0x0001这个地址的。当程序运行到一行位置有关码例如:ldr PC, A ,编译器首先就会按照链接脚本指定的A的那个地址0x1001寻找A函数,但是因为加载地址跟链接地址不同的原因,实际上A函数已经被放到了0x0001,所以执行就会出错。所以,当这两个地址不同的时候,执行一段位置有关码的时候就会发生不可预估的错误。

4、为什么会出现链接地址跟运行地址不同的情况?
答:当一块芯片启动的时候,依靠内部的SRAM,可以运行一小段代码,而因为DDR还没初始化,注定了开始的运行地址是在内部SRAM中的。当我们需要运行一个操作系统,那么点的内存怎么够运行呢?所以这时候就需要初始化DDR才可,而因为我们知道这代码将来都是在DDR上面运行的,所以链接脚本指定的链接地址肯定是DDR上面的地址,所以这就出现了链接地址跟运行地址不同的情况了。

5、什么是重定位?
答:由于出现4这样的问题,就需要使用重定位这种方式解决上面的问题了。那什么是重定位呢?重定位就是在链接地址跟运行地址不同的情况下,执行一段位置无关码,这段位置无关码的作用就是将原来的那份代码全部复制到链接地址那里去,然后自己再长跳转到新的那份代码的刚刚执行的那个位置。这样就实现了链接地址跟运行地址一致的情况了。

6、为什么需要重定位?
答:就是链接地址跟运行地址不同,在这个情况下我们可以有两种方案:
①全部使用位置无关码。
②进行重定位让这两个地址相同。
我们知道,如果是一个小代码,使用①时可以的,但是一个大的代码文件很难保证全部都使用位置无关码的,这也是不现实的,所以必须使用重定位解决这个问题。

补:介绍一下什么是虚拟地址和物理地址?

答:物理地址就是物理设计时被赋予的地址,属于硬件编码,确定后不可改。
虚拟地址意思是在软件操作和硬件被操作之间增加一个层次,叫做虚拟地址映射层,虚拟地址映射层会建立一个虚拟地址到物理地址的映射表。有了虚拟地址映射后,软件操作只需要给虚拟地址,然后在映射表中查到对应的物理地址,再发给硬件去执行。

补:介绍一下MMU

答:MMU就是内存管理单元,是一个硬件单元,主要功能是实现虚拟地址到物理地址的映射。建立虚拟地址映射的主要工作是建立转换表(TTB:转换表基地址),转换表分两个部分:表索引—虚拟地址 和 表项—物理地址,一堆表索引构成一个转换表单元。

补:cache作用?,为什么UBOOT启动时要关闭dcache

答:cache作为高速缓冲存储器,是连通CPU和内存数据的通道,可以提高数据传输速率。但刚上电时,CPU还不能管理cache,RAM中的数据还没有进入cache中,若不关闭dcache,cpu有可能直接在cache中取数据,从而导致数据预取异常。

补:FIQ比IRQ速度快的原因是?

答:①FIQ优先级比较高
②FIQ有自己专属的寄存器:R8-R12,不用通用寄存器,省略了入栈等操作
③FIQ位于异常向量表中末尾0x1c,不用跳转,可以直接放置异常处理函数

9、为什么需要Bootloader?

答:uboot是无条件启动的,但内核不能开机自动启动,需要uboot的帮忙。uboot要帮助内核实现重定位从(SD卡到DDR),uboot还要负责给内核提供启动参数。uboot的作用还有:部署整个系统,建立内存映射,初始化板级设备,提供一个命令行界面。

补:uboot是如何给内核传参的?

答:Uboot最终调用theKernel执行Linux内核,uboot调用这个函数时传递了三个参数,通过寄存器来实现传参,分别是r0,r1,r2对应的参数分别是0,机器码,大片传参tag的首地址。

补:环境变量和全局变量的区别?

答:全局变量的生命周期是在程序的一次运行中,开始运行时诞生,程序结束时死亡,下次运行从头开始。而环境变量被存储在Flash的环境变量分区,一旦保存,下次开机时维持上一次保存的值。
补:环境变量的作用?
答:①指导程序运行、②是程序运行时的配置属性。

10、说说Linux内核的竞争与并发 、或 Linux内核同步方式?

答:内核抢占:如果进程在执行内核函数时(在内核态运行),允许发生内核切换,这个内核就是抢占的。运行在内核态的进程可以自动放弃CPU,称为计划性进程切换;抢占式内核中,进程被迫放弃CPI,称为强制性进程切换。
内核抢占就会发生一种情况:多个任务同时访问同一片内存区域的情况,这些任务可能会相互覆盖这段内存中的数据,造成内存数据混乱;或者在多核环境下,多个core上的进程同时进入内核,就会出现访问竞争。
**并发:**多个用户同时访问同一份共享资源。造成并发原因主要有:
1、多线程并发访问
2、抢占式并发访问(内核调度程序)
3、中断并发访问
4、SMP(多核)核间并发访问

有并发就有竞争。因此编写内核代码重要思想就是避免使用全局变量。
如何防止我们的数据被并发访问呢?
原子操作:对于整形变量以及位操作来说,我们可以使用原子操作来做保护,但是像结构体这种就要换一种方法
使用atomic_t来声明一个原子变量
使用atomic_set()在init入口函数中设置原子变量的初始值
使用atomic_read()读取原子变量的值

自旋锁:等待自旋锁的进程会一直处于查询自选状态,所以只能用于短时期的轻量级加锁。如果线程A持有自旋锁时间过长,显然会浪费处理器的时间,降低了系统性能。
在中断里面使用自旋锁的时候,在获取锁之前一定要先禁止本地中断(也就是本CPU 中断,对于多核SOC 来说会有多个CPU 核),否则可能导致锁死现象的发生。
自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API函数,否则的话可能导致死锁。
不能递归申请自旋锁,否则就会造成自锁死。
自旋锁会自动禁止抢占,也就说当线程A得到锁以后会暂时禁止内核抢占。
声明一个自旋锁: spinlock_t
初始化自旋锁:spin_lock_init()
spin_lock_irqsave(), spin_unlock_irqstore()

信号量:相比于自旋锁,信号量可以使线程进入休眠状态,所以在那些占用资源比较久的场合,使用信号量会提高处理器的使用效率。但是在占用资源比较短时的场合,信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。
信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
将信号量的初始值设置>1时,这个信号量就是计数型信号量
将信号量的初始值设置不大于1时,这个信号量就是一个二值信号量

互斥体mutex:mutex可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
和信号量一样,mutex保护的临界区可以调用引起阻塞的 API函数。

11、为什么自旋锁不能睡眠,拥有信号量时就可以?

答:自旋锁禁止处理器抢占,而信号量允许处理器抢占。
自旋锁自动禁止抢占,也就说当线程A得到锁以后会暂时禁止内核抢占。如果线程A在持有锁期间进入了休眠状态,那么线程A会自动放弃CPU使用权。线程B开始运行,线程B也想要获取锁,但是此时锁被A线程持有,而且内核抢占还被禁止了!线程B无法被调度岀去,那么线程A就无法运行,锁也就无法释放死锁发生了! 而信号量在临界区休眠后,其他进程可以用抢占的方式继续运行,从而实现内存拷贝等功能并使睡眠的信号量程序由于获得了等待的资源而被唤醒,从而恢复正常的代码运行。

12、一个程序从开始运行到结束的完整过程

答:一个程序运行的过程分为:预处理、编译、汇编、链接
预处理:引入头文件、去除注释、处理条件编译指令、宏替换。
编译:对于处理后的文件进行语法分析、语义分析、符号汇总,volatile,static成员在此初始化,最后由.c文件生成.s文件
汇编:将汇编代码转成elf二进制文件,.s生成.o
链接:汇编生成的目标文件不能立即被执行,因为有问题未解决,比如:某文件中的函数有可能引用了另一个文件中定义的某个符号(如变量或者函数调用等),或者调用了某个库文件的函数等。 链接的目的就是将有关的目标文件彼此相连接,即将在一个文件中引用的符号同该符号在另一个文件中的定义连接起来,使所有的目标文件成为一个能被操作系统执行的整体。

补:静态链接和动态链接

答:静态链接和动态链接最大的区别在于链接的时机不一样,静态链接是在形成可执行程序前,而动态链接的进行则是在程序执行时。
静态链接:后缀是.a,主要在编译的时候将库文件里面代码搬迁到可执行的文件中;
在实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的,即每个*.c文件会形成一个*.o文件,为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接。
缺点:①浪费空间更新比较困难,每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。
优点:在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快
动态链接:后缀是.so,主要在执行的时候需要转换到库文件代码执行。
动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。
动态链接的过程:假设现在有两个程序program1.o和program2.o,这两者共用同一个库lib.o,假设首先运行程序program1,系统首先加载program1.o,当系统发现program1.o中用到了lib.o,即program1.o依赖于lib.o,那么系统接着加载lib.o,如果program1.o和lib.o还依赖于其他目标文件,则依次全部加载到内存中。当program2运行时,同样的加载program2.o,然后发现program2.o依赖于lib.o,但是此时lib.o已经存在于内存中,这个时候就不再进行重新加载,而是将内存中已经存在的lib.o映射到program2的虚拟地址空间中,从而进行链接(这个链接过程和静态链接类似)形成可执行程序。
优点:省空间、便于更新 缺点:链接被推迟到程序运行时,因此每次执行程序都需要进行链接,性能会有损失。

13、什么是内存泄漏和内存溢出?

答:一般说的内存泄漏是指堆内存泄漏。堆内存是指程序从堆中分配的,大小任意,使用完必须显示释放的内存。如果不释放,这块内存就不能再次被使用,因此说这块内存泄漏了。
内存泄漏分为四类: 常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏、隐式内存泄漏(危害最大)。
内存溢出是指要求分配的内存超出了系统给的,系统不能满足需求,因此产生溢出。栈满时再做进栈产生空间溢出叫上溢,栈空时再做出栈叫下溢。
内存越界:向系统申请了一块内存,而在使用内存时,超出了申请的范围(常见使用特定数组时发生内存越界)

补:什么是缓冲区溢出?有什么危害?其原因是什么?

答:缓冲区溢出是指当计算机想缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上.
危害:缓冲区溢出中,最为危险的是堆栈溢出因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。
原因:造成缓冲区溢出的主原因是程序中没有仔细检查用户输入的参数。

14、死锁的原因、条件,以及如何预防。

答:死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。

产生死锁的原因?

a. 竞争资源
系统中的资源可以分为两类:
可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
另一类资源是不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机。

产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁。

b. 进程间推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁。例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁

死锁产生的4个必要条件?

产生死锁的必要条件:
**互斥条件:**在一段时间内某资源仅为一进程所占用。
**请求和保持条件:**当进程因请求资源而阻塞时,对已获得的资源保持不放。
**不剥夺条件:**进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
**循环等待条件:**若干进程之间形成一种头尾相接的循环等待资源关系。

解决死锁的基本方法:

①资源一次性分配:静态分配,一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
②对某个进程来讲,只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏保持条件)
③可剥夺资源:当某进程无法获得需要的全部资源时处于等待状态,且释放已占有的资源(破坏不可剥夺条件)
④资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏循环等待条件)

15、硬链接和软链接的区别:

答:什么是链接? 链接操作是给系统已有的某个文件指定另外一个可用于访问它的名称。
硬链接:硬链接文件的内容和源文件的内容一模一样,相当于copy了一份,并且创建时间也一样。 硬链接只能引用同一个文件系统的文件,它引用的是文件在文件系统中的物理索引(inode)。若A是B的硬链接,则A的iNode节点号与B的iNode节点号相同,即一个节点对应两个不同的文件名,两个文件名指向同一个文件。A B对文件系统来说是平等的,删除其中一个,对另一个没影响。每增加或减少一个文件名时,inode节点上的链接数对应加减1,直到为0时,inode节点和对应的数据块被回收。
硬链接引用的是稳健的物理数据而不是文件在文件结构中的位置,不需要用户有访问原始文件的权限,也不会显示原始文件的位置,有助于文件的安全。
软连接:软链接文件只是维持了从软链接到源文件的指向关系,不是源文件的内容,大小不一样。如A是B的软链接(A和B都是文件名),A的目录项中的inode节点号与B的目录项中的inode节点号不相同,A和B指向的是两个不同的inode,继而指向两块不同的数据块。但是A的数据块中存放的只是B的路径名(可以根据这个找到B的目录项)。A和B之间是“主从”关系,如果B被删除了,A仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。
软连接可以跨文件系统,硬链接不可以。 软连接可以对不存在的文件创建软链接,硬链接不可以。软连接可以对目录进行连接,硬链接以。

16、虚拟内存和物理内存,虚拟地址与物理地址的转换

答:物理内存:指通过物理内存条而获得的内存空间,主要作用是在计算机运行时为操作系统和各种程序提供临时储存。
虚拟内存:对内存架构(内存、缓存、硬盘)进行管理(内存管理系统)的一种手段。简单理解就是在硬盘上划分出一块区域作为内存使用。意思是在物理内存不够用的时候把一些很少访问的内存数据转存到硬盘上,然后把这部分内存腾出来分配给其它应用。因为硬盘比内存慢的多,只要用虚拟内存就必然卡慢。所以最理想的状态是不使用虚拟内存,完全只用物理内存,避免卡慢。
虚拟内存就是让内存管理实现媒介透明的手段。

内存管理方法:方法包括加载地址、固定分区的、非固定分区的和交换内存管理,共同实现机制都是基址和极限

1、交换内存管理中的机制——基址和极限、
交换内存管理是这些方法中最灵活的。它使用的基址和极限机制实际上就是“将程序发出的虚拟地址加上基址得到物理地址”
2、交换内存管理的两大问题
空间浪费:程序不断的执行并释放的过程中,造成了内存空间中的可用空间不连续就难以加以应用,这种现象也称为**“外部碎片化”
程序大小受限 :当程序需要更多的内存空间时,需要将其全部从物理内存中“倒出”到磁盘上,再在内存中找到更大的一片区域去存放增长了的程序,这样使得
程序的增长效率低下**。同时,一个程序的大小还不能超过物理内存空间的大小。

3、解决方法——分页内存管理(或段式内存管理)——将一个进程分散到许多不连续的空间,就可以避免内存紧缩,减少碎片。
分页内存管理 :将虚拟内存空间和物理内存空间皆划分成大小相同的页面,例如4KB、8KB和16KB等。并将页作为内存空间的最小分配单位,一个程序的一个页面(虚拟页面)可以存放在任何一个物理页面中。
一个程序发出的虚拟地址由虚拟页面号和页内偏移值两部分组成,组成见下:

注:分页内存管理是如何解决交换内存管理中的两个问题的?
答:(空间浪费):通过将内存空间划分成大小一样的页面,并且将其作为内存分配的基本单位,这样就避免了大量外部碎片的积累,让内存空间得到有效利用。
(程序受限):分页内存管理下,允许一个进程的部分虚拟页面存放在物理内存中,另一部分存放在磁盘上,等到需要使用时再将其从磁盘中加载到物理内存中。也就是说,当程序需要额外的空间时,只需要对其分配新的页即可,这样做使得程序的增长效率较高。

4、虚拟地址->物理地址的转化(地址翻译)

翻译工作则是交给MMU(内存管理单元),它只对虚拟地址的页面号进行翻译,而不处理页内偏移值
MMU为每一个程序都配备了一个页表,里面存放的是虚拟页面到物理页面的映射,如果MMU接收到了程序发出的虚拟地址,在查找相对应的物理页面号时,没有找到,那么将会通过缺页中断来将需要的虚拟页面从磁盘中加载到物理内存的页面中。


随着虚拟页面的进出内存,页表的内容也是不断地变化的。
页表中还会含有一些记录信息,比如:记录虚拟页面在当前物理页面是否存在、对虚拟页面的操作是否有读写限制等。

补:内存管理中,会产生零头的是?

内存零头——内存碎片
答:外零头:内存中存在着的一些无法使用的空闲内存区域,这些内存区域补数据任何进程,但因内存太小,无法满足其他进程所申请的内存大小,而形成的的内存零头。
内零头:进程向操作系统请求内存分配时,系统满足了进程所需要的内存需求后,额外多分了一些内存给该进程。额外多分的内存归该进程所有,别的进程无法使用。
页式内存管理会产生内零头,原因:页式管理以页为单位(页面大小由系统决定,大小固定),假设内存总共100K,分为10页,每页10k,现在进程A申请56K内存,因为页面是以页为单位进行内存分配的,所以系统会提供6个页面,即60k内存空间,在最后一页只使用了6K,多出了4K的内存碎片,即内零头。
段式内存管理会产生外零头,原因:段的大小是程序逻辑确定的,大小不固定,进程申请多少内存,系统就给分配多少,这样就不会产生内零头。
比如内存总大小100k,A进程申请60K,还剩40K,如果B在申请50K,但是内存中剩余的区域就不能满足B的要求,就成了外零头。
请求段式存储管理就是在段式存储管理的基础上架了请求调段功能和段置换功能。

段式内存管理主要是为了信息共享:分页系统中的“页”只是存放信息的物理单位(块),并无完整的意义,不便于实现共享;然而**段却是信息的逻辑单位。**段页式存储管理方式

前面所介绍的分页和分段存储管理方式都各有优缺点。分页系统能有效地提高内存利用率,而分段系统则能很好地满足用户需求。我们希望能够把两者的优点结合,于是出现了段页式存储管理方式
段页式系统的基本原理,是分段和分页原理的结合,即先将用户程序分成若干个段,再把每个段分成若干个页,并为每一个段赋予一个段名。在段页式系统中,地址结构由段号、段内页号和页内地址三部分所组成。

补:Linux是如何避免内存碎片的?

答:(1)伙伴算法,用于管理物理内存,避免内存碎片;
(2)高速缓存Slab层用于管理内核分配内存,避免碎片。

17、内存位宽,32bit和64bit的区别?

答:32位的系统指的是32位数据线,数据线越多,一次传输处理的数据就越多,性能也越好。(32位系统一般地址线也是32位,地址线数量决定了CPU可以寻址内存空间的大小,32位地址线决定了内存地址只能有32位二进制,所以内存限制就是2的32次方,即4G)
内存位宽(数据线的数量)是指在一定时间(一个时钟周期)内所能传输数据的位数。
64bit的两大优点:可以进行更大范围的整数运算;可以支持更大的内存。但是只有在64bit操作系统和64bit软件基础上,64bit的性能才是32bit的两倍。实际上,在32Bit的应用程序下,32bit处理器的性能更强。
64bit系统下的虚拟内存空间大小:地址空间大小是2的48次方,因为并不需要64次方那么大的寻址空间,其中40位标志物理地址。

18、中断和异常的区别?

答:中断是指CPU对系统发生某事件时的一种响应:CPU暂停正在执行的程序,保留现场以后自动转去执行该事件的中断处理程序,执行完成后再返回到源程序的断点处继续执行。

中断分为外中断和内中断。
外中断——就是我们指的中断——是指由于外部设备事件所引起的中断,如通常的磁盘中断、打印机中断等;
内中断——就是异常——是指由于 CPU 内部事件所引起的中断,如程序出错(非法指令、地址越界)。内中断(trap)也被译为“捕获”或“陷入”。
异常是由于执行了现行指令所引起的。由于系统调用引起的中断属于异常。
中断则是由于系统中某事件引起的,该事件与现行指令无关。

相同点:都是CPU对系统发生的某个事情做出的一种反应。
区别:中断由外因引起,异常由CPU本身原因引起。
引入原因:中断的引入——为了支持CPU和设备之间的并行操作;异常的引入——表示CPU执行指令时本身出现的问题

补:中断上下部和中断上下文

答:设备的中断会打断内核中进程的正常调度和运行,系统要求中断服务程序尽可能地短小精悍。但是大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux将中断处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态清除中断标志后就进行**“登记中断”的工作。
“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求。现在,中断处理工作的重心就落在了底半部的头上,它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部往往被设计成不可中断。底半部则相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。尽管顶半部、底半部的结合能够改善系统的响应能力,但是,僵化地认为Linux设备驱动中的中断处理一定要分两个半部则是不对的。如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。
其实上面这一段大致说明一个问题,那就是:中断要尽可能耗时比较短,尽快恢复系统正常调试,所以把中断触发、中断执行分开,也就是所说的“上半部分(中断触发)、底半部(中断执行)”。
中断下半部2种解决方案
(1)下半部处理策略1:tasklet(小任务):需要较少时间的中等数量的急迫任务放在tasklet中。此时不会屏蔽任何中断(包括与自己的顶半部同类型的中断),所以不影响顶半部对紧急事务的处理;同时又
不会进行用户进程调度**,从而保证了自己急迫任务得以迅速完成。
(2)下半部处理策略2:workqueue(工作队列):需要较多时间且并不急迫(允许被操作系统剥夺运行权)的大量任务放在workqueue中。此时操作系统会尽量快速处理完这个任务,但如果任务量太大,期间操作系统也会有机会调度别的用户进程运行,从而保证不会因为这个任务需要运行时间将其它用户进程无法进行。可能引起睡眠的任务放在workqueue中。因为在workqueue中睡眠是安全的。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,用workqueue很合适。

中断上下文:对于中断而言,是硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理,中断上下文就可以理解为硬件传递过来的这些参数和内核需要保存的一些环境,主要是被中断的进程的环境。

补:进程上下文

答:进程上下文(这个中文翻译真的不是很好理解,用“环境”比它好很多)。一般的进程运行在用户态,如果这个进程进行了系统调用,那么此时用户空间中的程序就进入了内核空间,并且称内核代表该进程运行于内核空间中。由于用户空间和内核空间具有不同的地址映射,并且用户空间的进程要传递很多变量、参数给内核,内核也要保存用户进程的一些寄存器、变量等,以便系统调用结束后回到用户空间继续执行。这样就产生了进程上下文。
所谓的进程上下文,就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容。当内核需要切换到另一个进程时(上下文切换),它需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态继续执行。

补:硬中断和软中断的区别?

答:硬中断是由与系统相连的外设(比如网卡 硬盘 键盘等)自动产生的. 每个设备或设备集都有他自己的IRQ(中断请求), 基于IRQ, CPU可以将相应的请求分发到相应的硬件驱动上(注: 硬件驱动通常是内核中的一个子程序, 而不是一个独立的进程). 比如当网卡受到一个数据包的时候, 就会发出一个中断.
处理中断的驱动是需要运行在CPU上的, 因此当中断产生时, CPU会暂时停止当前程序的程序转而执行中断请求. 一个中断只能中断一颗CPU
硬件中断可以直接中断CPU. 它会引起内核中相关代码被触发. 对于那些需要花费时间去处理的进程, 中断代码本身也可以被其他的硬件中断
软中断一种推后执行的机制,定时器,网卡的数据的处理是很典型的软中断,这个和中断向量表里的中断是完全不一样的,以网络数据的处理为例,当网卡接到一个数据包后,其中断处理程序**(硬中断)只是把数据复制到缓冲区,然后就告诉网卡,你可以再传数据给我了**,也就是中断返回,但在此之前,网卡的中断处理程序要置一个标志位,告诉操作系统有事要做,这个事就是软中断,操作系统每次中断返回时会检查这个标志位,看是否有事要做,如果有,就会去处理,象前面提到的网卡,这时候操作系统就回调用软中断的处理函数,网卡的软中断程序就是做分析数据包,这个数据应该传给谁啊等这些工作.没有,就返回了,除了必须的部分。
软中断不会直接中断CPU, 也只有当前正在运行的代码(或进程)才会产生软中断. 软中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求.

补:硬件中断和软中断的区别
硬件中断是由外设引发的, 软中断是执行中断指令产生的.
硬件中断的中断号是由中断控制器提供的, 软中断的中断号由指令直接指出, 无需使用中断控制器.
硬件中断是可屏蔽的, 软中断不可屏蔽.
硬件中断处理程序要确保它能快速地完成任务, 这样程序执行时才不会等待较长时间, 称为上半部.
软中断处理硬中断未完成的工作, 是一种推后执行的机制, 属于下半部.

补:编写两个中断服务函数的区别
1.软中断发生的时间是由程序控制的,而硬中断发生的时间是随机的
2.软中断是由程序调用发生的,而硬中断是由外设引发的
3.硬件中断处理程序要确保它能快速地完成它的任务,这样程序执行时才不会等侍较长时间

补:中断处理流程

答:请求中断——响应中断——保护现场——中断服务——恢复现场——中断返回

19、波特率是什么?

答:波特率表示每秒钟传送的码元(在信息传输通道中,携带数据信息的信号单元)符号的个数,单位是bit/s,即每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。,通俗来说波特率就是一秒钟传输多少个0或1。
波特率越高,传输速度越快,距离越短。
补:为什么波特率要相同?
答:串行通信要求同步传输,如果波特率不一样,传输出来的数据不完整或错误。

20、arm和dsp、fpga的区别?

答:ARM具有较强的事务管理能力,同时外围接口丰富,并且标准化、通用性,低功耗方面做得更好,多用来跑界面及应用程序。
DSP主要用来计算,比如加密解密、调制解调等。具有强大的数据处理能力和较高的运行速度
FPGA是硬件设计,可以用作设计CPU的周边电路或者直接设计CPU本身。比如你想设计一个自己的CPU或者是其他的硬件电路。

单片机为了存储器管理的更方便,一般采用指令、数据空间统一编码冯 诺依曼结构,而DSP为了提高数据吞吐速度,基本都是指令、数据空间独立的哈弗结构

21、ROM、RAM、FLASH的概念?

答:RAM又叫随机存储器,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,并且速度快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。但是电源关闭时,RAM不能保留数据(数据掉电消失)。

补:片外RAM和片内RAM

答:RAM从所处位置分为片内和片外RAM两大类,片内RAM比片外速度快1.5-2倍。片内RAM在CPU设计时加上的,和CPU一样的制作工艺和材料,成本较高。片内RAM用来存放中断处理handlerRTOS处理器任务上下文切换内存分配等使用频率最高的代码和中断堆栈这种读写频率极高的内存区。片外RAM可以用来存储全局变量、bss、以及malloc分配的堆空间

补:外部总线、系统总线和内部总线

答:**内部总线:在CPU内部,寄存器之间和算术逻辑部件ALU与控制部件之间传输数据所用的总线称为片内总线(即芯片内部的总线)。**如:I2C总线、SPI总线和串行通信接口SCI总线。
系统总线:又称内总线或板级总线,是微机中各插件板与系统板之间的总线,用于插件板一级的互联。因为该总线是用来连接微机各功能部件而构成一个完整微机系统的,所以称之为系统总线。人们平常所说的微机总线就是指系统总线,如ISA总线、PCI总线等。
外部总线:是计算机和外部设备之间的总线。如:RS-232-C总线、RS-485总线、USB总线。

RAM又分为静态RAM(Static RAM/SRAM)和动态RAM(Dynamic RAM/DRAM),SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲。DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。DRAM分为很多种,这里介绍其中的一种DDR RAM。这种改进型的RAM和SDRAM是基本一样的,不同之处在于它可以在一个时钟读写两次数据,这样就使得数据传输速度加倍了。这是目前电脑中用得最多的内存,而且它有着成本优势。

补:内存工作原理:
内存是用来存放当前正在使用的(即执行中)的数据和程序,我们平常所提到的计算机的内存指的是动态内存(即DRAM),动态内存中所谓的"动态",指的是当我们将数据写入DRAM后,经过一段时间,数据会丢失,因此需要一个额外设电路进行内存刷新操作。
具体的工作过程是这样的:一个DRAM的存储单元存储的是0还是1取决于电容是否有电荷,有电荷代表1,无电荷代表0。但时间一长,代表1的电容会放电,代表0的电容会吸收电荷,这就是数据丢失的原因;刷新操作定期对电容进行检查,若电量大于满电量的1/2,则认为其代表1,并把电容充满电;若电量小于1/2,则认为其代表0,并把电容放电,藉此来保持数据的连续性。

ROM又称为只读存储器,ROM中数据是装入整机前事先写入的,CPU工作中只能读出,不能改写。断电后数据仍然存在
ROM分为两种,一种是指在CPU内部的只读存储区域,用来存储系统刚上电时对CPU一些核心外设(时钟,串口,MMU,FLASH等)进行初始化的代码,对该部分代码的改写只能用硬件烧写器进行,量产后会固化,不能再修改。
其中一种EEPROM是通过电子擦除,价格昂贵,写入较慢。
另一种ROM指的是flash,结合了ROM和RAM的长处,不仅可以电子擦除编程,还可以再断电时保存数据。flash分为NOR FLASH 和NAND FLASH,NOR中代码可以被用户直接运行。

引导程序必须放到ROM中,此时flash和ddr 都没有初始化,CPU只能先运行ROM中程序。
注:一般来讲,数据放RAM,代码放ROM。

22、IO工作方式:上拉输入、下拉输入、推挽输出、开漏输出

答:推挽输出可以输出高、低电平,结构一般是两个三极管分别受两个互补信号的控制,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)。一般来说,开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。

23、扇区、块、页的概念

答:扇区是磁盘中最小的物理存储单元,每个扇区是512字节。硬盘的读写以扇区为基本单位
块是操作系统中最小的逻辑存储单元,操作系统与磁盘打交道的最小单位是磁盘块
页是内存中最小的存储单位,页的大小是块大小的2的n次方倍,操作系统与内存打交道的最小单位是页

24、简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?

答:MMU(内存管理单元)支持虚拟内存管理,有MMU的操作系统:windows、Linux、android;不用MMU的:freertos、UCOS等。
MMU作用就是把虚拟地址转换成物理地址。地址的转换经历三个阶段:VA(CPU内核发出的虚拟地址),MVA(由硬件自动转换后的虚拟地址,供给cache和MUU使用)、PA(读写设备使用的物理地址)。MMU在映射的时候,是以页为单位的,一个映射单元包括一个物理页面(页表项)和一个虚拟页面(页表索引)。

25、同步串口和异步串口的区别?

答:异步通信是指数据传送以字符为单位,字符与字符间的传送是完全异步的,位与位之间的传送基本上是同步的。异步串行通信的特点可以概括为:①以字符为单位传送信息。②相邻两字符间的间隔是任意长。③因为一个字符中的比特位长度有限,所以需要的接收时钟和发送时钟只要相近就可以。④异步方式特点简单的说就是:字符间异步,字符内部各位同步
异步位系统传输的时候还要加上起始位和结束位,没有这两位接收方就不知道什么时候开始接收数据什么时候结束了。

同步通信是指数据传送是以数据块(一组字符)为单位,字符与字符之间、字符内部的位与位之间都同步。同步串行通信的特点可以概括为:①以数据块为单位传送信息。②在一个数据块(信息帧)内,字符与字符间无间隔。③因为一次传输的数据块中包含的数据较多,所以接收时钟与发送进钟严格同步,通常要有同步时钟。发送方与接收方严格的同步,二者波特率要相同。

26、查看驱动模块中打印信息应该使用什么命令?如何查看内核中已有的字符设备的信息?如何查看正在使用的有哪些中断号?

答:

  1. 查看驱动模块中打印信息的命令:dmesg
  2. 查看字符设备信息可以用lsmod 和modprobe,lsmod可以查看模块的依赖关系,modprobe在加载模块时会加载其他依赖的模块。
  3. 显示当前使用的中断号cat /proc/interrupt

27、请简述主设备号和次设备号的用途。如果执行mknod chartest c 4 64,创建chartest设备。请分析chartest使用的是那一类设备驱动程序。

答:1)主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。即Linux主设备号用来区分不同硬件设备类型,如串口和USB之间的区别; Linux次设备号用来区分不同硬件设备,如串口1和串口2之间的区别;
2)chartest 表示设备节点,4表示主设备号,64表示次设备号。(感觉类似于串口终端或者字符设备终端)。

28、注册一个字符设备驱动的方法:

答:1) int register_chrdev(unsigned int major, const char *name , struct file operations *fops);
该注册函数是早期的注册函数,major是设备的主设备号,name是驱动程序的名称,而fops是默认的file_operations结构(这是只限于file_operations结构)。对于register_chrdev的调用将为给定的主设备号注册0-255作为次设备号,并为每个设备建立一个对应的默认cdev结构。
驱动向内核注册自己的file_operations
2)register_chrdev_region/alloc_chrdev_region + cdev_init + cdev_add进行字符设备驱动注册
mydev = MKDEV(MYMAJOR,0);// MKDEV是将主设备号和次设备号转换成dev_t类型的一个内核函数。
register_chrdev_region(mydev,MYCNT,MYNAME); //注册/分配主次设备号:mydev设备号,MYCNT设备个数,MYNAME设备名字
cdev_init(&test_cdev,&test_fops);//把设备和file_operation结构体绑定起来,test_cdev 的使用需要定义static struct cdev test_cdev;test_fops是结构体变量名称;都是指针,所以传参时需要加取地址符。
cdev_add(&test_cdev,mydev,MYCNT);//完成真正的驱动注册

cat /proc/devices查看内核中已经注册过的字符设备驱动(和块设备驱动)

29、linux中RCU原理?

答:RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,在修改数据的时候,首先需要读取数据,然后生成一个副本,对副本进行修改,修改完成之后再将老数据update成新的数据,此所谓RCU。
在操作系统中,数据一致性访问是一个非常重要的部分,通常我们可以采用锁机制实现数据的一致性访问。例如,semaphore、spinlock机制,在访问共享数据时,首先访问锁资源,在获取锁资源的前提下才能实现数据的访问。这种原理很简单,根本的思想就是在访问临界资源时,首先访问一个全局的变量(锁),通过全局变量的状态来控制线程对临界资源的访问。但是,这种思想是需要硬件支持的,硬件需要配合实现全局变量(锁)的读-修改-写,现代CPU都会提供这样的原子化指令。采用锁机制实现数据访问的一致性存在如下两个问题:
  1. 效率问题。锁机制的实现需要对内存的原子化访问,这种访问操作会破坏流水线操作,降低了流水线效率。这是影响性能的一个因素。另外,在采用读写锁机制的情况下,写锁是排他锁,无法实现写锁与读锁的并发操作,在某些应用下会降低性能。
  2. 扩展性问题。当系统中CPU数量增多的时候,采用锁机制实现数据的同步访问效率偏低。并且随着CPU数量的增多,效率降低,由此可见锁机制实现的数据一致性访问扩展性差。
为了解决上述问题,Linux中引进了RCU机制。该机制在多CPU的平台上比较适用,对于读多写少的应用尤其适用。

30、insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?

答: insmod调用init函数(module_init),rmmod调用exit函数
要注意在init函数中申请的资源在exit函数中要释放,包括存储,ioremap,定时器,工作队列等等。也就是一个模块注册进内核,退出内核时要清理所带来的影响,带走一切不留下一点痕迹。
卸载模块时曾出现卸载失败的情形,原因是存在进程正在使用模块,检查代码后发现产生了死锁的问题。

31、设备驱动模型三个重要成员是?platform总线的匹配规则是?

答:设备驱动模型三个重要成员是 总线、设备、驱动
platfoem总线的匹配规则是:要匹配的设备和驱动都要注册,设备可以在设备树里注册,也可以通过代码注册设备,通过match函数匹配成功会去调用驱动程序里的probe函数(probe函数在这个platform_driver结构体中注册)。

32、驱动中操作物理绝对地址为什么要先ioremap?

答: 因为内核没有办法直接访问物理内存地址,必须先通过ioremap获得对应的虚拟地址

33、文件系统的作用

答: 文件系统是管理存储设备的一种方式。存储设备由很多扇区组成,每个扇区有512/1024字节,存储设备要以扇区为单位进行读写。如果没有文件系统,程序要自己去读写扇区,就得记着哪个文件在哪个扇区。有了文件系统之后用户只需要关注文件系统的目录和文件名,而不用管这个文件在磁盘的哪个位置。

补:根文件系统和内核的关联

答:(1)内核启动最后会去装载根文件系统,刚开始内核启动时是没有目录的,整个操作系统的根目录就是从根文件系统来的。
(2)进程1存放在根文件系统中,是用户态和内核态的一个交界处,是整个系统中的第一个应用程序。

34、编译配置内核的过程?

答:make x210ii_qt_defconfig make menuconfig make
补:第一步的目的是为了得到.config(内核在编译过程中会读取.config的配置项,并且用这些配置项去指导整个编译链接过程)
第二步的目的是为了配置.config文件的内容,菜单中的项目内容由Kconfig决定,选择值由.config决定

35、移植UBOOT要注意什么

答:①检查arch、cross_compile变量;
②注意autoconfig.mk,主要作用是指导整个uboot的编译过程,内容是很多confg_开头的宏,这些宏是移植uboot的关键。
③注意观察开发板现象和打印信息,比如开发板供电锁存成功,但不打印ok,根据启动流程,定位到问题位置,并解决
④留意机器码和内核是否一致

36、移植操作系统要注意什么?

答:①检查arch、cross_compile变量;
②把编译后得到的zImage通过tftp的方式下载到开发板上,并启动。发现,打印信息停在starting kernel…没有发现uncompressing linux done,说明自解压代码都没有完成,因此检查内核配置的解压地址和内核的链接地址是否一致,不一致就改正。
③自解压信息打印出来后,但还是没有启动,接下来检查内核的启动地址和机器码对不对。
④**驱动在执行过程中出现OOPS,原因是内核配置中支持了电源管理模块,但开发板没有这个硬件,因此内核在执行驱动时出错。**把电源管理模块在配置项中移除

37、优先级翻转

答:优先级翻转是当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。
例如:有优先级为A、B和C三个任务,优先级A>B>C,任务A,B处于挂起状态,等待某一事件发生,任务C正在运行,此时任务C开始使用某一共享资源S。在使用中,任务A等待事件到来,任务A转为就绪态,因为它比任务C优先级高,所以立即执行。当任务A也要使用共享资源S时,由于其正在被任务C使用,因此任务A被挂起,任务C开始运行。如果此时任务B等待事件到来,则任务B转为就绪态。由于任务B优先级比任务C高,因此任务B开始运行,直到其运行完毕,任务C才开始运行。直到任务C释放共享资源S后,任务A才得以执行。在这种情况下,优先级发生了翻转,任务B先于任务A运行。
解决优先级翻转问题有优先级天花板(priority ceiling)和优先级继承(priority inheritance)两种办法。
优先级天花板是当任务申请某资源时, 把该任务的优先级提升到可访问这个资源的所有任务中的最高优先级, 这个优先级称为该资源的优先级天花板。这种方法简单易行, 不必进行复杂的判断, 不管任务是否阻塞了高优先级任务的运行, 只要任务访问共享资源都会提升任务的优先级。
**优先级继承是当任务A 申请共享资源S 时, 如果S正在被任务C 使用,通过比较任务C 与自身的优先级,如发现任务C 的优先级小于自身的优先级, 则将任务C的优先级提升到自身的优先级, 任务C 释放资源S 后,再恢复任务C 的原优先级。**这种方法只在占有资源的低优先级任务阻塞了高优先级任务时才动态的改变任务的优先级,如果过程较复杂, 则需要进行判断。

38、什么是file_operations?

答:file_operation就是把系统调用和驱动程序关联起来的关键数据结构。这个结构的每一个成员都对应着一个系统调用。读取file_operation中相应的函数指针,接着把控制权转交给函数,从而完成了Linux设备驱动程序的工作。
注册设备编号仅仅是驱动代码必须进行的诸多任务中的第一个。大部分的基础性的驱动操作包括 3 个重要的内核数据结构,称为: file_operations、file、和 inode。需要对这些结构基本了解,才能够做大量感兴趣的事情。
struct file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。
struct file代表一个打开的文件,在执行file_operation中的open操作时被创建,这里需要注意的是与用户空间inode指针的区别,一个在内核,而file指针在用户空间,由c库来定义。
struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件。

struct inode包括二个重要的成员:
dev_t i_rdev 设备文件的设备号
struct cdev *i_cdev 代表字符设备的数据结构
struct inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.

39、什么是cdev?

答: 在Linux内核中,使用cdev结构体来描述一个字符设备。cdev包含两个主要的成员:通过成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性,通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等。
a – 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;
b – 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;
c – 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号;

用户空间访问该设备的程序,通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。

补:什么是udev?

答:应用层的一个应用程序,负责在设备初始化时自动创建设备文件。
注:①驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除,内核驱动和udev之间通过netlink协议进行信息传输。②应用层先启用udev(在挂载的初始化程序rcS中启用),内核驱动中使用相应接口(1)class_create、(2)device_create。

40、UCOS和linux的区别?

参考https://blog.csdn.net/chen_geng/article/details/51556459
答:①linux是分时系统(即把CPU的运行分成若干时间片分别处理不同的运算请求),不过可以改成实时。
ucos是实时系统(能立即对指令做出反应的操作系统)
②内核:ucos没有提供输入输出管理,文件系统,网络等服务;且为抢占式内核,允许中断服务程序中断,中断结束后重新进行任务调度,即重新调用最高优先级任务。
linux为抢占式内核,当进程运行在用户态时,可以被优先级更高的进程抢占,但当他进入核心态时,优先级再高也不能抢占它;区分用户态和核心态。
③任务调度:ucos最多可调度64个任务,每个任务具有不同的优先级,当前运行的总是优先级最高的任务,无法进行同等优先级的任务调度,最高优先级任务的确定是通过建立就绪任务表来实现的;
linux调度策略为优先级调度和时间片轮转调度,区别于UCOSii的,时间片轮转调度可进行同等优先级任务的调度,让这些任务依次运行一段时间,从而保证系统中同等优先级任务具有平等的运行权利;
④文件系统:ucos无文件管理系统; linux文件系统继承了unix系统的文件系统功能特性,还采用了EXT2文件系统,EXT2功能强大,易扩充,性能上全面优化。还有虚拟文件系统,使得linux可以支持多个不同的文件系统。/proc文件系统是一个伪文件系统,用户和应用程序可以通过它得到系统信息,改变内核的某些参数。
⑤应用领域:UCOS广泛应用与工业控制中,单片机领域中,有线数字电视领域机顶盒设计中,移动通信终端领域等。linux广泛应用在服务器领域,在桌面应用方面也达到了一定水平。
⑥其他:ucos不具备用户界面,一般使用第三方图形界面系统弥补,如:emWin。linux有较好的用户界面,如shell。
ucos支持信号量,用以实现任务与任务之间,任务与中断服务程序之间的同步和互斥问题;消息队列,用以实现任务间的缓冲通信;邮箱,一种低开销的传送信息机制;优先级继承协议和天花板协议,用以解决任务调度阻塞和优先级反转的问题。
linux还具有设备的独立性,将所有外部设备统一当做文件来管理。新增设备只需在内核中增加必要的设备驱动程序。
UCOSii移植比较简单,只需要几K字节的RAM即可。Linux移植比较复杂,需要几百k字节RAM以上。
ucos不带mmu,linux带mmu(MMU是内存管理单元,它是CPU中用来管理虚拟存储器、物理存储器的控制线路,同时负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。)

补:ucos3和ucos2区别?

答:新增时间片轮转调度,最大任务数由256提升到不限(但会受 CPU 所使用的存储空间的限制,包括代码空间和数据空间。),取消了消息邮箱。
允许一个任务优先级被多个任务使用,当该优先级处于最高就绪态时,UCOS就会轮流调度处于这个优先级的所有任务,让每个任务运行一段由用户指定的时间长度,即时间片。
UCOSIII 可以采用锁定内核调度的方式而不是关中断的方式来保护临界段代码,这样就可以将关中断的时间降到最低。
UCOSIII 允许一个任务同时等待多个事件。也就是说,一个任务能够挂起在多个信号量或消息队列上,当其中任何一个等待的事件发生时,等待任务就会被唤醒。

41、什么是内核中的attribute?

答:attribute对应将来/sys/class/leds/目录里的内容,一般是文件和文件夹。这些文件其实就是sysfs开放给应用层的一些操作接口。
attribute有什么用?作用就是让应用程序可以通过/sys/class/leds/目录下面的属性文件来操作驱动进而操作硬件设备。(attribute其实是另一条驱动实现的路线。有区别于之前讲的file_operations那条线。)

42:、信号量(互斥锁)和自旋锁的区别?

答:自旋锁 (进程运行条件不足,但仍然不让出CPU,让CPU原地打转)
(1)自旋锁不能递归,(递归:在持有A锁的情况下,再次想要获取A锁)
(2)自旋锁可以用在中断上下文(信号量不可以,因为可能睡眠),但是在中断上下文中获取自旋锁之前要先禁用本地中断。 补:中断上下文不参与进程调度。
(3)自旋锁的核心要求是:拥有自旋锁的代码必须不能睡眠,要一直持有CPU直到释放自旋锁
(4)信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此信号量和读写信号量只能在进程上下文使用,不能在中断上下文使用。而**自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。**如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作。

42:、信号量和互斥锁的区别?

答:①互斥量用于线程的互斥,信号量用于线程的同步。这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
②互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

③note:信号量可以用来实现互斥量的功能
互斥量值只能为0/1,信号量值可以为非负整数。
也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。
④互斥锁的作用域仅在线程,而信号量可以作用域线程和进程。

补:关于信号量的使用

答:信号量(semaphore)是一种进程间同步及互斥工具。信号量在操作系统中实现时一般作为一个整数变量,这种信号量称为整型信号量。信号量S的物理意义:
S >= 0表示某资源的可用数;
S<0 其绝对值表示阻塞队列中等待该资源的进程数目;
信号量的两个操作方法是PV,**P操作为S=S-1;表示申请一个资源,V操作为S=S+1;表示释放一个资源。**因为对整数的加减1操作是在一个指令完成的,而硬件能够保证中断只能发生在指令之间,所以PV操作不会被中断打扰执行结果。

43:寄存器和内存的区别?

答:寄存器是中央处理器内的组成部份,是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。寄存器是计算机中重要的部件之一,它是cpu能够直接访问的存储设备,并且是内存与CPU进行沟通的桥梁。寄存器容量很小,但是读取速度是最快的。
寄存器由于容量小,不可能存放什么变量什么的,它一般就存放一些地址作为索引,cpu执行指令的时候根据它的地址去内存找相关内容来完成当前的执行。包括变量的内容什么的。至于通用寄存器ax之类,通常就用来暂存中间的结果,比如从内存取出的变量,先用一寄存器保存,然后执行指令,这样寄存器的内容会发生变化(指令是直接作用于寄存器对象的),然后把它的内容可以传给内存保存下来。

内存是挂在CPU外面的数据总线上。访问内存时要在CPU的寄存器填上地址,再执行相应的汇编指令。这时CPU会在数据总线上生成读取或者写入内存数据的时钟信号,最后内存的内容会被CPU寄存器的内容更新(写入)或被读入CPU的寄存器(读取)。

补:为什么寄存器比内存块?

答:原因:距离不同;设计不同(内存一位就是一个晶体管和一个电容,寄存器多很多电子元件);工作方式不同,寄存器的工作方式只有2步:(1)找到相关的位(2)读取这些位
内存的工作方式就复杂的多:
(1)找到数据的指针(指针可能存放在寄存器内,所以这一步就已经包括寄存器的全部工作了。)
(2)将指针送往内存管理单元(MMU),由MMU将虚拟的内存地址翻译成实际的物理地址。
(3)将物理地址送往内存控制器(memory controller),由内存控制器找出该地址在哪一根内存插槽(bank)上.
(4)确定数据在哪一个内存块(chunk)上,从该块读取数据。
(5)内存数据先被送回内存控制器,再送回CPU,然后开始使用。
内存的工作流程比寄存器多出许多步,每一步都会产生延迟,累积起来就使得内存比寄存器慢得多。
为了缓解寄存器和内存之间速度的巨大差异,硬件设计师做出了许多努力,包括CPU内部设置缓存,优化CPU工作方式,尽量一次从内存读取指令所要用到的全部数据等等。如增加cache。

43、怎么看程序堆栈使用量?

答:①Linux系统中使用-pstack
②keil软件中通过仿真查看 sp_max ,该指针指向最大栈顶记忆

44、Flash读写步骤和nor与nand的读写速率区别?

答:扇区擦除的过程如下:
(1) 检查“忙碌寄存器位 BSY”,以确认当前未执行任何 Flash 操作;
(2) 将“激活扇区擦除寄存器位 SER ”置 1,并设置“扇区编号寄存器位 SNB”,选择要擦除的扇区;
(3) 将 “开始擦除寄存器位 STRT ”置 1,开始擦除;
(4) 等待 BSY 位被清零时,表示擦除完成。
写入数据
擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还需要配置一系列的寄存器,步骤如下:
(1) 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
(2) 将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
(3) 针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作;
(4) 等待 BSY 位被清零时,表示写入完成。

1、 NAND 比NOR 便宜;NAND 的容量比NOR 大(指相同成本);NAND 的擦写次数是NOR 的十倍;NAND 的擦除和写入速度比NOR 快,读取速度比NOR 稍慢
2、 NAND 和NOR 的读都可以以字节为单位,但NAND 的写以page 为单位,而NOR 可以随机写每一个字节。NAND 和NOR 的擦除都以block 为单位,但一般NAND (8KB)的block 比NOR(512kb) 的block 小。另外,不管是NAND 还是NOR ,在写入前,都必须先进行擦kb除操作,但是NOR 在擦除前要先写0 ;
3、 NAND 不能在片内运行程序,而NOR 可以。但目前很多CPU 都可以在上电时,以硬件的方式先将NAND 的第一个block 中的内容(一般是程序代码,且也许不足一个block ,如2KB 大小)自动copy 到ram 中,然后再运行,因此只要CPU 支持,NAND 也可以当成启动设备;
4、 NAND Flash一般地址线和数据线共用,对读写速度有一定影响;而NOR 闪存数据线和地址线分开,所以相对而言读写速度快一些。

45、内存寻址、MMU相关

答:
在x86系统中,地址之间的转换先经过段机制的分段处理,由逻辑地址获得线性地址(虚拟地址),再经分页机制获得物理地址。
逻辑地址:在机器语言中用来指定一个操作数或一条指令 的地址。是指由程式产生的和段相关的偏移地址部分
虚拟地址:是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。若没有启用分页机制,那么线性地址直接就是物理地址。是一个32位无符号整数,可以用来表示4GB的地址。
物理地址:是目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。
段机制:
段是虚拟地址空间的基本单元,三个方面描述段:段的基地址;段的界限;段的保护属性

逻辑地址由一个段标志符和一个指定段内相对地址的偏移量组成。为了快速找到段选择符,CPU提供段寄存器,主要目的是为了存放段标识符,如 cs ss ds 等,cs代码段寄存器,指向包含程序指令的段,另外指明当前CPU特权级,有0-3等级依次递减。
为了加快逻辑地址到线性地址的转换,CPU又提供一种非编程寄存器,每一个寄存器中含有8kb的段描述符,由段选择符指定,指定完成后,相应的段描述符就由内存装入到对应的寄存器中。至此,该段的逻辑地址转换就可以直接引用寄存器来完成。

段描述符:每个段由一个8kb的段描述符表示,它描述了端的特征,被放在全局描述符表GDT或局部描述符表LDT中。

分段单元工作原理:
①先检查段选择符的TI字段,已决定段描述符存在GDT还是LDT
②从段选择符的index字段计算段描述符的地址,Index字段*8,这个结果和gdtr或ldtr中的内容相加
③把逻辑地址的偏移量和段描述符字段的值相加就得到了线性地址。

Linux中的分段:
在linux中使用分段和分页机制有些多余,因为分段和分页都可以划分进程的物理地址空间分段可以给每一个进程分配不同的线性地址空间,而分页把同一地址空间映射到不同的物理空间。由于①所有进程使用相同的段寄存器值时,内存管理更简单,因为所有进程能共享同样的一组线性地址,②RISC体系不利于分段机制,因此linux更喜欢分页。

Linux使用用户代码段和用户数据段、内核代码段和内核数据段来对指令和数据寻址。相应的段选择符由宏__USER_CS、__USER_DS、__KERNEL_CS、__KERNEL_DS分别定义。
注:①与段相关的线性地址从0开始,达到2 的 32次方-1的寻址限长,即在用户态或内核态下所有进程使用相同的逻辑地址。
所有段从0x00000000开始,即在Linux下逻辑地址和线性地址是一致的

硬件中的分页:
分页单元把线性地址转换成物理地址。其中一个关键任务是把请求的访问类型和线性地址的访问权限比较,如果访问无效,就产生一个缺页异常
线性地址被分成以固定长度为单位的组,称为页。页内部连续的线性地址被映射到连续额物理地址中。这样内核就可以指定一个页的物理地址和其存取权限,而不用指定页所包含的全部线性地址的存取极限。
分页单元把所有的RAM (物理地址)分成固定长度的页框,每一个页框包含一个页,也就是说一个页框和一个页的长度一致。
线性地址映射到物理地址的数据结构称为页表。页表存在主存中,并在启用分页单元之前必须由内核对页表进行适当的初始化。

页被分成3个域,分别是目录表、页表和偏移量。线性地址的转换分两步,每一步都基于一种转换表,第一种为页目录表,第二种是页表。使用二级转换模式是为了减少每个进程页表所需RAM的数量,如果使用一级页表,需要2的32次方除以2的12次方(4kb)=2的20次方个表项来表示每个进程的页表。二级模式可以只为实际使用虚拟内存的进程请求页表减少内存使用。

分页机制转换原理:
页目录的物理地址存在控制寄存器cr3中,先行地址中的Directory字段决定页目录中的目录项,目录项指向适当的页表。地址中的Table字段又决定页表中的表项,而表项中又含有页框的物理地址。Offset地段决定页框内的相对位置。

46、补充问题:main函数运行前做了哪些工作?

答: 一般嵌入式开发流程就是先建立一个工程,再编写源文件,然后进行编译,把所有的*.s文件和*.c文件编译成一个*.o文件,再对目标文件进行链接和定位,编译成功后会生成一个*.hex文件和调试文件,接下来调试成功的话,就可以将它固化到flash里面去。
启动代码是用来初始化电路以及用来为高级语言写的软件作好运行前准备一小段汇编语言,是任何处理器上电复位时的程序运行入口点。刚上电时,初始化时钟,设置完后,需要把一些源文件从flash里面copy到内存中,又要对它们进行初始化读写,这又有频率的设置。我们又要设置堆栈,要跳到C语言的main函数里面运行。对普通的ARM CPU有这样一个要求:在绝对地址为零的地方要构建一个异常向量表,但并不是所有的ARM CPU都留有这个一个空间,这就需要用到映射的功能。我们可以将其它地方的一些空间映射到绝对地址里面。当发生异常时,ARM核来读取异常中断表的时候,它会使用映射之后的那个表,这个就可以接着往下执行,否则在绝对地址零的地方找不到任何信息,程序就会死掉。这些运行的环境全部建立好后,程序就会跳转到我们的main函数里面。  总之,启动代码,就是对最小系统的初始化。包括晶振,CPU频率等。

程序的启动过程:异常向量表的初始化–存储区初始化–时钟初始化—设置堆栈–调用高级语言入口函数– main()函数

47、补充问题:kmalloc、vmalloc有什么区别?

48、补充问题:如何判断堆栈溢出?

49、补充问题:Linux的命令

50、补充问题:GDB的使用?

51、补充问题:内核链表?

52、Linux调度原理?

答:linux是抢占式调度,2.4内核采用O(1)调度器调度进程,凭借静态时间片算法针对每一处理器的运行队列,解决了大服务器的工作负载问题,但是应对不了交互程序进程。因此,2.6.23内核后采用完全公平调度算法(CFS)替代。
进程主要分为I/O消耗型和处理器消耗型,前者指进程的大部分时间用来提交I/O请求或等待I/O请求,后者进程把时间大多用在执行代码上。调度就是要在这两个矛盾的目标之间寻找平衡:进程响应迅速和最大系统利用率。Linux更倾向于优先调度I/O消耗型进程,以保证交互式应用性能,但同时也没忽略处理器消耗型进程。
调度算法最基本一类是使用优先级调度,优先级高先运行,低的后运行,相同的按时间片轮转方式运行。
进程提供了两种优先级,一种是普通的进程优先级,第二个是实时优先级。前者适用SCHED_NORMAL调度策略,后者可选SCHED_FIFO或SCHED_RR调度策略。任何时候,实时进程的优先级都高于普通进程,实时进程只会被更高级的实时进程抢占,同级**实时进程之间是按照FIFO(一次机会做完)或者RR(多次轮转)**规则调度的。

实时进程优先级,只有静态优先级,其范围在0-99之间。值越高的进程总是优先执行。如果有数个优先级相同的实时进程,那么系统就会按照进程出现在队列上的顺序选择进程。
  不同调度策略的实时进程只有在相同优先级时才有可比性:
  1. 对于FIFO的进程,意味着只有当前进程执行完毕才会轮到其他进程执行
  2. 对于RR的进程。一旦时间片消耗完毕,则会将该进程置于队列的末尾,然后运行其他相同优先级的进程,如果没有其他相同优先级的进程,则该进程会继续执行。
  总而言之,对于实时进程,高优先级的进程就是大爷。它执行到没法执行了,才轮到低优先级的进程执行。

普通的进程优先级,采用nice值决定优先级,范围是-20到19,默认为0,值越大优先级越低。在Linux中,nice值代表时间片的比例。
Linux的CFS调度器就是一个针对普通进程的调度类,CFS在分配时间片时,会考虑nice大小,以nice值为权重调整进程的时间片。此外,系统调度时,也要考虑进程的属性。例如如果进程属于交互式进程,那么可以适当的调高它的优先级,使得界面反应地更加迅速,从而使用户得到更好的体验。

补:CFS调度原理?

答:CFS调度器和以往的调度器不同之处在于没有时间片的概念,而是分配cpu使用时间的比例。例如:各个进程之间按照nice权重的比例分配cpu时间。例如:2个进程A和B。A的权重是1024,B的权重是2048。那么A获得cpu的时间比例是1024/(1024+2048) = 33.3%。B进程获得的cpu时间比例是2048/(1024+2048)=66.7%。我们可以看出,权重越大分配的时间比例越大,相当于优先级越高。在引入权重之后,分配给进程的时间计算公式如下:
分配给进程的时间 = 总的cpu时间 * 进程的权重/就绪队列(runqueue)所有进程权重之和

Linux调度实现,经过:

时间记账——以确保每个进程只在公平分配给它的处理器时间内运行。
进程选择——CFS算法的核心:通过红黑树选择最小vruntime的任务。vruntime:记录一个程序到底运行了多长时间和还要运行多久。
调度器入口——选择哪个进程进行运行,何时将其投入运行。主要调用pick_next_task(),选择最高优先级的进程。
睡眠和唤醒——确保调度程序不会选出一个本不愿意被执行的进程。

53、操作系统的实时性如何体现?

54、Linux中malloc的底层实现原理?

答:(1)开辟的空间小于128k时,调用brk()函数,主要是从低向高移动_edata指针,_edata指针指向的是Linux地址空间中堆段的末地址。过程是:开始分配时,移动_edata指针(此时,只分配虚拟地址,不分配物理内存),第一次读写该内存时,发生缺页中断,然后内核才分配物理内存,然后虚拟地址空间建立映射关系。
(2)开辟空间大于128k时,使用mmap()函数,原因是brk分配的内存需要等高地址内存释放后才能释放,因为_edata指针只有一个,不能跳过释放。mmap分配的内存可以单独释放。

55、为什么函数参数压栈顺序是从右向左?

答:因为照顾printf()这种不定参函数,printf函数的原型是:printf(const char* format,…)
它是一个不定参函数,那么我们在实际使用中是怎么样知道它的参数个数呢?这就要靠format了,编译器通过format中的%占位符的个数来确定参数的个数。
假设参数的压栈顺序是从左到右的,这时函数调用的时候,format最先进栈,之后是各个参数进栈,最后pc进栈,此时,由于format先进栈了,上面压着未知个数的参数,想要知道参数的个数,必须找到format,而要找到format,必须要知道参数的个数,这样就陷入了一个无法求解的死循环了!
而如果把参数从右到左压栈,函数调用时,先把若干个参数都压入栈中,再压format,最后压pc,这样一来,栈顶指针加2便找到了format,通过format中的%占位符,取得后面参数的个数,从而正确取得所有参数。
所以,如果不存在这种不定参的函数,则参数的压栈顺序无论是从左到右还是从右到左都是没关系的。

56、什么是二叉树?

57、如何提高操作系统的开机时间

答:

58、CPU框架包括什么?

59、两个有序链表合并

60、tcp报文头与epoll

61、项目带操作系统后有什么区别

答:①嵌入式实时操作系统提高了系统的可靠性。操作系统管理的项目,干扰只引起若干进程中的一个被破坏,可以通过系统运行的系统监控进程对其进行修复。通常情况下,这个系统监视进程用来监视各进程运行状况,遇到异常情况时采取一些利于系统稳定可靠的措施,如把有问题的任务清除掉。
②其次,提高了开发效率,缩短了开发周期。在嵌入式实时操作系统环境下,开发一个复杂的应用程序,通常可以按照软件工程中的解耦原则将整个程序分解为多个任务模块。每个任务模块的调试、修改几乎不影响其他模块。
③再次,嵌入式实时操作系统充分发挥了32位cpu的多任务潜力。32位cpu比8、16位 cpu快,另外它本来是为运行多用户、多任务操作系统而设计的,特别适于运行多任务实时系统。32位cpu采用利于提高系统可靠性和稳定性的设计,使其更容易做到不崩溃。例如, cpu运行状态分为系统态和用户态。将系统堆栈和用户堆栈分开,以及实时地给出cpu的运行状态等,允许用户在系统设计中从硬件和软件两方面对实时内核的运行实施保护。

62、实时性系统的基本特性:

答:在特定的时间内完成特定的任务,实时性和可靠性。所谓实时操作系统,实际上是指操作系统工作时,各种资源可以根据需要随时进行动态分配,由于各种资源可以根据需要进行动态分配,所以其处理事务的能力较强,速度较快.

63、中断和轮询的特点

答:程序轮询方式:定时对各种I/O设备轮流询问一遍有无处理要求,轮询之后,有要求处理的,则进行处理.在处理I/O设备的请求之后,处理机返回继续工作。
程序中断简称中断,是指CPU在正常运行程序的过程中,由于预先安排或发生了各种随机的内部或外部事件,使CPU中断正在运行的程序,而转到为响应的服务程序去处理。
轮询:效率低,等待时间长,CPU利用率不高。
中断:容易遗漏一些问题,CPU利用率高。

64、分段和分页的区别

答:页是信息的物理单位,分页是实现离散分配方式,以消减零散的外零头,提高内存的利用率;或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。
段是信息的逻辑单位,它含有一组其意义相对完整的信息.分段的目的是为了更好满足用户的需要。页的大小固定且由系统确定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的。段的长度却是不固定的,决定于用户所编写的程序.。

65、makefile文件的作用是什么?

答:一个工程中的源文件不计其数,其按类型,功能,模块分别放在若干目录中.makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作.makefile带来的好处就是------“自动化编译”.一旦写好,只需要一个make命令,整个工程完全自动编译.
make是一个命令工具,是一个解释makefile中指令的命令工具.

嵌入式面试_面试宝典整理(包括内核)相关推荐

  1. python web前端开发面试_面试前端,听听别人怎么说!

    分享一个人的面试经验: 一年半经验,百度.有赞.阿里面试总结 前言 人家都说,前端需要每年定期出来面面试,衡量一下自己当前的技术水平以及价值,本人17年7月份,毕业到现在都没出来试过,也没很想换工作, ...

  2. 南方航空java面试_面试经验 南航面试经历分享

    南方航空面试经历分享 前言 每个人的成功经验都是来之不易的,今天小V的面经写得有点长,为了给大家写的详细一些,参考多一些,也是拼了! 小V的基本情况:小V是一名大三的在校学生,学的还是搬砖的理工科专业 ...

  3. freemarker面试_面试请不要再问我Spring ,阿里架构师吐血整理,这是对“Spring家族”最完美的诠释...

    随着现在互联网的发展,技术更新的周期越来越短,新兴技术的发展和提升也越来越快,对于开发者来说,一系列的开源框架的出现对工作起到了非常大的帮助.提起开源框架,相信大家最为熟悉的还是Spring家族,作为 ...

  4. 浏览器渲染机制面试_面试官不讲码德,问我Chrome浏览器的渲染原理(6000字长文)...

    前言 对于HTML,css和JavaScript是如何变成页面的,这个问题你了解过吗?浏览器究竟在背后都做了些什么事情呢?让我们去了解浏览器的渲染原理,是通往更深层次的开发必不可少的事情,能让我们更深 ...

  5. python接口测试面试_面试秘籍 | 一文搞定面试中接口测试问题

    作为软件测试的垂直领域深耕者,不仅要精于软件测试技术,更要关注行业软件测试需求,最直观的需求莫过于企业招聘需求,最近有不少求职的朋友跟我诉苦,企业对接口测试的要求越来越多了,都跪在了接口测试上,这让我 ...

  6. 安卓线程同步面试_面试BAT大厂,可少不了这些题目!

    点击上方的终端研发部,右上角选择"设为星标" 每日早8点半,技术文章准时送上 公众号后台回复"学习",获取作者独家秘制精品资料 往期文章 走出腾讯和阿里,大厂员 ...

  7. mysql数据库调优 面试_面试-MySQL篇:数据库调优

    今天谈谈 MySQL 的调优问题,比较偏项目实战.面试中也喜欢问 "xxx如何优化"的问题,这类问题相对开放,一不小心就变成了送命题,但是风险与机遇并存,假如你能理论结合项目经验给 ...

  8. 数据暑假实习面试_面试数据科学实习如何准备

    数据暑假实习面试 Unfortunately, on this occasion, your application was not successful, and we have appointed ...

  9. 动态规划编程面试_面试的前25大动态编程问题

    动态规划编程面试 Dynamic programming is one of the most asked paradigms in any product-based company intervi ...

最新文章

  1. GNU make manual 翻译(五十一)
  2. u-boot2013.01.01 for s5pv210: u-boot启动流程
  3. 在HTML文件中加载js
  4. Flask web开发之路二
  5. Java与MySQL连接报错_java连接数据库报错(
  6. 华为Mate X2黄牛价已超两万,结果小伙子没拿稳,成全网首摔?
  7. 【Flink】Flink jvm参数配置GC日志
  8. android开发实现静默安装(fota升级)
  9. pytorch tensor操作:tensor与numpy转换
  10. 数据结构课程设计——宿舍管理查询软件
  11. SpringBoot解决XSS跨站脚本攻击
  12. Matlab 曲线拟合之polyfit与polyval函数
  13. 苹果怎么滚动截屏_30个小技巧,带你感受苹果系统到底有多好用
  14. XBee无线模块的通信方式和结合Arduino的具体实施过程
  15. CVR/CTR/ROI
  16. OpenGLES学习(一)图片显示
  17. echarts如何在json地图上设置多种颜色的点位和自定义背景弹出框
  18. a12处理器和骁龙855_骁龙855和苹果A12处理器对比实用评测
  19. 特征向量中的NLU意图识别流程及应用
  20. python的函数嵌套机制

热门文章

  1. 基于LabVIEW的电阻合格率检测装置仿真设计
  2. 哈佛的学生都在看这种视频,每天15分钟,英语水平暴增!
  3. IT行业工资高,但并不适合所有人
  4. 基于Qt的收银点餐系统之小票打印(一)
  5. 光纤交换机是什么?光纤交换机品牌ONV/光网视
  6. 移动广告效果监测,App推广广告投放归因工具
  7. 怎么把视频里的音乐提取成音频?
  8. 快讯:好奇号火星车传来喜讯
  9. 如何安装husky_Husky robot 玩耍1
  10. 一个专科计算机专业就业前景如何,读专科有前途吗?附大专最吃香的五大专业...