文章目录
- Linux驱动开发
- 1、Linux内核组成
- 2、用户空间与内核的通讯方式有哪些?
- 3、系统调用read/write流程
- 4、内核态用户态的区别
- 5、bootloader内核 根文件的关系
- 6、BootLoader的作用
- 7、BootLoader两个启动阶段
- 1、汇编实现,完成依赖于CPU体系架构的设置,调用阶段二的代码。
- 2、C语言实现
- 8、Linux内核同步方式
- 9、为什么自旋锁限制睡眠,信号量不限制睡眠
- 10、Linux检查内存状态命令
- 11、大小端区别以及各自优点
- 12、程序运行开始到结束的全部过程
- 13、硬链接与软链接
- 14、中断的产生,以及执行的流程
- 15、挂起、休眠、 关机相关指令
- 16、虚拟内存
- 17、rom ram 概念浅析
- 18、malloc、vmalloc、kmalloc的区别
- 18、在1G内存的计算机中能否malloc(1.2G)?为什么?(2021浙江大华二面问题)
- 19、ARM寄存器
- 20、Linux下基本目录结构(记住几个重要的,bin、sbin、dev、etc、proc、opt、sys、usr)
- 21、说说probe函数作用
- 22、总线、设备与驱动的三者关系(Linux驱动核心概念,必须理解)
- 23、==Linux启动流程==
- 24、中断中能不能加msleep,mdelay呢?为什么?这两个有什么区别?
- Linux内核data段和bss段的区别
- 中断有哪些类型
- “设备驱动可以分为哪些类型?”、
- 操作系统
- 1、申请两个一字节内存会是连续的吗 ?
- 2、堆栈申请内存的区别
- 堆栈溢出一般是由什么原因导致的?
- 3、链接过程分为动态链接和静态链接,分别用在哪些场景下?
- 4、 何为内存泄露、内存越界?若运行过程中出现内存泄漏,有哪些定位手段?
- 5、创建和销毁消息队列
- 6、死锁四个必要条件以及解决方案
- 7、什么是进程、线程
- 7、进程与线程的区别
- 7、多进程和多线程的区别
- 7、多进程、线程优缺点
- 8、什么时候用线程,什么时候用进程
- 8、多进程、多线程同步(通讯)的方法
- 进程间通讯:
- 线程间通讯:
- 互斥锁与信号量的区别?
- 8、各通信方式的比较和优缺点
- 9、进程的状态转换
- 10、父子进程的关系与区别
- 11、进程上下文与中断上下文
- 12、讲讲用户态和内核态
- 13、一个进程最多能创建多少个线程
- 14、线程同步与互斥
- 15、并发、同步、异步、互斥、阻塞、非阻塞的理解
- 16、孤儿进程,僵尸进程,守护进程
- 17、==扇区、 块、 页 、簇的概念==
- 18、简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?
- 19、锁机制
- 20、实时操作系统与分时操作系统的区别
- 21、为什么要内存对齐
- 22、Linux内存管理
- 23、共享内存怎么实现同步
- 24、创建线程的函数、底层实现,线程的本质是什么
- 25、进程线程的状态转换图
- “操作系统有哪些调度策略?”。
- 计算机网络
- 1、TCP如何保证传输可靠
- 2、TCP/UDP区别
- 3、TCP/UDP优缺点
- 4、HTTPS与HTTP的区别
- 5、URI与URL
- 6、三次握手
- 7、只有二次握手的问题
- 8、四次挥手
- 9、为什么要等2msl
- 10、为什么握手是三次,挥手是四次
- 11、==socket IO多路复用==
- select函数实现:
- epoll函数实现
- 12、程序崩溃定位、程序死机解决。
Linux驱动开发
1、Linux内核组成
Linux内核主要由进程调度(SCHED)、进程间通信(IPC)、内存管理(MMU)、虚拟文件系统(VFS)、网络接口(NET)和5个子系统组成
1、进程调度
程调度控制系统中的多个进程对CPU的访问,使得多个进程能在CPU中“微观串行,宏观并行”地执行。
2、进程间通讯
Linux支持进程间的多种通信机制,包含信号量、共享内存、管道等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递。
3、内存管理
内存管理的主要作用是控制多个进程安全地共享主内存区域。当CPU提供内存管理单元(MMU)时,Linux内存管理完成为每个进程进行虚拟内存到物理内存的转换。
4、网络接口
网络接口提供了对各种网络标准的存取和各种网络硬件的支持。在Linux中网络接口可分为网络协议和网络驱动程序,网络协议部分负责实现每一种可能的网络传输协议,网络设备驱动程序负责与硬件设备通信
5、虚拟文件系统
Linux虚拟文件系统(VFS)隐藏各种了硬件的具体细节,为所有的设备提供了统一的接口。而且,它独立于各个具体的文件系统,是对各种文件系统的一个抽象,它使用超级块super block存放文件系统相关信息,使用索引节点inode存放文件的物理信息,使用目录项dentry存放文件的逻辑信息。
2、用户空间与内核的通讯方式有哪些?
1、系统调用:提供特定的内核空间与系统空间的信息传递
2、信号:内核空间出现异常会发送信号给进程
3、/proc 可以读取内核的信息,并修改部分信息
4、 文件:可以通过指定文件的读写操作来实现通信,流程不够实时,需要循环检测来实践
3、系统调用read/write流程
用户空间read()–内核空间sys_read()–scullfop.read–scull_read()
4、内核态用户态的区别
当内核执行用户自己的代码时称为用户态此时处理器在特权级最低的用户代码中运行,使用到内核程序的时候在内核态运行,特权级最高ring0,一般中断、异常、系统调用,都能将cpu从用户态切换到内核态。
5、bootloader内核 根文件的关系
6、BootLoader的作用
启动内核前的一段程序,可以初始化硬件的相关设备,将设备的软硬件环境带到一种合适的状态
7、BootLoader两个启动阶段
1、汇编实现,完成依赖于CPU体系架构的设置,调用阶段二的代码。
硬件设备初始化: 配置时钟相关参数,比如分频系数等等(内核时钟,总线时钟,IO接口时钟)
关闭看门狗:看门狗用于防止程序跑飞,但是在 uboot启动阶段,还没有加载 操作系统,
关闭MMU:MMU是用于虚拟地址向物理地址进行映射的一个结构。在 uboot阶段操作的就直接是 物理地址,所以不需要转换。
关闭中断:uboot引导linux起到的过程中本身就是一个完成的过程,不需要中断机制
为第二段代码准备RAM空间
设置pc指针指向start_armboot函数 跳转到第二段代码的C语言入口
2、C语言实现
初始化硬件设备:最少需要一个串口与用户进行交互。
将内核和根文件系统从FLASH中读到RAM中
为内核设置启动参数:重要参数bootcmd以及booargs
调用内核
8、Linux内核同步方式
1、原子操作
2、互斥锁 资源被占用时,程序只能进入休眠状态,
3、信号量
4、自旋锁 自旋锁保护的临界区不能进入休眠,所以效率高。但是一直占用CPU资源
9、为什么自旋锁限制睡眠,信号量不限制睡眠
自旋锁禁止处理器抢占,信号量不限制处理抢占,一旦自旋锁支持睡眠,cpu无法运行。
10、Linux检查内存状态命令
top可以实时查看内存的使用情况。
free-m可以查看内存使用情况
cat /proc/meminfo 查看更详细情况
11、大小端区别以及各自优点
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
小端字节序:低位字节在前,高位字节在后。
网络中 ,要按照网络字节序,一般为大端。
计算机内部是小端字节序
拓展:为什么不统一字节序
- 计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读第一个字节,再读第二个字节。所以,计算机的内部处理都是小端字节序。
- 但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
12、程序运行开始到结束的全部过程
预处理:
-
预编译:头文件包含,替换宏定义,识别条件编译
-
编译: .c 变成 .s 生成汇编文件
-
汇编 .s 变成 .o 汇编成二进制文件
-
链接 .o 变成 可执行文件
13、硬链接与软链接
硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”
的功能。
软链接又称之为符号连接
(Symbolic Link)。软链接文件类似于Windows的快捷方式。它实际上是一个特殊的文件。
区别:A是B的硬链接代表inode号相同,删除了一个对另外一个没有影响,软连接inode不同,a指向的是另一个数据块,这个数据块代表着B的位置,把B删了之后,A的数据块里面的内容将是无效链接。
14、中断的产生,以及执行的流程
中断是一种使CPU停止正在执行的任务,转而去处理特殊事件的操作,引起中断的事件被称为中断源。
中断的处理过程一般由三部分组成:
1、准备部分,保护现场
,保存之前程序运行的上下文
2、处理部分,真正执行
中断处理函数
3、结束部分,关闭中断,恢复现场
,打开中断
15、挂起、休眠、 关机相关指令
1、挂起:pm-suspend
2、休眠:pm-hibernate
3、重启 reboot
4、关机 shutdown -h now
16、虚拟内存
当运行数据超出物理内存容纳限度的时候,部分数据就会自行“溢出”,这时系统就会将硬盘上的部分空间模拟成内存——虚拟内存,并将暂时不运行的程序或不使用的数据存放到这部分空间之中,等待需要的时候方便及时调用。
17、rom ram 概念浅析
RAM:(Random Access Memory,RAM)随机存储器,也叫内存,支持随机读写,电源关闭时RAM不能保存数据【access vt. 访问,存取(计算机文件); 到达; 进入;】
ROM:(Read Only Memory,ROM)只读存储器:只能读出,断电后依然能够保存数据,但不等于硬盘
18、malloc、vmalloc、kmalloc的区别
1、kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存
2、kmalloc保证分配的内存在物理上是连续的,内存只有在要被DMA访问的时候才需要物理上连续,malloc和vmalloc保证的是在虚拟地址空间上的连续
3、kmalloc能分配的大小有限(可分配的内存大小范围在32~131027(128k)字节),vmalloc和malloc能分配的大小相对较大
4、vmalloc比kmalloc要慢。 尽管在某些情况下才需要物理上连续的内存块,但是很多内核代码都用kmalloc来获得内存,而不是vmalloc。这主要是出于性能的考虑。vmalloc函数为了把物理内存上不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。糟糕的是,通过vmalloc获得的页必须一个个地进行映射,因为它们物理上是不连续的,这就会导致比直接内存映射大得多的TLB抖动,vmalloc仅在不得已时才会用–典型的就是为了获得大块内存时。
5、vmalloc申请的虚拟空间连续,物理空间不连续。
18、在1G内存的计算机中能否malloc(1.2G)?为什么?(2021浙江大华二面问题)
是有可能申请1.2G的内存的。回答这个问题前需要知道malloc的作用和原理,应用程序通过malloc函数可以向程序的虚拟空间申请一块虚拟地址空间,与物理内存没有直接关系,得到的是在虚拟地址空间中的地址,之后程序运行所提供的物理内存是由操作系统完成的。
19、ARM寄存器
37个arm寄存器
未分组寄存器R0~R7
分组寄存器R8~R14
寄存器R13在ARM指令中常用作堆栈指针SP
R14称为子程序链接寄存器LR(Link Register)
程序计数器PC(R15)
R16用作CPSR(CurrentProgram Status Register,当前程序状态寄存器)
20、Linux下基本目录结构(记住几个重要的,bin、sbin、dev、etc、proc、opt、sys、usr)
bin …基本命令的可执行文件
boot …内核映像已经启动时需要用到的一些文件
dev …设备文件
etc …系统配置文件,包括启动文件
home …用户目录
lib …基本库,例如C库和内核模块
lost+found …在文件系统修复时恢复的文件
mnt …临时文件系统的挂载点
nfsroot …nfs文件夹,一般不使用
opt …添加的软件包
proc …内核以及进程信息的虚拟文件系统
root …root用户目录
sbin …用于系统管理的可执行程序
share …共享文件目录
sys …系统设备和文件层次结构,向用户提供详细的内核数据信息
tmp …临时文件
usr …该目录的二级目录包含许多对用户很有用的应用程序和文档
var …存放系统日志或一些服务程序的临时文件
21、说说probe函数作用
probe函数是总线型驱动中涉及到的函数,是当驱动与设备匹配之后执行的函数,主要完成设备初始化,其实是普通字符驱动中init函数所完成的功能,包括注册设备号,初始化cdev,添加cdev,创建类等等。
与probe相对的就是remove函数,其实作用是解除映射以及销毁设备号和cdev。
22、总线、设备与驱动的三者关系(Linux驱动核心概念,必须理解)
总述:总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。在最底层,Linux系统中的每一个设备都用device结构的一个实例来表示。而驱动则是使总线上的设备能够完成它应该完成的功能。
设备
:设备代表真实的、具体的物理器件,在软件上用器件的独特的参数属性来代表该器件。
驱动
:代表着操作设备的方式和流程。对于应用来说,应用程序open打开设备后,接着就read访问这个设备,驱动就是如何实现这个访问的具体的过程。驱动主要包括两部分:
- 第一是通过对SOC的控制寄存器进行编程,按总线要求输出时序和命令,成功地与外围设备进行交互;
- 第二是对第一步中得到的数据进行处理,并向应用层提供特定格式的数据。
总线
:代表着同类设备需要共同遵守的工作时序,不同的总线对于物理电平的要求是不一样的,对于每个比特的电平维持宽度也是不一样,而总线上传递的命令也会有自己的格式约束。在软件层面主要是用来管理设备与驱动。
设备与驱动想让系统知道自己首先要注册自己。另外总线还相当于红娘,给设备和驱动牵线,最简单的匹配方式就是直接对名字匹配。这个匹配方式一般是match函数
总线在匹配设备和驱动之后驱动要考虑一个这样的问题,设备对应的软件数据结构代表着静态的信息,真实的物理设备此时是否正常还不一定,因此驱动需要探测这个设备是否正常。我们称这个行为为probe,至于如何探测,那是驱动才知道干的事情,总线只管吩咐得了。所以我们可以猜测在总线的管理代码中会有这样的逻辑:
if(match(device, driver) == OK) {
driver->probe(); }
23、Linux启动流程
1、开机自检:主要对各种硬件进行检测,包括CPU 内存、主板、硬盘等等。
2、uboot 上下两段,引导内核
3、就开始运行第一个程序 /sbin/init
4、根据运行级别运行进程
5、启动 /etc/init.d 中的开机脚本
24、中断中能不能加msleep,mdelay呢?为什么?这两个有什么区别?
中断理论上是不能有延时操作的,但是为了特殊情况操作,又把中断分为顶半部中断和底半部中断。顶半部不能延时,底半部可以处理延时。一般用mdelay吧,因为我平常没看到sleep。(可能是推迟但不停止程序?)
Linux内核data段和bss段的区别
从进程的角度【进程就是程序运行起来,和c语言内存分配一样】,Linux内核是采用虚拟地址空间的,如下两张图所示,分别为32位、64位系统下进程地址空间的大概布局。
本文主要分析的是进程的用户空间地址中的数据分配,也就是上图中低地址中用户空间部分,下图为用户空间中内存通用分布图如下:
bss段、data段分别存放什么样的数据?两者分开的好处是什么?
先整体看:bss段与data段首先说明:data段和bss段都属于数据段,在编译时分配。
1、bss段(Block Started Symbol,意为“以符号开始的块”),只是简单维护地址空间中开始和结束的地址,在实际运行对内存区域有效地清零即可。bss段存放全局未初始化/初始化为0、静态未初始化/初始化为0的变量,在磁盘上并不占用相应的磁盘空间。
2、data段,存放的是全局初始化(初始化非0)、静态初始化(初始化非0)的变量,在磁盘中占用相应的磁盘空间,这些变量在程序开始之前具有具体值,是可执行文件的一部分,当程序执行运行时,将可执行文件加载到内存中,读取相应的变量值。
3、bss段维护的是0值的数据变量,不需要占据相应的磁盘空间,data段存放的是具体有效非0值,需要占据相应的磁盘空间,以供程序运行时加载读取相应的值。两者分开维护管理,有效地降低可执行文件占据磁盘的容量,否则初始化化数据项越多,可执行文件越大,运行时加载到内存所需要的时间消耗越大。
中断有哪些类型
“设备驱动可以分为哪些类型?”、
操作系统
1、申请两个一字节内存会是连续的吗 ?
堆分配的空间在逻辑地址上是连续的,但在物理地址上是不连续的
2、堆栈申请内存的区别
- 堆是由程序员申请的,需要主动回收。栈是系统分配的,自己回收
- 申请大小限制:堆大一些,栈小,预先定好的,大概2M左右
- 内存中的栈区处于相对较高的地址以地址的增长方向为上的话,栈地址是向下增长的。而堆区相对较低的地址以地址的增长方向为上的话,堆地址是向上增长
- 堆的申请效率低一些,但是使用方便,栈的申请效率高。
堆栈溢出一般是由什么原因导致的?
- 1.没有回收垃圾资源
- 2.层次太深的递归调用
3、链接过程分为动态链接和静态链接,分别用在哪些场景下?
静态链接:链接器在链接阶段将各种库文件和相关文件集成到可执行文件中,在windows下静态链接库以.lib结尾,linux下以.a结尾
静态链接的优点:
1)装载速度很快,运行速度比动态链接快;
2)只需要开发人员在开发机上有完整的.lib文件,不需要在用户机器上有完整的.lib文件,自完备
静态链接的缺点:
1)可执行文件很大,并且相同代码很多,资源浪费
动态链接:动态链接是把链接过程在运行时进行,动态链接在可执行文件装载或运行的时候,由操作系统的装载程序加载库文件,windows下以.dll结尾,也有.lib的,但是这个是叫做导入库,和静态链接的不一样,linux下以.so结尾
动态链接的优点:
1)可执行文件很小;
2)适合大规模软件开发,开发过程耦合度小、独立,便于不同开发人员和开发组织开发;
3)不同编程语言按照约定可以使用同一套.dll库;
4)dll文件与exe文件独立,如果输出接口相同,更换dll文件不会对exe文件产生影响,可拓展性和可维护性好
动态链接的缺点:
1)速度没有静态链接快;
2)不具有自完备,如果用户机器中没有.dll文件,程序将无法运行并且报错
4、 何为内存泄露、内存越界?若运行过程中出现内存泄漏,有哪些定位手段?
内存溢出:程序在申请内存时,系统没有足够的内存空间供其使用。
内存泄露:向系统申请内存使用完后没有归还,导致有效内存被占用。内存泄露最终会导致内存溢出。例如:new一块内存使用,使用完后没有delete。或者malloc申请一块内存并用指针指向它,但修改了指针指向,此时会有内存泄漏
内存越界:向系统申请一块内存后,使用时超出了内存申请范围。例如:通过下标取数组元素时,下标过大导致越界。
5、创建和销毁消息队列
创建 msgget(key_t key, int flag)
控制 msgctl(int msgqid, int cmd, struct msqid_ds *buf)(删除)
添加 msgsnd(int msgqid, const void *msgp, size_t size, int flag)
接收 msgrcv(int msgqid, void *msgp, size_t size, long msgtype, int flag)
6、死锁四个必要条件以及解决方案
1、互相排斥
2、不可剥夺
3、循环等待
4、保持资源
解决方案:
(1)加锁顺序(线程按照一定的顺序加锁)
(2)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
(3)死锁检测
7、什么是进程、线程
- 进程是资源(CPU、内存等)分配的基本单位,
- 线程是CPU调度和分配的基本单位(程序执行的最小单位)。
- 同一时间,如果CPU是单核,只有一个进程在执行,所谓的并发执行,也是顺序执行,只不过由于切换速度太快,你以为这些进程在同步执行而已。
- 多核CPU可以同一时间点有多个进程在执行。
7、进程与线程的区别
-
进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。进程是拥有资源的基本单位,线程是调度和分配的基本单位线程和进程都可以并发执行。
-
线程:线程是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。
-
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
-
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
-
包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
-
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
-
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
-
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
7、多进程和多线程的区别
多进程:操作系统中同时运行的多个程序
多线程:在同一个进程中同时运行的多个任务
举个例子,多线程下载软件,可以同时运行多个线程,但是通过程序运行的结果发现,每一次结果都不一致。 因为多线程存在一个特性:随机性。造成的原因:CPU在瞬间不断切换去处理各个线程而导致的,可以理解成多个线程在抢CPU资源。
多线程并不能提高运行速度,但可以提高运行效率,让CPU的使用率更高。但是如果多线程有安全问题或出现频繁的上下文切换时,运算速度可能反而更低。
7、多进程、线程优缺点
多进程优点:
• 每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;
• 通过增加CPU,就可以容易扩充性能;
• 可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;
• 每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大
多进程缺点:
• 逻辑控制复杂,需要和主程序交互;
• 需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算
• 多进程调度开销比多线程大;
多线程优点:
• 无需跨进程边界,适合各线程间大量数据的传送;
• 程序逻辑和控制方式简单;
• 所有线程可以直接共享同一个进程的内存和变量等;
• 线程方式消耗的总资源比进程方式好;
多线程缺点:
• 每个线程与主程序共用地址空间,受限于2GB地址空间;
• 线程之间的同步和加锁控制比较麻烦;
• 一个线程的崩溃可能影响到整个程序的稳定性;
• 到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;
• 线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU。
8、什么时候用线程,什么时候用进程
从开销、数据量和稳定性分析
1)创建和销毁较频繁使用线程,因为创建进程花销大。
2)需要大量数据传送使用线程,因为多线程切换速度快,不需要跨越进程边界。
3)安全稳定选进程;快速频繁选线程;
8、多进程、多线程同步(通讯)的方法
进程间通讯:
-
管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
-
有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
-
高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。
-
信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-
消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
-
信号 ( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
-
共享内存( sharedmemory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
-
套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
线程间通讯:
- 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
- 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
- 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
- 信号量(有时被称为信号灯)机制(Semaphore):包括无名线程信号量和命名线程信号量
- 信号机制(Signal): 类似进程间的信号处理
互斥锁与信号量的区别?
- 互斥锁用于线程的互斥,信号量用于线程的同步。这是互斥锁和信号量的根本区别,也就是互斥和同步之间的区别。同时互斥锁的作用域仅仅在于线程,信号量可以作用于线程和进程。
8、各通信方式的比较和优缺点
-
管道:速度慢,容量有限,只有父子进程或兄弟进程能通讯,是半双工的通信,数据只能单向流动
-
FIFO:任何进程间都能通讯,但速度慢
-
消息队列:
- 容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
- 由系统调用函数来实现消息发送和接收之间的同步,从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用方便。
-
共享内存:能够很容易控制容量,速度快,但是要保持同步,比如一个进程在写的时候,另一个进程要注意读的问题。
-
信号:不能传递复杂消息,用于通知接收进程某个事件已经发生。主要作为进程间以及同一进程不同线程之间的同步手段。通过给进程发送相应信号,该进程接收到此信号后运行信号处理函数或者作出默认或忽略回应
-
套接字:可用于不同机器间的进程通信
比如讲管道的缺点是效率低,然后说消息队列解决了这个问题,再说到消息队列的通信不及时的缺点引出共享内存,然后再讲到信号量来解决多个进程修改共享内存的冲突问题
9、进程的状态转换
1、创建
2、就绪
3、运行
4、阻塞
5、终止
10、父子进程的关系与区别
父进程调用fork()以后,克隆出一个子进程,子进程和父进程拥有相同内容的代码段、数据段和用户堆栈。父进程和子进程谁先执行不一定,看CPU。所以我们一般我们会设置父进程等待子进程执行完毕。
父进程会继承子进程的一些信息,包括:各种ID、根目录、环境、以及共享资源。
区别在于:fork返回值,进程ID,每个进程都有自己独立的资源空间。
11、进程上下文与中断上下文
1、进程上文:指进程从用户态切到内核态需要保存的用户态在CPU寄存器中的值,以便再执行的时候能恢复切换时的状态。
2、进程下文:切换到内核态中执行的程序,进程运行在内核态中的部分。
3、中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间,这个过程中,中断也会需要硬件传入的参数或者变量,所以上文可以看做是,保护这些参数和变量
4、中断下文:执行在内核空间的中断服务函数
总而言之:保护用户状态的是上文,内核态执行的程序是下文
12、讲讲用户态和内核态
用户态一般是运行在低特权级别,大部分用户直接面对的程序都是运行在用户态;反之,当程序运行在0级特权级上时,就可以称之为运行在内核态。
运行在用户态下的程序不能直接访问操作系统内核数据结构和程序
当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态。
切换到内核态的三种方法:
1、系统调用:用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。
2、异常:当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
3、中断:当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。
13、一个进程最多能创建多少个线程
一个进程能使用的空间约是2G
,一个线程栈空间大概是1M
,所以理论上可以创建2048个线程
。
14、线程同步与互斥
1、同步:指的是线程
之间所有的一种制约关系,一个线程的执行依赖于另一个线程的消息,没有得到另一个线程的消息时需要等到,直到另一个线程发送。
2、互斥:一般指使用进程
的共享资源时,同一个资源只能被一个线程访问,指单个线程访问的排他性。
15、并发、同步、异步、互斥、阻塞、非阻塞的理解
1、并发:指同一时间有多个线程在同一个处理器上运行,两种并发关系分别是同步与互斥
.
2、互斥:进程之间排斥使用临界资源的现象
3、同步:线程之间相互依赖,前一个的输入作为后一个的输出。
4、异步:与同步相对,任务彼此独立,在等待某事件的过程中,继续做自己的事情,不需要等待这一事情完成后继续做自己的事。线程是实现异步的一个方式,主线程不需要等待任务完成。
5、阻塞:指调用函数结果返回前,当前线程会被挂起
,函数只有得到结果之后才会返回。
6、非阻塞:如果函数不能立即得到结果,该函数不会阻塞当前线程,会立即返回。
16、孤儿进程,僵尸进程,守护进程
孤儿进程:父进程退出,子进程还在,被init进程收养,完成后序状态采集工作。
僵尸进程:fork创建子进程,子进程退出后,父进程没有调用wait或者waitpid函数去读取子进程退出返回的代码,僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。
处理:子进程退出时发送SIGCHILD信号,父进程处理SIGCHILD信号,信号处理函数中用wait处理僵尸进程。
守护进程:在后台运行不与任何终端关联的进程,一般在进程启动时就会运行,他们以root用户运行。
17、扇区、 块、 页 、簇的概念
一个磁盘按层次分为 磁盘组合 -> 单个磁盘 -> 某一盘面 -> 某一磁道 -> 某一扇区
通俗的来讲,在Windows下如NTFS等文件系统中叫做簇;在Linux下如Ext4等文件系统中叫做块(block)。每个簇或者块可以包括2、4、8、16、32、64…2的n次方个扇区。
读写基本单位是扇区。磁盘的原理,物理实现,磁盘控制器是按照扇区这个单位读取等操作数据的。
页:操作系统经常与内存和硬盘这两种存储设备进行通信,类似于“块”的概念,都需要一种虚拟的基本单位。所以,与内存操作,是虚拟一个页的概念来作为最小单位。与硬盘打交道,就是以块为最小单位。
18、简述处理器在读内存的过程中,CPU核、cache、MMU如何协同工作?
我们将cache放置在CPU和主存之间,作为主存数据的缓存。 当CPU试图从主存中load/store拿数据的时候, CPU会首先从cache中查找对应地址的数据是否缓存在cache 中。如果其数据缓存在cache中,直接从cache中拿到数据并返回给CPU。
MMU:现代操作系统普遍采用虚拟内存管理机制,这需要MMU(Memory Management Unit,内存管理单元)的支持。有些嵌入式处理器没有MMU,则不能运行依赖于虚拟内存管理的操作系统(stm32)
。MMU的作用就是负责虚拟地址(virtual address)转化成物理地址(physical address)。
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上。
19、锁机制
(1)互斥锁:mutex,保证在任何时刻,都只有一个线程访问该资源,当获取锁操作失败时,线程进入阻塞,等待锁释放。
(2)读写锁:rwlock,分为读锁和写锁,处于读操作时,可以运行多个线程同时读。但写时同一时刻只能有一个线程获得写锁。
互斥锁和读写锁的区别:
-
(a)读写锁区分读锁和写锁,而互斥锁不区分
-
(b)互斥锁同一时间只允许一个线程访问,无论读写;读写锁同一时间只允许一个线程写,但可以多个线程同时读。
(3)自旋锁:spinlock,在任何时刻只能有一个线程访问资源。但获取锁操作失败时,不会进入睡眠,而是原地自旋,直到锁被释放。这样节省了线程从睡眠到被唤醒的时间消耗,提高效率。
(4)条件锁:就是所谓的条件变量,某一个线程因为某个条件未满足时可以使用条件变量使该程序处于阻塞状态。一旦条件满足了,即可唤醒该线程(常和互斥锁配合使用)
(5)信号量:允许多个线程访问资源,但是线程数由信号量决定。
20、实时操作系统与分时操作系统的区别
实时操作系统是在外界时间和数据产生时能快速反应处理,按照任务的优先级,尽快的完成操作。
分时操作系统是使一台计算机被多个用户使用,将系统处理器时间和内存按照一定的时间间隔,轮流的切换给不同终端用户的程序使用。由于时间间隔很短,就好像每个用户独占计算机一样。
21、为什么要内存对齐
-
平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址 处取某些特定类型的数据,否则抛出硬件异常。
-
性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说: 结构体的内存对齐是拿空间来换取时间的做法。
22、Linux内存管理
(1)正文段( text段/代码段)
这是由 CPU 执行的机器指令的部分。通常,正文段是可共享的,所以即使是频繁执行的程序(如文本编辑器,C 编译器和 shell 等)在存储器中也只需有一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其指令。
正文段是用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像
。
(2)初始化数据段(数据段)
数据段用来存放可执行文件中已经初始化的全局变量
,换句话说就是存放程序静态分配的变量和全局变量。
例如,C 程序中任何函数之外的声明:
int num = 10;
使此变量以其初值存放在初始化数据段中。
(3)未初始化数据段(bbs段)
bbs段包含了程序中未初始化的全局变量
,在程序开始执行之前,内核将此段中的数据初始化为 0 或空指针。
例如:函数外的声明:
long sum[100];
使此变量存放在非初始化数据段中。
(4)堆
堆是用于存放进程进行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。 由于历史上形成的惯例, 堆位于未初始化数据段和栈之间 。
(5)栈
栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static 声明的变量,static 意味着在数据段中存放变量
)。除此之外在函数调用结束后,函数的返回值也会被存放回栈中。由于栈的后进先出(LIFO)特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲我们可以把堆栈看成一个临时数据寄存,交换的内存区。
(6)参数和环境区
命令行参数和环境变量。
23、共享内存怎么实现同步
1、利用信号灯实现共享内存的同步
,reader和writer通过信号通信必须获取对方的进程号,可利用共享内存保存双方的进程号。reader和writer运行的顺序不确定,可约定先运行的进程创建共享内存并初始化。利用pause, kill, signal等函数可以实现该程序(流程和前边类似)。
2、互斥锁同步
:需要在访问共享内存中数据的时候,查询互斥锁的状态来进行同步。
互斥锁是在线程中出现的概念,但是进程包含多个线程,而共享内存是两个或多个进程之间共享一个给定的存储区
24、创建线程的函数、底层实现,线程的本质是什么
int pthread_create(pthread_t id , pthread_attr_t attr, void(fun)(void), void *arg);
实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数 clone
。
如果复制
对方的地址空间,那么就产出一个“进程”;
如果共享
对方的地址空间,就产生一个“线程”。
所以线程在内核看来是轻量级进程
25、进程线程的状态转换图
(1)就绪状态:进程已获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
(2)执行状态:进程已获CPU,正在执行。单处理机系统中,处于执行状态的进程只一个;多处理机系统中,有多个处于执行状态的进程。
(3)阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)
通常导致进程阻塞的典型事件有:请求I/O,申请缓冲空间等。
一般,将处于阻塞状态的进程排成一个队列,有的系统还根据阻塞原因不同把这些阻塞集成排成多个队列。
(1) 就绪→执行
处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
(2) 执行→就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
(3) 执行→阻塞
正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
(4) 阻塞→就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
“操作系统有哪些调度策略?”。
计算机网络
1、TCP如何保证传输可靠
确认应答机制
超时重传机制
字节编号机制
流量控制、拥塞控制
都是基于动态窗口机制
计算机网络——三次握手与四次挥手
2、TCP/UDP区别
TCP面向连接,UDP无连接
TCP是字节流,UDP是数据报
TCP保证数据可靠,UDP可能丢包
TCP占用资源较多,UDP较少
TCP只有两个端点,只能一对一通讯,UDP可以一对多,多对一和多对多。
3、TCP/UDP优缺点
tcp优点:可靠,首先有三次握手来建立连接,有包括确认,窗口,重传,拥塞控制机制,传输完后还会断开连接来节约系统资源。
缺点:慢,效率低,占用系统资源多。三次握手慢,且TCP容易被攻击,其次各种保护机制占用大量资源。
udp优点:快,没有握手,是一个无状态传输协议,因此传输速度非常快。
缺点:不可靠不稳定,网络不好容易丢包。
4、HTTPS与HTTP的区别
1、前者明文传输,后者需要加密SSL。
2、前者要申请CA证书
3、端口号不一样,前者是443,后者是80;
计算机网络——HTTP协议与HTTPS协议
5、URI与URL
前者是一个概念,后者是具体的网址,后者属于前者
6、三次握手
1、第一次握手:客户端发送syn包(若为j),客户端进入SYN_SEND状态,待服务器确认 syn:同步序列编号
2、第二次握手:服务器收到syn包,首先确认客户端的syn(j+1),自己也发一个syn包(k),进入SYN_RECV状态。
3、第三次握手:客户端收到应答包(j+1),向服务器发送确认包(k+1),此包发送完毕后进入established阶段,完成三次握手。
7、只有二次握手的问题
1、防止失效的报文从新到了服务器,而产生错误。
如果是两次握手还会出现一个问题,客户端的第一次SYN请求在网络中阻塞时,客户端重新发送第二次SYN请求,服务器收到第二次SYN请求后,成功与客户端两次握手,双方建立连接,在数据传输结束后,双方断开链接,这时,第一次的SYN请求在服务端到来,服务端会认为客户端想要重新建立链接,给客户端发出确认建立连接,会一直等待客户端发送数据,而客户端已经完成了自己的数据传输任务,不会再给服务端发信息,于是服务端就一直等待,造成了资源的浪费。
8、四次挥手
1、第一次挥手:客户端将FIN置为1,发送seq给服务端,进入FIN WAIT 1状态
2、第二次挥手:服务端收到FIN后,回复数据加一,进入CLOSE_WAIT状态,此时客户端已经没有要发送的数据了,等到客户端发送完数据,客户端进入FIN_WAIT2状态。
3、第三次挥手:服务端将FIN置为1,发送序列号给客户端,进入LAST_ACK状态;
4、第四次挥手:客户端收到FIN后,进入TIME_WAIT状态,ACK置为1,发送序列号加一,服务器收到确认后变成closed状态,不再向客户端发送数据,客户端等待2*MSL后也进入CLOSED状态,完成四次挥手。
9、为什么要等2msl
1、保证客户端的最后一个确认报文能到达服务器,不会丢失,如果没有2MSL等待时间,那么服务端一直发送超时重发报文,最终无法进入CLOSED状态
2、2MSL时长可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中不会出现旧连接中的报文段
10、为什么握手是三次,挥手是四次
计算机网络——三次握手与四次挥手
11、socket IO多路复用
Linux网络编程——IO多路复用
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:
(1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
(2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
(3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
(4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
(5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
select函数实现:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
maxfdp——传入参数,集合中所有文件描述符的范围,即最大文件描述符值+1
readfds——传入传出参数,select调用时传入要监听的可读文件描述符集合,select返回时传出发生可读事件的文件描述符集合
writefds——传入传出参数,select调用时传入要监听的可写文件描述符集合,select返回时传出发生可写事件的文件描述符集合
errorfds——传出参数,select返回时传出发生事件(包括可读和可写)中异常事件的文件描述符集合
timeout——传入参数,设置select阻塞的时间。若设置为NULL,则select一直阻塞直到有事件发生;
-
若设置为0,则select为非阻塞模式,执行后立即返回;
-
若设置为一个大于0的数,即select的阻塞时间,若阻塞时间内有事件发生就返回,否则时间到了立即返回
select工作原理:传入要监听的文件描述符集合(可读、可写或异常)开始监听,select处于阻塞状态,当有事件发生或设置的等待时间timeout到了就会返回,返回之前自动去除集合中无事件发生的文件描述符,返回时传出有事件发生的文件描述符集合。但select传出的集合并没有告诉用户集合中包括哪几个就绪的文件描述符,需要用户后续进行遍历操作。
epoll函数实现
针对select的缺点做了如下改进:
-
内核中保存一份文件描述符集合(红黑树),无需用户每次都重新传入,只需告诉内核修改的部分即可。
-
内核不再通过轮询的方式找到就绪的文件描述符,而是通过异步 IO 事件唤醒。
-
内核仅会将有 IO 事件的文件描述符返回给用户,用户也无需遍历整个文件描述符集合。
12、程序崩溃定位、程序死机解决。
打印信息、崩溃日志
一般用assert 断言的方法,或者打印信息,GDB跟一下等等方法。
段错误一般看看内存溢出,指针越界等等。
gdb可以看到段错误的地方,