1.什么是设备驱动程序?
应用程序和实际硬件之间的软件层,连接硬件和内核之间的桥梁
2.linux将存储器和外设分为3类?
字符设备、块设备、网络设备
字符设备:按字节访问的设备,有序访问
块设备:能一次传送一个或者多个长度是512字节的整块数据的设备,linux允许按任意字节传送,随机访问
网络设备:面向数据包的接收和发送而设计
3.关键字volatile含义?
一个定义为volatile的变量是说这变量可能会被硬件设备的终端意想不到地改变,这样,编译器就不会去
优化这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读
取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
4.异常的类别
中断(interrupt):异步发生,来自I/O设备的信号,并不是由指令造成。总是返回到下一条指令。(内部中断(用户态切换到内核态),外部中断(外设))
陷阱(trap):同步发生,有意的异常,是执行一条指令的结果。总是返回到下一条指令。
故障(fault):同步发生,由错误情况引起,可能被修复。可能返回到当前指令。
终止(abort):同步发生,不可恢复的错误(硬件错误),终止处理程序从不将控制返回给应用程序。不会返回。
5.源程序到可执行文件的过程
源程序(.c)--[预处理器cpp]--预处理后程序(.i)--[编译器ccl]--汇编程序(.s)--[汇编器as]--可重定位目标程序(.o)--[链接器ld]--可执行程序(二进制)
6.程序和进程、线程
程序:是一个静态的实体,存放在磁盘上的代码和数据的可执行映像
进程:是一个动态的实体,一个执行中的程序的实例,分配和管理资源的基本单位
线程:进程是由一个或者多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,共享同样的代码和全局数据。调度的最小单位
进程上下文:由程序正确运行的所需的状态组成,
这些状态:程序代码、栈、寄存器内容、PC(程序计数器)、数据、环境变量、打开文件的描述符集合。
并发运行导致一个进程指令和另一个进程指令交替运行---进程上下文切换
进程四要素:(1)一段程序可供其执行
(2)有进程专用的内核空间堆栈
(3)内核中有一个task_struct数据结构
(4)有独立的用户空间(故进程是用户空间的概念),线程没有自己独立的地址空间
线程可分为:用户线程,内核线程
驱动程序既不是线程也不是进程。
7.引起竞态的原因
多CPU、单CPU抢占调度、中断
解决竞态措施---保证共享资源的互斥访问
中断屏蔽、原子操作、自旋锁(适合临界区小,不能有阻塞可能)、信号量(适合临界区大)、互斥体
8.arm处理器的7中工作模式
用户模式、系统模式、快速中断模式、外部中断模式、管理模式、未定义指令中止模式、数据访问终止模式
9.非向量中断和向量中断
非向量中断:由软件提供中断服务程序入口地址,多个中断共享一个总入口,进入后利用软件判断具体哪个中断
向量中断:由硬件提供中断服务程序入口地址,每个中断对应一个中断号,并对应一个中断入口地址
10.MMU(内存管理单元)的作用
提供虚拟地址到物理地址的映射
内存访问权限的保护
Cache缓存控制
11.I/O空间和内存空间
I/O空间:一般是指在x86处理器专用的空间,它是将外设寄存器看做独立的空间,有自己一套独立地址线
位于IO空间的外设寄存器称为IO端口,并用专门的指令(区别于内存访问指令)来访问.
内存空间:cpu理论最大寻址空间
位于内存空间的外设寄存器称为IO内存,和内存统一编址,和访问内存指令一样访问外设寄存器
12.TCP和UDP的区别
TCP:传输控制协议,面向有连接的可靠传输协议,流模式
三次握手建立连接
数据传递有确认,重传等机制,可靠稳定
缺点:效率低,占用系统资源高
UDP:用户数据报协议,面向无连接,数据报模式
没有握手等机制,传输速度快,安全,系统占用资源少
缺点:不可靠,网络不好时,数据传输时容易丢包
没有重新传输机制
TCP:QQ文件传输,http(对网络通讯质量有要求时)
UDP:qq视频,语音,(对网络质量要求不高,追求通讯速度)
13.ISO/OSI七层网络模型和linux网络模型
应用层 应用层(HTTP/FTP)
表示层
会话层
-----------------------------------------------------------------
传输层 传输层(TCP/UDP)
-----------------------------------------------------------------
网络层 网络层(ARP/IP/ICMP)
-----------------------------------------------------------------
数据链路层 物理层
物理层
14.栈和堆的区别
1.管理方式: 栈由编译器自动管理;堆由程序员控制,使用方便,但易产生内存泄露。
2.生长方向: 栈向低地址扩展(即”向下生长”),是连续的内存区域;堆向高地址扩展(即”向上生长”),是不连续的内存区域。
这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历。
3.空间大小: 栈顶地址和栈的最大容量由系统预先规定(通常默认2M或10M);堆的大小则受限于计算机系统中有效的虚拟内存,
32位Linux系统中堆内存可达2.9G空间。
4.存储内容: 栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,
然后是被调函数的局部变量。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点
继续运行下条可执行语句。堆通常在头部用一个字节存放其大小,堆用于存储生存期与函数调用无关的数据,具体内容由程序员安排。
5.分配方式: 栈可静态分配或动态分配。静态分配由编译器完成,如局部变量的分配。动态分配由alloca函数在栈上申请空间,
用完后自动释放。堆只能动态分配且手工释放。
6.分配效率: 栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高。
堆由函数库提供,机制复杂,效率比栈低得多。Windows系统中VirtualAlloc可直接在进程地址空间中分配一块内存,快速且灵活。
7.分配后系统响应: 只要栈剩余空间大于所申请空间,系统将为程序提供内存,否则报告异常提示栈溢出。
操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,
然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增
加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。,大多数系统会在该内存空间首地址处记录本次分配的内存大小,
供后续的释放函数(如free/delete)正确释放本内存空间。此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分
重新放入空闲链表中。
8.碎片问题: 栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。而频繁申请释放操作
会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。可见,堆容易造成内存碎片;由于没有专门的系统支持,效率很低;
由于可能引发用户态和内核态切换,内存申请的代价更为昂贵。所以栈在程序中应用最广泛,函数调用也利用栈来完成,调用过程中的参数、
返回地址、栈基指针和局部变量等都采用栈的方式存放。所以,建议尽量使用栈,仅在分配大量或大块内存空间时使用堆。
使用栈和堆时应避免越界发生,否则可能程序崩溃或破坏程序堆、栈结构,产生意想不到的后果。
15.局部变量和全局变量的区别
i.作用域不同
全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要
用extern 关键字再次声明这个全局变量。
静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量
对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行
结束后,变量被撤销,其所占用的内存也被收回。
静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件
里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
ii.内存分配区域不同
全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。
16.const应用
声明常量:
int const a=10;
const int a;
声明指向常量的指针,不能修改指针指向的值,能修改指针的值,就是能修改指向。
int const *a;//const int *a;
声明指向变量的常量指针,不能修改指针的值,就是不能修改指向,能修改指针指向的值
int *const a;
17.上拉电阻和下拉电阻作用
上拉电阻就是把不确定的信号通过一个电阻钳位在高电平,此电阻还起到限流的作用。提高驱动能力。
下拉电阻是把不确定的信号钳位在低电平。
1、 当TTL电路驱动CMOS电路时,如果TTL电路输出的高电平低于CMOS电路的最低高电平(一般为3.5V), 这时就需要在TTL的输出端接上拉电阻,以提高输出高电平的值。
2、 OC门电路必须加上拉电阻,以提高输出的高电平值。
3、 为加大输出引脚的驱动能力,有的单片机管脚上也常使用上拉电阻。
4、 在CMOS芯片上,为了防止静电造成损坏,不用的管脚不能悬空,一般接上拉电阻降低输入阻抗,提供泄荷通路。
5、 芯片的管脚加上拉电阻来提高输出电平,从而提高芯片输入信号的噪声容限,增强抗干扰能力。
6、 提高总线的抗电磁干扰能力。管脚悬空就比较容易接受外界的电磁干扰。
7、 长线传输中电阻不匹配容易引起反射波干扰,加上下拉电阻是电阻匹配,有效的抑制反射波干扰。
18.什么是存储机制里的大、小端模式
Little-Endian(小端)就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
Big-Endian(大端)就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。(网络字节序)
19.串口UART中FIFO作用
FIFO是先进先出缓冲区的意思,即串口接收到的数据可以先进入FIFO,不必马上进入中断服务程序接收,这样可节省CPU时间。
对于发送数据也一样,可以把要发送的数据一起写入FIFO,串口控制器可按写入顺序依次发送出去。
20.字符I/O
从流中读:单个字符
fgetc(),getc(),getchar()
写入流中:单个字符
fputc(),putc(),putchar()
从流中读到缓冲区:字符串
fgets(),gets()--------gets()不在缓冲区结尾存储换行符
从缓冲区写到流中:字符串
fputs(),puts()--------puts()在缓冲区结束再添加换行符
21.Socket编程(网络进程间的通信)
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。
这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
22.文件描述符和文件指针
文件描述符:在linux系统中打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一
份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。
文件指针:C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区
和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在Windows系统上,文件描述符被
称作文件句柄)。
23.阻塞和非阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起,就是条件不满足时就会被挂起。被挂起的进程进入休眠状态,被从调度器的运行队
列移走,直到等待的条件被满足。系统采用时间片轮转策略,CPU将让出。当条件满足时,休眠会被唤醒。
char buf;
fd = open("/dev/ttyS1",O_RDWR);
.....
res = read(fd,&buf,1); //当串口上有输入时才返回,没有输入则进程挂起睡眠
if(res == 1)
{
printf("%c/n",buf);
}
非阻塞是指不管条件满不满足都会返回。并不挂起,它或者放弃,或者不停地查询,
直至可以进行操作为止。
char buf;
fd = open("/dev/ttyS1",O_RDWR|O_NONBLOCK);//O_NONBLOCK 非阻塞标识
.....
while(read(fd,&buf,1)!=1);//串口上没有输入则返回,所以循环读取
printf("%c/n",buf);
在阻塞模式下,若从网络流中读取不到指定大小的数据量,阻塞IO就在那里阻塞着。比如,已知后面会有10个字节的数据发过来,
但是我现在只收到8个字节,那么当前线程就在那傻傻地等到下一个字节的到来,对,就在那等着,啥事也不做,直到把这10个字节读取完,
这才将阻塞放开通行。
在非阻塞模式下,若从网络流中读取不到指定大小的数据量,非阻塞IO就立即通行。比如,已知后面会有10个字节的数据发过来,
但是我现在只收到8个字节,那么当前线程就读取这8个字节的数据,读完后就立即返回,等另外两个字节再来的时候再去读取。
评价::非阻塞IO比阻塞IO有更高的性能,但是对于开发来的,难度就成数倍递增了。由于是有多少数据就读取多少数据,这样在读取完整
之前需要将已经读取到的数据保存起来,而且需要与其他地方来的数据隔离开来不能混在一起,否则就不知道这数据是谁的了,呵呵。
24.malloc和new区别及使用
说明:
malloc:用于c语言中,动态内存分配函数,分配到堆上的内存,未初始化(calloc进行了初始化),分配的是连续的内存
new:用于c++中,动态内存分配关键词,分配到堆上的内存,未初始化,分配的不一定是连续的内存
用法:
void *malloc(int size);
malloc返回的是void*指针,需要强制转化为实际类型(有必要进行返回值是否是NULL进行验证)
//申请一个整型变量
int *p;
p=(int *)malloc(sizeof(int));
//申请一个大小为num的整型数组
int *parr;
parr=(int *)malloc(num*sizeof(int));
数据类型* name;
name=(数据类型*)malloc(num*sizeof(数据类型));
//进行释放
free(parr);//必须整块释放,不允许只释放一部分
new返回实际数据类型地址指针
//申请一个整型变量
int *p=new int;//p所指向的内存,一般不称为变量,而是数据对象
数据类型*pointer=new 数据类型;
delete p;
//new的主要用武之地在于大型数据(数组,字符串,结构体)
//比如数组在静态联编时,必须在编译前指定数组长度;动态联编可以在运行时指定数组长度
//申请一个大小为num的整型数组
int *parr=new int [num];
delete [] parr;
//申请大小为num的结构体数组
typedef struct test
{
int a;
float b;
}Test;
Test *ptest=new Test [num];
delete [] ptest;
25.const和#define的区别
(1)define宏是在预编译时展开
const常量是在运行时使用
(2)define宏没有数据类型,没有类型安全检查,只是展开
const有数据类型,在编译期间会进行数据类型安全检查
(3)const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝
26.指针和引用
(1)引用主要用于作为函数参数和返回值,无法返回局部变量的引用
(2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
(3)相比指针传递函数参数,程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
(4)相比一般值传递,当发生函数调用时,需要给 形参分配存储单元,形参变量是实参变量的副本,传递结构时会占用很大内存
(5)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(6)不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)。
(7)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
27.strcpy和memcpy区别
1、 复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、 复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"