嵌入式linux基础知识

    xiaoxiao2025-06-14  26

    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不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。     3、 用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy          28.sizeof和strlen     strlen不包含'\0'     sizeof包含'\0'     char a[]="hello";     strlen("hello")=strlen(a)=5     sizeof("hello")=sizeof(a)=6          sizeof(数组名)=数组长度     sizeof(指针)=4     传递给函数的数组,再用sizeof也是4      29.实时操作系统     非实时操作系统:多人多任务操作系统,windows,linux,unix时间片轮转运作模式     实时操作系统:能够对外界事件和数据作出快速处理并快速响应ucosii,vxworks         硬实时:各个任务不仅要求执行无误还要做到准时         软实时:主要宗旨是各个任务越快越好,并不要求限定某一个任务必须在多长时间内完成              linux进程任务状态:         执行态(TASK_RUNNING):             正在被CPU运行或者就绪的状态,分3种:内核运行态,用户运行态,就绪态         可中断睡眠态(TASK_INTERUPTIBLE):             处于等待状态中的进程,一旦被该进程等待的资源被释放,进程进入运行状态         不可中断睡眠态(TASK_UNINTERRUPTIBLE):             只能用wake_up()函数唤醒         暂停态(TASK_STOPPED):             收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU进入暂停态,收到SIGCONT进入运行态         僵死态(TASK_ZOMBIE):             进程已终止,资源未被回收                  ucosii进程任务状态:         就绪状态:             具备了运行的充分条件         运行状态:             处于就绪态的任务被调度器获得了CPU的使用权         等待状态:             正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行,此时就转为等待状态         睡眠状态:             任务只以代码的形式驻留在程序空间(ROM或RAM),还没有交给操作系统管理时的情况         中断服务状态:             一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序                  linux调度:从就绪的进程表中选择最合适的进程或者线程来拥有cpu使用权         调度策略:选择哪个进程来占用CPU的依据             实时调度类:SCHED_FIFO先入先出实时进程                         SCHED_RR时间片轮转             CFS调度类(公平调度):SCHED_NORMAL普通的分时调度                                 SCHED_BATCH批处理进程                                 SCHED_IDLE只在系统空闲时被调度的进程                                              一个进程会选出其中一种作为自己参与调度的依据,而不是整个系统都是一种策略             实时调度 > CFS调度   《优先级》             linux总调度策略::优先级                      调度时机:schedule()调度函数什么时候被调用             主动式调度:主动请求内核调用schedule(),主动让出CPU             抢占式调度:                 用户抢占:linux2.4、linux2.6                 内核抢占:linux2.6-----更高优先级的进程可以抢占内核中运行的进程                      调度步骤:schedule()函数执行步骤             i.清理当前运行中的进程(释放资源)             ii.选择下一个要运行的进程             iii.设置新进程的运行环境             iv.进程的上下文切换                                                                                                                                 
    转载请注明原文地址: https://ju.6miu.com/read-1299942.html
    最新回复(0)