select和epoll的区别

答:select的时间复杂度O(n)。它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。 epoll的时间复杂度O(1)。epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

异步IO和同步IO区别?

答:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。而异步就是过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。所以异步IO和同步IO区别就是数据拷贝的时候进程是否阻塞。

linux的进程状态有哪些?

答:Linux系统下进程通常存在6种不同的状态,分为:就绪态、运行态、僵尸态、可中断睡眠状态(浅度睡眠)、不可中断睡眠状态(深度睡眠)以及暂停态。

什么是死锁?产生死锁的原因是什么?

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

1.竞争资源;

2.进程间推进顺序非法

什么是优先级翻转,如何避免优先级翻转?

答:优先级翻转指的是一个具有中等优先级的任务比一个更高优先级的任务先执行。优先级翻转的主要原因是对共享资源的访问产生了互斥,因此我们可以采用带有优先级继承机制的互斥量来避免优先级翻转。

Linux驱动程序的功能是什么?

答:设备驱动连接操作系统和硬件。设备驱动程序是一种可以使计算机与设备进行通信的特殊程序,可以说相当于硬件的接口。操作系统只有通过这个接口,才能控制硬件设备的工作。安装在操作系统中的驱动程序可以完成设备的初始化和释放,进行外部数据和操作系统的通信和数据交互,控制硬件的行为,并检查设备可能出现的故障并报错。

Linux驱动程序的分类有哪些?

答:Linux将硬件设备分为3大类,分别是字符设备、块设备和网络设备。字符设备是指那些能一个字节一个字节读取数据的设备,如键盘鼠标等,常见的SPI/I2C/UART默认也是字符设备。块设备与字符设备类似,一般是像磁盘一样的设备。网络设备主要负责主机之间的数据交换。与字符设备和块设备完全不同,网络设备主要是面向数据包的接收和发送而设计的。

内核程序中申请内存使用什么函数?

答:内核中使用kmalloc(),kzalloc(), vmalloc(), alloc_page()等函数。 kamlloc申请的内存区域位于物理内存的映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,存在比较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。 kzalloc() 函数与 kmalloc() 额外附加了 __GFP_ZERO 标志,所以它除了申请内核内存外,还会对申请到的内存内容清零。 vmalloc() 函数则会在虚拟内存空间给出一块连续的内存区,但这片连续的虚拟内存在物理内存中并不一定连续。由于 vmalloc() 没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。需要注意的是vmalloc() 和 vfree() 可以睡眠,因此不能从中断上下文调用。 alloc_page()用于申请连续的物理页,可以通过page_address()把指定的页转化成逻辑地址。 如何区分这几个概念: kmalloc是最常用的内存分配函数,可以原子操作,速度快,缺点就是大小有上限,kzalloc是强制清零版本的kmalloc,而vmalloc只有在需要申请大内存的时候使用,会陷入阻塞

内核程序中申请内存和应用程序时申请内存有什么区别?

答:应用程序的内存申请,例如C/C++可以使用malloc函数,与内核的kmalloc(),kzalloc(), vmalloc(), alloc_page()等函数相比,比较像vmalloc机制,即虚拟地址申请,物理地址不一定连续,区别在于,应用程序malloc不会做分配物理页的动作,交由内核去申请,而vmalloc本身在内核中会执行这个动作。

自旋锁和信号量在互斥使用时需要注意什么?在中断服务程序里面的互斥是使用自旋锁还是信号量?

答:自旋锁会一直自旋等待,不会休眠;而信号量是等待信号来唤醒进程,进程会睡眠。使用时注意希望长等待不吃系统资源时使用信号量,反过来短期的等待使用自旋锁,但会增加系统开销。 中断中不可以使用信号量,因为中断不能睡眠。

异步IO和同步IO的区别

答:同步和异步来自于Linux内核态和用户态的切换,通常IO包括网络IO和来自设备/文件的IO,完成IO操作分两步,发起请求和执行。 当请求被阻塞时,为同步IO,否则为异步IO。同步IO进程需要等待IO执行完毕。而异步IO在IO执行时,进程可以继续执行,不陷入阻塞。

讲一讲Linux中断的原理和开发方法?

答:Linux中断处理过程分为两部分: 上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。 下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。 那些对时间敏感、执行速度快的操作可以放到中断处理函数中,也就是上半部。剩下的所有工作都可以放到下半部去执行,比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。一般来说,不希望被其他中断打断、对时间敏感、和硬件有关的任务建议放在中断上半部。

说一说进程的几个状态及转换条件?

答:进程状态包括就绪态,运行态,阻塞态,除此以外还包括新建态和退出态。 进程首次创建并未参与调度为新建态 从新建态做好准备(初始化成功)等待调度为就绪态 内核调度此进程时,此进程从就绪态转换为运行态 进程执行完内核分配的时间片后,从运行态再次回到就绪态 进程执行时如果等待其他条件满足后才能执行,则进程从运行态转换为阻塞态 阻塞的进程等待的条件满足后,进程重新进入就绪态 进程终止或者异常,进程将进入退出态

static修饰局部变量和全局变量会有什么效果

答:静态全局变量:具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被 static 关键字修饰过的变量具有文件作用域。 静态局部变量:具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在。

堆和栈的区别

答:栈由系统分配,堆由程序员控制,例如C/C++的malloc函数 栈的大小较小,有默认上限,堆理论上可以申请整个虚拟内存的大小 栈向下增长,地址由高到低,堆向上增长,地址从低到高 栈的分配效率较高,保证函数执行跳转的效率,堆的分配更为复杂,容易产生碎片,需要回收 栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等;堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。

 简述gcc编译过程

答:预处理、编译、汇编和链接,一个hello.c的c语言程序如下。 预处理阶段:hello.c–>hello.i 编译阶段:hello.i–>hello.s 汇编阶段:hello.s–>hello.o 链接阶段:hello.o–>hello

线程与进程的区别

答:进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发;线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。 一个线程只能属于一个进程,而一个进程可以有多个线程 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存 进程是资源分配的最小单位,线程是CPU调度的最小单位 进程切换的开销也远大于线程切换的开销。进程编程调试简单可靠性高,但是创建销毁开销大;线程正相反,开销小,切换速度快,但是编程调试相对复杂

野指针出现情况、怎么解决

答:1.指针变量声明时没有被初始化,可以在指针声明时初始化,可以是具体的地址值,也可让它指向NULL。 2.指针 p 被 free 或者 delete 之后,没有置为 NULL。指针指向的内存空间被释放后指针应该指向NULL。 3.指针操作超越了变量的作用范围,在变量的作用域结束前释放掉变量的地址空间并且让指针指向NULL。

dma有什么用

答:直接存储器存取(Direct Memory Access,DMA)可以让某些电脑内部的硬体子系统(电脑外设),可以独立地直接读写系统存储器,而不需绕道 CPU。在同等程度的CPU负担下,DMA是一种快速的数据传送方式。它允许不同速度的硬件装置来沟通,而不需要依于 CPU的大量中断请求。大大提高了访问效率,减少访问时间,降低CPU资源的消耗。

说一说RISC和CISC的区别

答:精简指令集RISC和复杂指令集CISC CISC处理的是不等长指令集,它必须对不等长指令进行分割,因此在执行单一指令的时候需要进行较多的处理工作。而RISC执行的是等长精简指令集,CPU在执行指令的时候速度较快且性能稳定。因此在并行处理方面RISC明显优于CISC,RISC可同时执行多条指令,它可将一条指令分割成若干个进程或线程,交由多个处理器同时执行。由于RISC执行的是精简指令集,所以它的制造工艺简单且成本低廉。

谈一谈什么是系统声明周期,说说你对敏捷开发的理解以及和SDLC的关系

答:SDLC:sdlc(系统生命周期,系统生存周期)是软件的产生直到报废的生命周期,是软件工程中的一种思想原则,包括: 问题定义及规划、需求分析、软件设计、程序编码、软件测试、运行维护 敏捷开发的核心是迭代开发(iterative development)。敏捷一定是采用迭代开发的方式。迭代开发将一个大任务,分解成多次连续的开发,本质就是逐步改进。一般采用"增量开发"(incremental development)划分迭代。所谓"增量开发",指的是软件的每个版本,都会新增一个用户可以感知的完整功能。 虽然敏捷开发将软件开发分成多个迭代,但是也要求,每次迭代都是一个完整的软件开发周期,必须按照软件工程的方法论,进行正规的流程管理。也就是说,敏捷开发的每一次迭代都需要一个完整的SDLC。

说说对MMU及TLB的理解

答:MMU是内存管理单元,它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制,在较为简单的计算机体系结构中,负责总线的仲裁以及存储体切换(bank switching,尤其是在8位的系统上)。 TLB(Translation Lookaside Buffer)传输后备缓冲器是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存。TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。 TLB( Translation Look- aside buffer)专门用于缓存内存中的页表项,一般在MMU单元内部。TLB是一个很小的 cache,TLB表项( TLB entry)数量比较少,每个TLB表项包含一个页面的相关信息,例如有效位、虚拟页号、修改位、物理页帧号等。当处理器要访问一个虚拟地址时,首先会在TLB中查询。如果TLB表项中没有相应的表项,称为TLB Miss,那么就需要访问页表来计算出相应的物理地址。如果TLB表项中有相应的表项,那么直接从TLB表项中获取物理地址,称为TLB命中。

芯片选型考虑哪些因素?

答:1.根据功能设计需求、成本、供应商等主要因素初步确定几款合适的芯片。 2.普通I/O口,考虑数量、负载能力,还需要保证裕量,如果有迭代升级,还需要考虑兼容性 3.片上存储和外围存储,保证bootloader和程序image的容量;内存支持,由程序的RAM需求决定 4.主频及时钟,决定芯片的运行效率,响应和处理速度 5.电源及功耗,由板上电源和承载能力决定 6.如果是CPU芯片,考虑多核和多线程并发能力;如果是FPGA芯片,考虑逻辑单元资源数;如果是DSP芯片,考虑浮点计算能力 7.工作环境,如辐射、单粒子和温度范围 8.芯片成本和交付日期,是否可以购买到,以及购买周期与开发周期的平衡 总结,与软件相关的因素:I/O,内存,外存,处理器核和多线程,系统可移植性等

有cache的CPU上使用DMA如何保证数据的一致性

答:Cache是CPU和主存之间的缓冲,DMA是为了主存和I/O数据交互设计的,期间CPU不参与控制。那么如果数据在主存中被CPU修改但是仍在cache中,即尚未更新主存,此时DMA获取的将是旧的数据,导致数据的不一致性。因此DMA在访问主存时,应当先检查cache是否命中,如果命中的话,DMA需要从Cache读取数据而非内存。 在设计时可以使用总线监视技术或者nocache机制解决非一致性问题,在软件层次上,当DMA往主存写数据时,在DMA传输之前,可以invalid DMA Buffer地址范围的高速缓存。在DMA传输完成后,程序读取数据不会由于cache hit导致读取过时的数据。 相反,DMA将数据输出时,在DMA传输之前,可以clean DMA Buffer地址范围的高速缓存,clean的作用是写回cache中修改的数据。在DMA传输时,不会把主存中的过时数据发送到I/O设备。

volatile关键字作用?

答:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。一般说来,volatile用在如下的几个地方:

 1) 中断服务程序中修改的供其它程序检测的变量需要加 volatile;

 2) 多任务环境下各任务间共享的标志应该加 volatile;

 3) 存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义;

简述字节对齐?

答:内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。 如果不按照平台要求对数据存放进行对齐,会带来存取效率上的损失。比如32位的Intel处理器通过总线访问(包括读和写)内存数据。每个总线周期从偶地址开始访问32位内存数据,内存数据以字节为单位存放。如果一个32位的数据没有存放在4字节整除的内存地址处,那么处理器就需要2个总线周期对其进行访问,显然访问效率下降很多。因此,通过合理的内存对齐可以提高访问效率。为使CPU能够对数据进行快速访问,数据的起始地址应具有“对齐”特性。比如4字节数据的起始地址应位于4字节边界上,即起始地址能够被4整除。此外,合理利用字节对齐还可以有效地节省存储空间。但要注意,在32位机中使用1字节或2字节对齐,反而会降低变量访问速度。因此需要考虑处理器类型。还应考虑编译器的类型。在VC/C++和GNU GCC中都是默认是4字节对齐。 字节对齐最主要反映在结构体对齐,对齐的原则如下: 

1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。 

2) 结构体或类的自身对齐值:其成员中自身对齐值最大的那个值。

3) 指定对齐值:#pragma pack (value)时的指定对齐值value。

 4) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

 宏函数和内联函数的区别

答:内联函数是代码被插入到调用者代码处的函数。如同 #define 宏,内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。 宏定义不检查函数参数和返回值,只是展开,相对来说,内联函数会检查参数类型,所以更安全。 内联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以像调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。

虚拟地址怎么转换到物理地址

答:地址转换(Address Translation)负责将虚拟地址转换成物理地址。现代操作系统有很多实现手段: 最简单的base+bound技术,进程通过寄存器base和bound来转换内存,不能保证安全性,程序的不同内存区需要连续,不能动态管理。 段技术:硬件为每个进程分配一组Base和Bound寄存器,每一对Base和Bound控制虚拟地址空间的一部分内存,称为段。每一段的虚拟地址空间是连续的,转换得到物理地址空间也是连续的,各个段之间不需要连续。这个技术可以很好的管理不同内存区,但是对于长度掌控的管理开销较大,寻找一段长度适合的物理地址需要额外开销。 页技术:将虚拟内存空间和物理内存空间皆划分成大小相同的页面,例如4KB、8KB和16KB等。并将页作为内存空间的最小分配单位,一个程序的一个页面(虚拟页面)可以存放在任何一个物理页面中。一个程序发出的虚拟地址由虚拟页面号和页内偏移值两部分组成。 快表TLB通常和处理器在一起,查找速度非常快,包含多级TLB,第一级TLB容量小、速度快,第二级TLB容量大、速度比第一级慢一些。可以大大提高虚拟地址转换为物理地址的效率

进程A执行,发生一个中断,中断发了一个信号,会让更高优先级的进程B执行,这时候中断结束,应该返回进程A还是进入进程B,并解释理由

答:返回进程A。操作系统中中断优先级高于普通的进程,中断发生时,将保护进程A的现场并进入中断上下文,此时进程A仍然是执行态;中断结束后,操作系统将恢复进程A的现场并执行剩下的时间片,进程B的优先级虽然提高了,但需要等待A执行结束后下次调度时才可以执行,如果系统中没有其他中断或者更高优先级的进程的情况。

如果两个进程,都要去调用一个设备驱动,设备驱动里面定义的变量是在内核态吗?如果进程A访问并改变了这个变量,那么进程B再访问,是不是读到改变之后的值?

答:驱动程序中的变量是在内核态中,因为操作系统只有内核态可以访问到硬件设备,驱动程序从内核态像用户态输出API以便调用和间接访问硬件设备。 进程B访问的是改变后的值,只有在没有考虑并发编程的驱动程序中才可能发生数据的不一致性。内核驱动程序开发必须考虑并发的问题,因为驱动程序将会被一个或者多个进程访问,对于共享的变量,需要加入互斥锁、自旋锁、信号量或者原子操作等同步技术保证数据的一致。

两个进程的内核空间是共享的吗

答:用户空间中,每个进程的用户空间是互相独立的,互不相干。 内核空间中,绝大部分是共享的,并不是完全共享,因为内核空间中,不同进程的内核栈之间是不共享的。之所以使用进程的内核栈而非“用户栈”,是避免用户态下进程被抢占改变引发内核崩溃,因此每个进程在内核中有一个独立的内核栈。

 三次握手为什么ACK信号是SYN信号值+1

答:为什么三次握手过程中,比如第一次握手,A向B发了seq=x,B给A回的ack是=x+1呢?如果有数据的话,那理应等于x+LEN。如果没有数据,那就应该保持不动就等于x才对。其实不然,服务器端回复的这个+1,是代表他收到了SYN标示。也就是说由于SYN或者FIN的存在,即使没有数据传输,但服务器端仍然需要通过+1来回应一句“我收到了”。因此握手过程中seq=x的话,ack = x+1。其他几次握手挥手也是同样道理。

驱动程序里面,如果有一个buffer,在不同场景下需要定义不同大小,怎么办?

答:可以预先定义一个结构体,包含长度变量和一个指针,长度标志由ioctl函数从应用程序输入,并动态申请和释放内存。也可以在模块加载时预先申请不同大小的缓存。 另一种办法是动态检测程序所需要的缓存数量,采用多级缓存的方式,根据所需传递数据的大小动态申请分布式缓存。

传引用和传指针区别

答:指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

请你说一说cache的作用

答:Cache存储器,电脑中为高速缓冲存储器,是位于CPU和主存储器DRAM(Dynamic Random Access Memory)之间,规模较小,但速度很高的存储器,通常由SRAM(Static Random Access Memory 静态存储器)组成。它是位于CPU与内存间的一种容量较小但速度很高的存储器。CPU的速度远高于内存,当CPU直接从内存中存取数据时要等待一定时间周期,而Cache则可以保存CPU刚用过或循环使用的一部分数据,如果CPU需要再次使用该部分数据时可从Cache中直接调用,这样就避免了重复存取数据,减少了CPU的等待时间,因而提高了系统的效率。Cache又分为L1Cache(一级缓存)和L2Cache(二级缓存),L1Cache主要是集成在CPU内部,而L2Cache集成在主板上或是CPU上。

动态库和静态库的区别,后缀格式,以及函数的相对地址区别

答:区别 命名方式不同: 静态库libxxx.a:库名前加”lib”,后缀用”.a”,“xxx”为静态库名。 动态库libxxx.so:库名前加”lib”,后缀变为“.so”。 链接时间不同: 静态库的代码是在编译过程中被载入程序中。 动态库的代码是当程序运行到相关函数才调用动态库的相应函数 链接方式不同: 静态库的链接是将整个函数库的所有数据在编译时都整合进了目标代码。 动态库的链接是程序执行到哪个函数链接哪个函数的库。(用哪个链接哪个) 优缺点? 静态库: 优点是,在编译后的执行程序不再需要外部的函数库支持,运行速度相对快些; 缺点是,如果所使用的静态库发生更新改变,你的程序必须重新编译。 动态库 : 优点是,动态库的改变并不影响你的程序,所以动态函数库升级比较方便; 缺点是,因为函数库并没有整合进程序,所以程序的运行环境必须提供相应的库。

说说你了解的进程调度算法

答:不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。 批处理系统 批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。 先来先服务 first-come first-serverd(FCFS) 按照请求的顺序进行调度。有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 短作业优先 shortest job first(SJF) 按估计运行时间最短的顺序进行调度。长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。 最短剩余时间优先 shortest remaining time next(SRTN) 按估计剩余时间最短的顺序进行调度。 交互式系统 交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。 时间片轮转 将所有就绪进程按FCFS的原则排成一个队列,每次调度时,把CPU时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。时间片轮转算法的效率和时间片的大小有很大关系:因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。而如果时间片过长,那么实时性就不能得到保证。 优先级调度 为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。 多级反馈队列 一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 实时系统 实时系统要求一个请求在一个确定时间内得到响应。分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。包括公平调度、RR、FIFO等。

SPI是什么?有几条线?几种模式?

答:SPI,是一种高速的,全双工,同步的通信总线,在芯片的管脚上只占用四根线。SPI总线有四种工作方式,通过行同步时钟极性和相位可以进行组合配置。

常见的操作系统进程调度策略有哪些?

答:常见的调度策略有6种,先来先服务调度(FCFS)。短进程优先调度(SPF)。高响应比优先调度(HRN)。时间片轮转调度(RR)。多级反馈队列调度(RRWMF)。最高优先权优先调度(FPF)。

逻辑地址、线性地址、物理地址、总线地址、虚拟地址的区别?

答:逻辑地址指的是与内存段相关的偏移地址部分。线性地址:线性地址 = 逻辑地址 + 基地址。 如果启用了分页机制,那么线性地址使用页表项变换后就是物理地址。如果没有启用分页机制,那么线性地址就是物理地址。总线地址其实就是物理地址。虚拟地址是通过MMU内存管理虚拟映射出来的地址。

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

答:1、系统调用,提供特定的用户空间与内核空间的信息传递。

2、信号,内核空间出现一些异常时候会发送信号给进程,如SIGSEGV、SIGPIPE等。 

3、/proc,proc可以读取内核空间的配置信息和运行状态并且设置部分属性的值。 

4、文件,可以通过指定文件的读写操作来实现通信。 

5、netlink,类似socket通信方式,可以读写大量的数据,实现稍微复杂。

6、ioctl,可以实现数据量比较少时候的通信

预防死锁?

答:打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

硬链接与软链接的区别?

答:硬链接与原文件公用一个inode号,他们是同一个文件,而软链接与原文件拥有不同的inode号,他们是两个不同的文件;在文件属性上软链接明确写出了是链接文件,而硬链接没有写出来,因为在本质上硬链接文件和原文件是完全平等关系;软链接的链接数目不会增加,硬链接没增加一个,链接数目就会加1;硬链接文件显示的大小是跟原文件是一样的,软连接不一定。 

 Linux系统的挂起、待机、休眠?

答:挂起:CPU、内存工作,而硬盘、显示器等外部设备停止工作。 待机:只对内存供电,CPU、外设停止工作。 休眠:CPU、内存停止工作,内存中的数据保存在硬盘中,外部设备也停止工作。

TCP、UDP的区别?

答:TCP是面向连接的,UDP是面向无连接的。 TCP是面向字节流的,UDP是基于数据报的。 TCP提供可靠服务(正确性、顺序性),UDP提供不可靠服务。 TCP程序结构复杂,占用资源多;UDP程序结构简单,占用资源少。 TCP有拥塞控制;UDP没有拥塞控制。 TCP只支持一对一;UDP支持一对一、一对多、多对一、多对多。

 简述TCP三次握手的过程。

答:第一次握手:客户端创建传输控制块,然后向服务器发出连接请求报文(将标志位SYN置1,随机产生一个序列号seq=x),接着进入SYN-SENT状态。 第二次握手:服务器收到请求报文后由SYN=1得到客户端请求建立连接,回复一个确认报文(将标志位SYN和ACK都置1,ack=x+1,随机产生一个序列号seq=y),接着进入SYN-RCVD状态。此时操作系统为该TCP连接分配TCP缓存和变量。 第三次握手:客户端收到确认报文后,检查ack是否为x+1,ACK是否为1,是则发送确认报文(将标志位ACK置1,ack=y+1,序列号seq=x+1),此时操作系统为该TCP连接分配TCP缓存和变量。服务器收到确认报文并检查无误后则连接建立成功,两者都进入ESTABLISHED状态,完成三次握手。

TCP为什么需要三次握手,第三次握手去掉行不行?

答:进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。第三次握手不能去掉,第三次握手能防止发生死锁,因为若为两次握手且服务器发出第二次握手而客户端没有收到,服务器开始传输数据报后客户端便不会理会,导致服务器以为丢包而源源不断地发送数据报,造成死锁。

简述TCP四次挥手的过程。

答:第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

为什么TCP建立连接是三次握手,而关闭连接却是四次挥手?

答:建立连接的时候,服务器在LISTEN状态下,不需要等待,可以立即建立连接,把ACK和SYN放在一个报文里发送给客户端,因此只需要三次握手。 而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接受数据,而自己未必将所有数据都发送完了,所以服务器可以立即关闭,也可以发送一些数据后再关闭,所以服务器的确认报文(ACK=1)和连接释放报文(FIN=1,ACK=1)一般分开发送,因此形成四次握手。

进程和线程的区别?

答:进程是资源分配的最小单位,线程是CPU调度的最小单位。一个进程可以包含多个线程,所以进程要比线程消耗更多的计算机资源。每个进程都有单独的地址空间,而同一进程内的线程共享进程的地址空间。

 C语言中,static关键字的作用?

答:在C中,static主要定义全局静态变量、定义局部静态变量、定义静态函数。

 1、定义全局静态变量:在全局变量前面加上关键字static,该全局变量变成了全局静态变量。全局静态变量有以下特点。

a.在全局区分配内存。

 b.如果没有初始化,其默认值为0.

c.该变量在本文件内从定义开始到文件结束可见。 

2、定义局部静态变量:在局部变量前面加上关键字static,其特点如下: 

a.该变量在全局数据区分配内存。 

b.它始终驻留在全局数据区,直到程序运行结束。 

c. 其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束

 堆和栈得区别?

答:堆和栈的主要区别有:1、栈由系统自动分配,而堆是人为申请开辟;

2、栈获得的空间较小,而堆获得的空间较大;

3、栈由系统自动分配,速度较快,而堆一般速度比较慢;

4、栈是连续的空间,而堆是不连续的空间。

什么是野指针,产生的原因是什么?

答:野指针是指向位置随机的、不正确的指针。

野指针产生的原因有: 1、创建指针时没有对指针进行初始化,导致指针指向一个随机的位置;

2、释放指针指向的内存后没有置空,从而指向垃圾内存;

DMA有什么用

答:DMA是在专门的硬件( DMA)控制下,实现高速外设和主存储器之间自动成批交换数据尽量减少CPU干预的输入/输出操作方式。主要作用就是减少CPU的负担。

进程间通信方法

答:常用的进程间通信方式有: 管道 ,包括有名管道和无名管道。 信号量。 共享内存 消息队列 本地套接字

程序中的内存分配方法

答:内存为程序分配空间有四种分配方式: 

1、连续分配方式 

2、基本分页存储管理方式 

3、基本分段存储管理方式

 4、段页式存储管理方式

c语言volatile作用和用法

答:volatile关键字用来阻止编译器认为的无法“被代码本身”改变的代码进行优化。如在C语言中,volatile关键字可以用来提醒编译器它后面所定义的变量随时有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。

编译有几个阶段 每个阶段做什么事情

答:词法分析阶段:读入源程序,对构成源程序的字符流进行扫描和分解,识别出单词, 语法分析阶段:机器通过词法分析,将单词序列分解成不同的语法短语,确定整个输入串能够构成语法上正确的程序。 语义分析阶段:检查源程序上有没有语义错误,在代码生成阶段收集类型信息 中间代码生成阶段:在进行了上述的语法分析和语义分析阶段的工作之后,有的编译程序将源程序变成一种内部表示形式 代码优化:这一阶段的任务是对前一阶段产生的中间代码进行变换或进行改造,目的是使生成的目标代码更为高效,即省时间和省空间 目标代码生成:这一阶段的任务是把中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码

内核申请内存vmalloc和kmalloc的区别是什么

答:1,vmalloc分配的一般为高端内存,只有当内存不够的时候才分配低端内存;kmallco从低端内存分配。

 2,vmalloc分配的物理地址一般不连续,而kmalloc分配的地址连续,两者分配的虚拟地址都是连续的;

 3,vmalloc分配的一般为大块内存,而kmaooc一般分配的为小块内存,(一般不超过128k);

简单描述一下数组指针和指针数组

答:数组指针是一个指针,指向一个数组。指针数组由n个指针类型的数组元素组成。数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。

简单描述linux设备驱动中的总线,设备和驱动的关系。

答:总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。一个现实的Linux设备和驱动通常都需要挂接在一种总线上。设备与驱动的关联通过总线的match()方法进行匹配,驱动挂载总线时与所有设备进行匹配,设备挂载总线时与所有的驱动进行匹配,所以驱动和设备的挂载无先后之分。匹配成功后会通过调用驱动的probo()方法来初始化设备。

简述一下什么是红黑树

答:红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一倍。具体来说,红黑树是满足如下条件的二叉查找树

 1.每个节点要么是红色,要么是黑色。

 2.根节点必须是黑色

 3.红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)。

 4.对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点。

 5.最长的路径长度不会超过任意路径的两倍。

指针和引用的区别

答:(1)指针是实体,占用内存空间;引用是别名,与变量共享内存空间。 

(2)指针不用初始化或初始化为NULL;引用定义时必须初始化。 

(3)指针中途可以修改指向;引用不可以。 

(4)指针可以为NULL;引用不能为空。 

(5)sizeof(指针)计算的是指针本身的大小;而sizeof(引用)计算的是它引用的对象的大小。 

(6)如果返回的是动态分配的内存或对象,必须使用指针,使用引用会产生内存泄漏。 

(7)指针使用时需要解引用;引用使用时不需要解引用‘*’。

(8)有二级指针;没有二级引用。

说说内联函数和宏函数的区别

答:相同点: (1)二者都是通过将函数调用替换成完整的函数体,相比函数调用的时间、空间开销而言,二者提高了效率。 不同点: (1)宏定义不是函数,而内联函数时函数,因此内联函数可以调试,宏定义不能。 (2)宏定义的代码展开阶段是预处理阶段,而内联函数在编译阶段,因此内联函数有类型安全检查,宏定义没有 (3)内联函数作为类的成员函数时,可以访问类的所有成员(公有、保护、私有),宏定义不能。

在FreeRTOS中,二值信号量和互斥量的区别?

答:互斥型信号量必须是同一个任务申请,同一个任务释放,其他任务释放无效。同一个任务可以递归申请。二进制信号量,一个任务申请成功后,可以由另一个任务释放。

在FreeRTOS中,任务通知的运行机制是怎么样的?

答:任务通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕,可以直接使用。任务通知可以在任务中向指定任务发送通知,也可以在中断中向指定任务发送通知,FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue就是这个通知值。只有在任务中可以等待通知,而不允许在中断中等待通知。如果任务在等待的通知暂时无效,任务会根据用户指定的阻塞超时时间进入阻塞状态,我们可以将等待通知的任务看作是消费者;其它任务和中断可以向等待通知的任务发送通知,发送通知的任务和中断服务函数可以看作是生产者,当其他任务或者中断向这个任务发送任务通知,任务获得通知以后,该任务就会从阻塞态中解除。

什么情况下会栈溢出?如何避免?

答:1.局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出。

2.递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。

3.指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用户输入等等。 解决这类问题的办法有两个, 一是增大栈空间,二是改用动态分配,使用堆(heap)而不是栈(stack)。

深复制和浅复制的区别?

答:浅复制:被复制对象的所有变量都含有与原来对象相同的值,而所有其他对象的引用仍然指向原来的对象。深复制:被复制对象的所有变量都含有与原来对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制的新对象,而不再是原有的那些被引用的对象。

从一个源文件到可执行文件的过程?

答:主要包括4个过程: 1. 预处理;2.编译(产生.s文件,-s);3.汇编(产生.o或者.obj文件,-c);4. 链接

简述一下快速排序的步骤

答:1.从数列中挑出一个元素,称为 "基准";

2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置; 

3.递归地把小于基准值元素的子数列和大于基准值元素的子数列排序;

头文件的两种包含方式的区别

答:< >引用的是编译器的类库路径里面的头文件,#include <> 的查找位置是标准库头文件所在目录;" "引用的是你程序目录的相对路径中的头文件, #include "" 的查找位置是当前源文件所在目录。

CAN通信介绍;CAN通信报文的标识符有几位?

答:CAN 是ISO国际标准化的串行通信协议。CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。通信报文标识符有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。

C语言结构体怎么定义节省内存

答:1.在保证值域足够的情况下,用小字节变量代替大字节变量,如用short替代int 

2.将各成员按其所占字节数从小到大声明,以尽量减少中间的填补空间(字节对齐)。 

3.可以取消字节对齐,#pragma pack(1),当然这会牺牲效率,谨慎采用。

STM32 中断是怎么进入到中断服务程序的

答:在STM32中,为了区分不同的中断,每个设备有自己的中断号。系统有0-255一共256个中断。系统有一张中断向量表,用于存放256个中断的中断服务程序入口地址。每个入口地址对应一段代码,即中断服务程序。

malloc和new的区别

答:1、申请的内存所在位置不同。new操作符从自由存储区上为对象动态分配内存空间,malloc函数从堆上动态分配内存。

 2. 返回类型安全性不同。new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

 3. 内存分配失败时的返回值不同。new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL。malloc分配内存失败时返回NULL。

 4. 是否需要指定内存大小不同。使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。malloc则需要显式地指出所需内存的尺寸

为什么局部变量未赋值时,每次初始化的结果是不确定的?

答:定义局部变量,其实就是在栈中通过移动栈指针来给程序提供一个内存空间和这个局部变量名绑定。因为这段内存空间在栈上,而栈内存是反复使用的,上次用完没清零的,所以说使用栈来实现的局部变量定义时如果不显式初始化,值就是脏的,是不确定的。

linux的锁机制?

答:(1)互斥锁:mutex,保证在任何时刻,都只有一个线程访问该资源,当获取锁操作失败时,线程进入阻塞,等待锁释放。 (2)读写锁:rwlock,分为读锁和写锁,处于读操作时,可以运行多个线程同时读。但写时同一时刻只能有一个线程获得写锁。 (3)自旋锁:spinlock,在任何时刻只能有一个线程访问资源。但获取锁操作失败时,不会进入睡眠,而是原地自旋,直到锁被释放。这样节省了线程从睡眠到被唤醒的时间消耗,提高效率。 (4)条件锁:就是所谓的条件变量,某一个线程因为某个条件未满足时可以使用条件变量使该程序处于阻塞状态。一旦条件满足了,即可唤醒该线程(常和互斥锁配合使用) (5)信号量。

介绍下常用的gdb命令

答:quit:退出gdb,结束调试 list:查看程序源代码 reverse-search:字符串用来从当前行向前查找第一个匹配的字符串 run:程序开始执行 help list/all:查看帮助信息 break:设置断点 break get_sum:以函数名设置断点 break 行号或者函数名 if 条件:以条件表达式设置断点 watch 条件表达式:条件表达式发生改变时程序就会停下来 next:继续执行下一条语句 ,会把函数当作一条语句执行 step:继续执行下一条语句,会跟踪进入函数,一次一条的执行函数内的代码

C++ 什么情况下必须用初始化列表

答:1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。 2.const 成员或引用类型的成员。因为 const 对象或引用类型只能初始化,不能对他们赋值。

linux用户态怎么进入内核态

答:linux从用户态到内核态的切换通过系统调用接口来实现,而系统调用切换时通过软件中断来完成,该中断是程序人员自己开发出的一种正常的异常,那么在Linux下,这个异常具体就是调用int $0x80的汇编指令,这条汇编指令将产生向量为0x80的编程异常。

操作系统条件变量的惊群效应是什么

答:惊群效应原本是:当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。在操作系统中,多进程/多线程等待同一资源时,当某一资源可用时,多个进程/线程会惊醒,竞争资源。这就是操作系统中的惊群。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐