赞
踩
1、select和epoll的区别
select的时间复杂度O(n)。它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。 epoll的时间复杂度O(1)。epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
2、linux的进程状态有哪些?
Linux系统下进程通常存在6种不同的状态,分为:就绪态、运行态、僵尸态、可中断睡眠状态(浅度睡眠)、不可中断睡眠状态(深度睡眠)以及暂停态。
3、死锁的必要条件是什么
产生死锁的必要条件:
(1)互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
(2)请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
(4)环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
4、什么是优先级翻转,如何避免优先级翻转?
优先级翻转指的是一个具有中等优先级的任务比一个更高优先级的任务先执行。优先级翻转的主要原因是对共享资源的访问产生了互斥,因此我们可以采用带有优先级继承机制的互斥量来避免优先级翻转 ,解决优先级翻转的方法:优先级天花板、优先级继承。
优先级天花板 :优先级天花板是当线程申请某资源时,把该线程的优先级提升到可访问这个资源的所有线程中的最高优先级,这个优先级称为该资源的优先级天花板。这种方法简单易行,不必进行复杂的判断,不管线程是否阻塞了高优先级线程的运行, 只要线程访问共享资源都会提升线程的优先级。
优先级继承 :优先级继承是当线程A申请共享资源Source时,如果共享资源Source正在被线程C使用,通过比较线程C与自身的优先级,如发现线程C的优先级小于自身的优先级, 则将线程C的优先级提升到自身的优先级,线程C释放资源Source后,再恢复线程C的原优先级。这种方法只在占有资源的低优先级线程阻塞了高优先级线程时才动态的改变线程的优先级。
5、Linux驱动程序的功能是什么?
设备驱动连接操作系统和硬件。设备驱动程序是一种可以使计算机与设备进行通信的特殊程序,可以说相当于硬件的接口。操作系统只有通过这个接口,才能控制硬件设备的工作。安装在操作系统中的驱动程序可以完成设备的初始化和释放,进行外部数据和操作系统的通信和数据交互,控制硬件的行为,并检查设备可能出现的故障并报错。
6、 Linux驱动程序的分类有哪些?
驱动程序分为三大类,字符设备、块设备、网络设备。字符设备,按字节读取,提供连续的数据流,一般不支持随机读取,块设备,可以访问任意位置数据,读取位置和大小由程序决定,支持随机读取,网络设备是虚拟的,用于不同主机间的数据包传输。网络设备主要负责主机之间的数据交换。与字符设备和块设备完全不同,网络设备主要是面向数据包的接收和发送而设计
上层应用是如何调动驱动的
1、应用层的程序会通过open去打开设备文件,进入内核的虚拟文件系统VFS
2、vfs中有一个结构体节点 struct inode,其中包含了设备的设备号,设备类型,返回的设备的结构体
3、驱动程序中通过对应的设备号和设备类型可以找到该设备的结构体,保存在cdev中,该结构体还有一个很重要的操作函数接口file——operations
4、打开设备文件的时候,会分配一个struct file,将该操作函数接口的地址保存在该结构体中,返回一个fd,该fd对应struct file,通过fd来调操作函数,来操作设备程序。
7、内核程序中申请内存使用什么函数?
内核中使用kmalloc(),kzalloc(), vmalloc(), alloc_page()等函数。
(1)kamlloc申请的内存区域位于物理内存的映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,存在比较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB。
(2)kzalloc() 函数与 kmalloc() 额外附加了 __GFP_ZERO 标志,所以它除了申请内核内存外,还会对申请到的内存内容清零。
(3)vmalloc() 函数则会在虚拟内存空间给出一块连续的内存区,但这片连续的虚拟内存在物理内存中并不一定连续。由于 vmalloc() 没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。需要注意的是vmalloc() 和 vfree() 可以睡眠,因此不能从中断上下文调用。
(4)alloc_page()用于申请连续的物理页,可以通过page_address()把指定的页转化成逻辑地址。
(5)如何区分这几个概念: kmalloc是最常用的内存分配函数,可以原子操作,速度快,缺点就是大小有上限,kzalloc是强制清零版本的kmalloc,而vmalloc只有在需要申请大内存的时候使用,会陷入阻塞
8、内核程序中申请内存和应用程序时申请内存有什么区别?
应用程序的内存申请,例如C/C++可以使用malloc函数,与内核的kmalloc(),kzalloc(), vmalloc(), alloc_page()等函数相比,比较像vmalloc机制,即虚拟地址申请,物理地址不一定连续,区别在于,应用程序malloc不会做分配物理页的动作,交由内核去申请,而vmalloc本身在内核中会执行这个动作。
9、自旋锁和信号量在互斥使用时需要注意什么?在中断服务程序里面的互斥是使用自旋锁还是信号量?
自旋锁会一直自旋等待,不会休眠;而信号量是等待信号来唤醒进程,进程会睡眠。使用时注意希望长等待不吃系统资源时使用信号量,反过来短期的等待使用自旋锁,但会增加系统开销。 中断中不可以使用信号量,因为中断不能睡眠。
10、 请你讲一讲Linux中断的原理和开发方法?
Linux中断处理过程分为两部分:
上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可以放在上半部完成。
下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部去执行,这样中断处理函数就会快进快出。
那些对时间敏感、执行速度快的操作可以放到中断处理函数中,也就是上半部。剩下的所有工作都可以放到下半部去执行,比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。一般来说,不希望被其他中断打断、对时间敏感、和硬件有关的任务建议放在中断上半部。
11、 堆和栈的区别
1.分配与回收机制不同,栈由系统自动分配回收,堆由程序员手动分配与回收。
2.空间大小不同,堆的大小由程序员手动开辟,理论上可开辟整个虚拟内存大小,栈的大小较小,有默认上限。
3.地址增长方向不同,堆地址由低地址往高地址方向增长,栈的地址由高地址向低地址方向增长。
4.存放内容不同:栈存放函数返回地址,局部变量,相关参数,寄存器内容等。堆顶有一个字节的堆的大小,堆中存放的内容由程序员自己填充。
5.效率不同:栈由系统自动分配,空间较小,执行效率较高。堆由程序员自己控制,分配复杂,容易产生碎片,还需程序员手动回收,效率较低。
12、线程与进程的区别
1.(调度)进程是资源调度分配的最小单位,线程是系统调度的最小单位
2.(内存)进程拥有独立的内存单元空间,线程共享一个进程内存
3.(并发)进程实现了系统的并发,线程实现了进程的并发
4.(销毁)进程创建销毁需要单独操作一个地址空间,系统开销大。线程共享一个内存单元,系统开销较小
5.(子任务)进程是程序的动态执行过程,线程是进程的子任务
6.(一对多)一个进程可以拥有多个线程,一个线程只能属于一个进程
13、volatile关键字作用?
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。一般说来,volatile用在如下的几个地方:
1) 中断服务程序中修改的供其它程序检测的变量需要加 volatile;
2) 多任务环境下各任务间共享的标志应该加 volatile;
3) 存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义;
14、 宏函数和内联函数的区别
1.宏是由预处理器对宏进行替代;而内联函数是通过编译器控制来实现的
2.宏定义只是简单的文本替换,内联函数直接被嵌入到目标代码中去
3.宏定义不是真正的函数,没有参数类型检查,不安全;而内联函数是真正的函数,有类型检查,更为安全
4.宏在定义时要注意参数,一般用括号括起来,否则容易出现二义性,而内联函数不会出现二义性
15、如果两个进程,都要去调用一个设备驱动,设备驱动里面定义的变量是在内核态吗?如果进程A访问并改变了这个变量,那么进程B再访问,是不是读到改变之后的值?
驱动程序中的变量是在内核态中,因为操作系统只有内核态可以访问到硬件设备,驱动程序从内核态像用户态输出API以便调用和间接访问硬件设备。 进程B访问的是改变后的值,只有在没有考虑并发编程的驱动程序中才可能发生数据的不一致性。内核驱动程序开发必须考虑并发的问题,因为驱动程序将会被一个或者多个进程访问,对于共享的变量,需要加入互斥锁、自旋锁、信号量或者原子操作等同步技术保证数据的一致性。
16、两个进程的内核空间是共享的吗
用户空间中,每个进程的用户空间是互相独立的,互不相干。 内核空间中,绝大部分是共享的,并不是完全共享,因为内核空间中,不同进程的内核栈之间是不共享的。之所以使用进程的内核栈而非“用户栈”,是避免用户态下进程被抢占改变引发内核崩溃,因此每个进程在内核中有一个独立的内核栈。
17、 三次握手为什么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。其他几次握手挥手也是同样道理。
18、 驱动程序里面,如果有一个buffer,在不同场景下需要定义不同大小,怎么办?
(1)可以预先定义一个结构体,包含长度变量和一个指针,长度标志由ioctl函数从应用程序输入,并动态申请和释放内存。
(2)可以在模块加载时预先申请不同大小的缓存。
(3)动态检测程序所需要的缓存数量,采用多级缓存的方式,根据所需传递数据的大小动态申请分布式缓存。
19、传引用和传指针区别
指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。
20、动态库和静态库的区别,后缀格式,以及函数的相对地址区别
1、 命名方式不同: 静态库libxxx.a:库名前加”lib”,后缀用”.a”,“xxx”为静态库名。
动态库libxxx.so:库名前加”lib”,后缀变为“.so”。
2、链接时间不同: 静态库的代码是在编译过程中被载入程序中。
动态库的代码是当程序运行到相关函数才调用动态库的相应函数
3、链接方式不同: 静态库链接是将整个函数库的所有数据在编译时都整合进了目标代码。 动态库的链接是程序执行到哪个函数链接哪个函数的库。
(用哪个链接哪个)。
优缺点:
静态库: 优点是,在编译后的执行程序不再需要外部的函数库支持,运行速度相对快些; 缺点是,如果所使用的静态库发生更新改变,你的程序必须重新编译。
动态库 : 优点是,动态库的改变并不影响你的程序,所以动态函数库升级比较方便;
缺点是,因为函数库并没有整合进程序,所以程序的运行环境必须提供相应的库。
21、常见的操作系统进程调度策略有哪些?
常见的调度策略有6种,先来先服务调度(FCFS)。短进程优先调度(SPF)。高响应比优先调度(HRN)。时间片轮转调度(RR)。多级反馈队列调度(RRWMF)。最高优先权优先调度(FPF)。
22、逻辑地址、线性地址、物理地址、总线地址、虚拟地址的区别?
逻辑地址指的是与内存段相关的偏移地址部分。线性地址:线性地址 = 逻辑地址 + 基地址。 如果启用了分页机制,那么线性地址使用页表项变换后就是物理地址。如果没有启用分页机制,那么线性地址就是物理地址。总线地址其实就是物理地址。虚拟地址是通过MMU内存管理虚拟映射出来的地址。
23、 用户空间与内核空间有哪些通信方式?
1、系统调用,提供特定的用户空间与内核空间的信息传递。
2、信号,内核空间出现一些异常时候会发送信号给进程,如SIGSEGV、SIGPIPE等。
3、/proc,proc可以读取内核空间的配置信息和运行状态并且设置部分属性的值。
4、文件,可以通过指定文件的读写操作来实现通信。
5、netlink,类似socket通信方式,可以读写大量的数据,实现稍微复杂。
6、ioctl,可以实现数据量比较少时候的通信
24、 怎样预防死锁?
(1)打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
(2)打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
(3)打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
(4)打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
25、 硬链接与软链接的区别?
1硬链接与原文件公用inode号,是同一个文件;而软链接与原文件不同inode号,是两个不同的文件;
2文件属性上软链接明确写出了链接文件,而硬链接没有写出来;
3软链接的链接数目不会增加,硬链接每增加一个,链接数目加一;
4硬链接文件大小跟原文件一样,软连接不一定。
26、 Linux系统的挂起、待机、休眠?
挂起:CPU、内存工作,而硬盘、显示器等外部设备停止工作。 待机:只对内存供电,CPU、外设停止工作。 休眠:CPU、内存停止工作,内存中的数据保存在硬盘中,外部设备也停止工作。
27、 TCP、UDP的区别?
TCP面向连接(三次握手、四次挥手),UDP直接发送,不管你收不收得到; TCP是面向字节流,UDP基于数据报; TCP提供可靠服务(提供序列号和确认号(确认号为发送的序列号加1,即下次发送的序列号)来判断顺序是否正确,通过TCP校验和来判断数据是否正确); UDP直接发,其他不管; TCP程序结构复杂,占用资源多,UDP程序结构简单,占用资源少; TCP有拥塞控制(通过窗口来控制),UDP直接发,不管,没有拥塞控制; TCP只支持一对一, UDP玩的花,不管是一对一,一对多,多对一还是多对多都可以。 所以TCP的速度慢,而UDP的速度快。 UDP有检验和,所以当接收端收到一个错误的UDP报文,会直接扔了。(传输视频就是使用UDP,收到错误,扔了就是一帧,对整个视频没多大影响)
28、 简述TCP三次握手的过程。
29、 TCP为什么需要三次握手,第三次握手去掉行不行?
进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。第三次握手不能去掉,第三次握手能防止发生死锁,因为若为两次握手且服务器发出第二次握手而客户端没有收到,服务器开始传输数据报后客户端便不会理会,导致服务器以为丢包而源源不断地发送数据报,造成死锁。
30、 简述TCP四次挥手的过程。
31、 为什么TCP建立连接是三次握手,而关闭连接却是四次挥手?
建立连接的时候,服务器在LISTEN状态下,不需要等待,可以立即建立连接,把ACK和SYN放在一个报文里发送给客户端,因此只需要三次握手。 而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接受数据,而自己未必将所有数据都发送完了,所以服务器可以立即关闭,也可以发送一些数据后再关闭,所以服务器的确认报文(ACK=1)和连接释放报文(FIN=1,ACK=1)一般分开发送,因此形成四次握手。
32、DMA有什么用
DMA是在专门的硬件( DMA)控制下,实现高速外设和主存储器之间自动成批交换数据
尽量减少CPU干预的输入/输出操作方式。主要作用就是减少CPU的负担。
33、进程间通信方法
进程间通信方式有:
管道 ,包括有名管道和无名管道。
信号量:
共享内存:
消息队列:
本地套接字 :
信号:
34、 程序中的内存分配方法
内存为程序分配空间有四种分配方式:
1、连续分配方式 :
2、基本分页存储管理方式 :
3、基本分段存储管理方式:
4、段页式存储管理方式:
35、内核申请内存vmalloc和kmalloc的区别是什么
1,vmalloc分配的一般为高端内存,只有当内存不够的时候才分配低端内存;kmallco从低端内存分配。
2,vmalloc分配的物理地址一般不连续,而kmalloc分配的地址连续,两者分配的虚拟地址都是连续的;
3,vmalloc分配的一般为大块内存,而kmaooc一般分配的为小块内存,(一般不超过128k);
36、 简单描述linux设备驱动中的总线,设备和驱动的关系。
总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每 注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。一个现实的Linux设备和驱动通常都需要挂接在一种总线上。设备与驱动的关联通过总线的match()方法进行匹配,驱动挂载总线时与所有设备进行匹配,设备挂载总线时与所有的驱动进行匹配,所以驱动和设备的挂载无先后之分。匹配成功后会通过调用驱动的probo()方法来初始化设备。
37、 简述一下什么是红黑树
红黑树是一种近似平衡的二叉查找树,它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一倍。具体来说,红黑树是满足如下条件的二叉查找树
1.每个节点要么是红色,要么是黑色。
2.根节点必须是黑色
3.红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色)。
4.对于每个节点,从该点至null(树尾端)的任何路径,都含有相同个数的黑色节点。
5.最长的路径长度不会超过任意路径的两倍。
38、指针和引用的区别
(1)指针是实体,占用内存空间;引用是别名,与变量共享内存空间。
(2)指针不用初始化或初始化为NULL;引用定义时必须初始化。
(3)指针中途可以修改指向;引用不可以。
(4)指针可以为NULL;引用不能为空。
(5)sizeof(指针)计算的是指针本身的大小;而sizeof(引用)计算的是它引用的对象的大小。
(6)如果返回的是动态分配的内存或对象,必须使用指针,使用引用会产生内存泄漏。
(7)指针使用时需要解引用;引用使用时不需要解引用‘*’。
(8)有二级指针;没有二级引用。
39、什么情况下会栈溢出?如何避免?
1.局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出。
2.递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。
3.指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用户输入等等。
解决这类问题的办法有两个, 一是增大栈空间,二是改用动态分配,使用堆(heap)而不是栈(stack)。
40、深复制和浅复制的区别?
41、 头文件的两种包含方式的区别
< >引用的是编译器的类库路径里面的头文件,#include <> 的查找位置是标准库头文件所在目录;
" "引用的是你程序目录的相对路径中的头文件, #include "" 的查找位置是当前源文件所在目录。
42、STM32 中断是怎么进入到中断服务程序的
在STM32中,为了区分不同的中断,每个设备有自己的中断号。系统有0-255一共256个中断。系统有一张中断向量表,用于存放256个中断的中断服务程序入口地址。每个入口地址对应一段代码,即中断服务程序。
43、 malloc和new的区别
1、申请的内存所在位置不同。new操作符从自由存储区上为对象动态分配内存空间,malloc函数从堆上动态分配内存。
2. 返回类型安全性不同。new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
3. 内存分配失败时的返回值不同。new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL。malloc分配内存失败时返回NULL。
4. 是否需要指定内存大小不同。使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。malloc则需要显式地指出所需内存的尺寸。
44、linux的锁机制?
(1)互斥锁:mutex,保证在任何时刻,都只有一个线程访问该资源,当获取锁操作失败时,线程进入阻塞,等待锁释放。
(2)读写锁:rwlock,分为读锁和写锁,处于读操作时,可以运行多个线程同时读。但写时同一时刻只能有一个线程获得写锁。
(3)自旋锁:spinlock,在任何时刻只能有一个线程访问资源。但获取锁操作失败时,不会进入睡眠,而是原地自旋,直到锁被释放。这样节省了线程从睡眠到被唤醒的时间消耗,提高效率。
(4)条件锁:就是所谓的条件变量,某一个线程因为某个条件未满足时可以使用条件变量使该程序处于阻塞状态。一旦条件满足了,即可唤醒该线程(常和互斥锁配合使用)
(5)信号量。
45、linux用户态怎么进入内核态
系统调用:
软件中断:
硬件中断:
46、 操作系统条件变量的惊群效应是什么
惊群效应原本是:当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。在操作系统中,多进程/多线程等待同一资源时,当某一资源可用时,多个进程/线程会惊醒,竞争资源。这就是操作系统中的惊群。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。