嵌入式面试问题系列|常见嵌入式面试题之Linux操作系统篇 ——第2期


常见嵌入式面试题之Linux操作系统篇 ——第2期

    • 前言
    • 第2期问题:
      • 1.读写锁
      • 2.select和epoll函数的使用和解释
      • 3.Fork出来的子进程和父进程都有什么区别
      • 4.TCP/IP五层协议各个都实现什么功能
      • 5.信号量的处理流程
      • 6.线程同步和互斥都包含哪些内容
      • 7.你了解的嵌入式linux
      • 8.物理内存和虚拟内存的区别
      • 9.为什么要用虚拟内存
      • 10.通信协议(SPI、IIC、UART)
      • 11.什么是DMA
      • 12.内存碎片产生原因及解决办法
      • 13.UART
      • 14.IIC
      • 15.SPI
      • 16.字和字节
      • 17.程序什么时候应该使用线程,什么时候单线程效率高
      • 18.winsock建立连接的主要实现步骤
      • 19.什么是动态链接和静态链接
      • 20.IP组播的优点
    • 总结

前言 本博客旨在分享学习过程中整理过的相关知识,主要涉及C语言、C++及嵌入式相关问题。若涉及引用未注明出处的部分请及时私信或评论。博客内容有误的部分也会及时勘正,最后如有转载请注明出处~谢谢支持!

第2期问题: 1.读写锁
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
【嵌入式面试问题系列|常见嵌入式面试题之Linux操作系统篇 ——第2期】在读写锁保持期间也是抢占失效的。如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。
2.select和epoll函数的使用和解释
select原理概述
调用select时,会发生以下事情:
从用户空间拷贝fd_set到内核空间;
注册回调函数__pollwait;
遍历所有fd,对全部指定设备做一次poll(这里的poll是一个文件操作,它有两个参数,一个是文件fd本身,一个是当设备尚未就绪时调用的回调函数__pollwait,这个函数把设备自己特有的等待队列传给内核,让内核把当前的进程挂载到其中);
当设备就绪时,设备就会唤醒在自己特有等待队列中的【所有】节点,于是当前进程就获取到了完成的信号。poll文件操作返回的是一组标准的掩码,其中的各个位指示当前的不同的就绪状态(全0为没有任何事件触发),根据mask可对fd_set赋值;
如果所有设备返回的掩码都没有显示任何的事件触发,就去掉回调函数的函数指针,进入有限时的睡眠状态,再恢复和不断做poll,再作有限时的睡眠,直到其中一个设备有事件触发为止。
只要有事件触发,系统调用返回,将fd_set从内核空间拷贝到用户空间,回到用户态,用户就可以对相关的fd作进一步的读或者写操作了。
epoll原理概述
调用epoll_create时,做了以下事情:
内核帮我们在epoll文件系统里建了个file结点;
在内核cache里建了个红黑树用于存储以后epoll_ctl传来的socket;
建立一个list链表,用于存储准备就绪的事件。
调用epoll_ctl时,做了以下事情:
把socket放到epoll文件系统里file对象对应的红黑树上;
给内核中断处理程序注册一个回调函数,告诉内核,如果这个句柄的中断到了,就把它放到准备就绪list链表里。
调用epoll_wait时,做了以下事情:
观察list链表里有没有数据。有数据就返回,没有数据就sleep,等到timeout时间到后即使链表没数据也返回。而且,通常情况下即使我们要监控百万计的句柄,大多一次也只返回很少量的准备就绪句柄而已,所以,epoll_wait仅需要从内核态copy少量的句柄到用户态而已。
两者区别:
select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可
select缺点:
(1)每次调用 select(),都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大,同时每次调用 select() 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大。
(2)单个进程能够监视的文件描述符的数量存在最大限制,在 Linux 上一般为 1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低
(3)select函数在每次调用之前都要对参数进行重新设定,这样做比较麻烦,而且会降低性能
select()和poll()系统调用的本质一样,poll()的机制与select()类似,与select()在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理。
3.Fork出来的子进程和父进程都有什么区别
子进程得到的是除了代码段是与父进程共享的意外,其他所有的都是得到父进程的一个副本,子进程的所有资源都继承父进程,得到父进程资源的副本,既然为副本,也就是说,二者并不共享地址空间。,两个是单独的进程。两者的虚拟空间不同,但其对应的物理空间是同一个
关于文件描述符:继承父进程的文件描述符时,相当于调用了dup函数,父子进程共享文件表项,即共同操作同一个文件,一个进程修改了文件,另一个进程也知道此文件被修改了。
线程与进程之间的关系
一个进程的线程之间共享由进程获得的资源,但线程拥有属于自己的一小部分资源,就是栈空间,保存其运行状态和局部自动变量的。
#include #include main () { pid_t pid; pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("i am the child process, my process id is %d\n",getpid()); else printf("i am the parent process, my process id is %d\n",getpid()); }

(该部分为转载,转载地址由于时间原因不详)
Q: 我就想不到为什么两行都打印出来了,在我想来,不管pid是多少,都应该只有一行才对
A: 这里的if和else不是以前理解的选择分支。fork后产生的子进程和父进程并行运行的.这种理解是不正确的。if 和 else 还是选择分支。 主要的原因是,fork() 函数调用一次,返回两次。两次返回的区别是:子进程的返回值是0,父进程返回值为新子进程的进程ID。
Q: 但是只有一个pid=fork(); 呀,fork()返回的第二次值在什么时候赋给pid呢
A: pid这个变量是有两个的, 父进程一个, 子进程一个。
操作系统中的“进程(process)”概念。一个进程,主要包含三个元素:
一个可以执行的程序;
和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等);
程序的执行上下文(execution context)。
pid=fork();
操作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文和数据,绝大部分就是原 进程(父进程)的拷贝,但它们是两个相互独立的进程!此时程序寄存器pc,在父、子进程的上下文中都声称,这个进程目前执行到fork调用即将返回(此时 子进程不占有CPU,子进程的pc不是真正保存在寄存器中,而是作为进程上下文保存在进程表中的对应表项内)。问题是怎么返回,在父子进程中就分道扬镳。
父进程继续执行,操作系统对fork的实现,使这个调用在父进程中返回刚刚创建的子进程的pid(一个正整数),所以下面的if语句中pid<0, pid==0的两个分支都不会执行。所以输出i am the parent process…
子进程在之后的某个时候得到调度,它的上下文被换入,占据 CPU,操作系统对fork的实现,使得子进程中fork调用返回0。所以在这个进程(注意这不是父进程了哦,虽然是同一个程序,但是这是同一个程序的另 外一次执行,在操作系统中这次执行是由另外一个进程表示的,从执行的角度说和父进程相互独立)中pid=0。这个进程继续执行的过程中,if语句中 pid<0不满足,但是pid==0是true。所以输出i am the child process…
fork 之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互 相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中 fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
4.TCP/IP五层协议各个都实现什么功能
TCP/IP层次模型共分为五层:应用层HTTP、传输层TCP、网络层IP、数据链路层Data-link、物理层physical。
应用层—应用层是所有用户所面向的应用程序的统称。ICP/IP协议族在这一层面有着很多协议来支持不同的应用,如我们进行万维网(WWW)访问用到了HTTP协议、文件传输用FTP协议、电子邮件发送用SMTP、域名的解析用DNS协议、 远程登录用Telnet协议等等,都是属于TCP/IP应用层的.
传输层—这一层的的功能主要是提供应用程序间的通信,TCP/IP协议族在这一层的协议有TCP和UDP。
网络层—是TCP/IP协议族中非常关键的一层,主要定义了IP地址格式,从而能够使得不同应用类型的数据在Internet上通畅地传输,IP协议就是一个网络层协议。
网络接口层(数据链路层)—这是TCP/IP软件的最低层,负责接收IP数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层。
5.信号量的处理流程
信号量是一种软中断,用来实现Linux内核和应用程序之间的异步通信。
每一个信号量由一个4字节整形数据表示。可以通过man 7 signal查看所有信号量描述。其中1——32号信号量是从Unix继承而来,33——64是Linux内核定义的信号量。
Linux内核为每个信号量设置了默认处理动作,如Term(终止执行)、Ign(忽略)、Core(终止执行并产生coredump)、Stop(停止运行)和Cont(继续运行),当应用程序接收到某个信号量时,则按照默认处理动作执行。应用程序也可以通过sigaction()或者signal()函数修改默认的处理动作,比如屏蔽或者忽略某个信号量等。
应用程序信号量处理函数通常是链接glibc中默认的信号量处理函数,也可以自己编写和指定信号量处理函数。但是需要注意的是,SIGKILL和SIGSTOP信号量不能被捕获(caught)、屏蔽(blocked)或者忽略(ignored)。
信号量触发情况有三种:
1)Linux内核检测到应用程序异常,发送特定的信号量给应用程序,应用程序捕获到信号量后,调用信号量处理函数;
2)Linux内核因为内部事件而给应用程序发送特定信号,通知应用程序发生了某个事件,如著名的segmentation fault,应用程序捕获到信号量后,调用信号量处理函数;
3)Linux内核检测到外部事件,如Ctrl+C,Ctrl+Z等,发送特定信号给应用程序,应用程序捕获到信号量后,调用信号量处理函数;
6.线程同步和互斥都包含哪些内容
线程同步:同步就是协同步调,按预定的先后次序进行运行。
线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系。
线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。
线程同步的方式和机制:
临界区(Critical Section)、互斥对象(Mutex):主要用于互斥控制;都具有拥有权的控制方法,只有拥有该对象的线程才能执行任务,所以拥有,执行完任务后一定要释放该对象。
信号量(Semaphore)、事件对象(Event):事件对象是以通知的方式进行控制,主要用于同步控制。
7.你了解的嵌入式linux
1、Linux系统是免费的自由软件
Linux系统是通过公共许可协议GPL的自由软件。这种软件具有两个特点,一是开放源代码并免责提供,二是开发者可以根据自身需要自由修改、复制和发布程序的源码。因此,用户可以从互联网上很方便地免费下载并使用Linux操作系统,不需要担心版权问题。
2、良好的硬件平台可移植性
硬件平台可移植性是指将操作系统从一个硬件平台转移到另一个硬件平台时,只需改变底层的少量代码,无需改变自身的运行方式。Linux系统最早诞生于PC机环境,一系列版木都充分利用了X86 CPU的任务切换能力,使X86 CPU的效能发挥得淋漓尽致。另外,Linux系统几乎能在所有主流CPU搭建的体系结构上运行,包括 Intel/AMD、HP-PA、MIPS、Powerpc、 ULTRASPARC和 ALPHA等,其伸缩性超过了所有其他类型的操作系统。
3、完全符合POSIX标准
POSⅨ也称为可移植的Linux操作系统接口,是由ANSI和ISO制订的一种国际标准,它在源代码级别上定义了一组最小的Linux操作系统接口。Linux系统遵循这一标准,使得它和其他类型的Linux系统之间可以很方便地相互移植平台上的应用软件。
4、具有良好的图形用户界面
Linux系统具有类似于 Windows操作系统的图形界面,其名称是 X-Window系统。X- Window是一种起源于Linux操作系统的标准图形界而,它可以为用户提供一种具有多种窗口管理功能的对象集成环境。
5、具有强大的网络功能
由于Linux系统是依靠互联网平台迅速发展起来的,所以也具有强大的网络功能。它在内核中实现了 TCP/TP协议簇,提供了对 TCP/TP协议簇的支持。同时,它还可以支持其他各种类型的通信协议,如 IPX’SPX、 Apple Talk、PP、SLP和ATM等。
6、丰富的应用程序和开发工具
由于 Linux系统具有良好的可移植性,目前绝大部分其他Linux系统下使用的流行软件都已经被移植到 Linux系统中。另外,由于 Linux得到了IBM、 Intel、 Oracle及 Syabse等知名公司的支持,这些公司的知名软件也都移植到了 Linux系统中,因此, Linux获得越来越多的应用程序和应用开发工具。
7、良好的安全性和稳定性
Linux系统采取了多种安全措施,如任务保护机制、审计跟踪、核心报校、访问授权等,为网络多用户环境中的用户提供了强大的安全保障。由于Linux系统的开放性及其他一些原因,使其对计算机病具有良好的防御机制, Linux平台基木上不需要安装防病毒软件。另外,Linux系统具有极强的稳定性,可以长时间稳定地运行。
8.物理内存和虚拟内存的区别
物理内存:真实的硬件设备(内存条)
虚拟内存:利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间(Swap Space)。(为了满足物理内存的不足而提出的策略)。
内核会将暂时不用的内存块信息写到交换空间,这样以来,物理内存得到了释放,这块内存就可以用于其它目的,当需要用到原始的内容时,这些信息会被重新从交换空间读入物理内存。
1.进程还没开始运行时, 磁盘中程序的文件和虚拟内存先建立好映射,此时数据还在磁盘中。并没有拷贝到物理内存
2.当运行对应程序代码段的时候,之前虚拟内存建立的映射中,会有张页表项,记录实际的磁盘地址,和是否已经加载到物理内存中。
3.当发现数据未加载到物理内存时,发生缺页异常,对应的缺页中断响应函数会将磁盘上的数据拷贝到物理内存中。
4.而当对应的数据已经加载过时,会直接从物理内存获取。同时要是已经拷贝到物理内存的数据好久没用,liunx又会将不经常使用的页面文件交换到虚拟内存
9.为什么要用虚拟内存
没有虚拟内存概念的时候,程序寻址用的都是物理地址。取决于CPU的地址线条数,32位平台的话 2^32也就是4G。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小。
增加一个中间层,利用一种间接的地址访问方法访问物理内存。解决进程地址空间隔离的问题
没有有效的内存管理机制时,直接使用物理地址有以下三个问题:
???1. 地址空间不隔离:
???物理地址是连续的,直接使用物理地址,所有程序使用的内存在一个空间内,当一个程序内存溢出,很容易影响其他程序的正常运行,导致其他程序崩溃。
???2.内存使用效率低:
???假设计算机有129M内存,已经有两个程序在运行,程序A需要10M,程序B需要100M,当程序C需要20M,执行时,调入内存,由于程序需要的空间是连续的,所以直接换出A可能释放空间不够,只能换出B,过程中有大量数据在内存和磁盘间交互,效率并不高。
???3.程序运行地址不确定:
???程序编写时,很多时候访问数据和指令跳转的目标地址是固定的,而直接使用物理地址,不能保证每次装入都放在同一块区域,就给程序编写带来了一定的麻烦。
问题解决:
???针对问题1:
???虚拟地址保证每个进程都有自己独立的虚拟空间,而且进程空间只能访问自己的地址空间,这样就做到了进程隔离。
???针对问题3:
???以程序A为例,虚拟空间总是0到100M的连续空间,可以以分段的形式一对一的映射到物理空间上一段100M的空间内,这样即使每次分配的物理空间不固定,但程序看到的虚拟空间地址是固定的,大大方便了编程。
???针对问题2:
???解决效率的方法,是分页,我们把进程的虚拟空间按页分割,叫虚拟页,常用的装载到内存中,不常用的代码和数据保存到磁盘里,需要的时候再读入,相应的物理内存分页叫物理页,两者映射,可以大大增加内存的使用效率。
10.通信协议(SPI、IIC、UART)
UART(Universal Asynchronous Receive Transmitter):经常所说的串口,基本都用于调试。
主机和从机至少要接三根线,RX、TX和GND。TX用于发送数据,RX用于接受数据(收发不是一根线,所以是全双工方式)。
如果A是PC机,B是单片机,A和B之间还要接一块电平转换芯片,用于将TTL/CMOS(单片机电平)转换为RS232(PC机电平)。因为TTL/CMOS电平范围是01.8/2.5/3.3/5V(不同单片机范围不同),高电压表示1,低电压表示0。而RS232逻辑电平范围-12V12V,-5-12表示高电平,+5+12V表示低电平。
SPI(Serial Peripheral Interface, 同步外设接口)是由摩托罗拉公司开发的全双工同步串行总线,该总线大量用在与EEPROM、ADC、FRAM和显示驱动器之类的慢速外设器件通信。
SPI是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。SPI 接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟),CS(从使能信号)四种信号构成,CS 决定了唯一的与主设备通信的从设备,片选信号低电平有效。如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟来发起通讯。通讯时,数据由SDO 输出,SDI 输入,数据在时钟的上升或下降沿由SDO 输出,在紧接着的下降或上升沿由SDI 读入,这样经过8/16 次时钟的改变,完成8/16 位数据的传输。
IIC(Inter Integrated Circuit):两根线:一个时钟线SCL和一个数据线SDA。只有一根数据线,所以是半双工通信。
假设A给B发数据(这里A.SCL接B.SCL, A.SDA接B.SDA)。起初SDA和SCL上的电平都为高电平。然后A先把SDA拉低,等SDA变为低电平后再把SCL拉低(以上两个动作构成了iic的起始位),此时SDA就可以发送数据了,与此同时,SCL发送一定周期的脉冲(周期和PCLK有关,一般会在IIC的控制寄存器中设置)。SDA发送数据和SCL发送脉冲的要符合的关系是:SDA必须在SCL是高电平是保持有效,在SCL是低电平时发送下一位(SCL会在上升沿对SDA进行采样)。规定一次必须传8位数据,8位数据传输结束后A释放SDA,但SCL再发一个脉冲(这是第九个脉冲),这会触发B通过将SDA置为低电平表示确认(该低电平称为ACK)。最后SCL先变为高电平,SDA再变为高电平(以上两个动作称为结束标志)如果B没有将SDA置为0,则A停止发送下一帧数据。IIC总线(即SDA和SCL)上的每个设备都有唯一地址,数据包传输时先发送地址位,接着才是数据。一个地址字节由7个地址位(可以挂128个设备)和1个指示位组成(7位寻址模式)。指示位是0表示写,1表示读。还有10位寻址模式,使用两个字节来保存地址,第一个字节的最低两位和第二个字节的8位合起来构成10位地址。
2、UART, SPI, IIC的区别与联系:
第一个区别当然是名字:
UART(Universal Asynchronous Receiver Transmitter:通用异步收发器)
SPI(Serial Peripheral Interface:串行外设接口);
I2C(INTER IC BUS)
第二,区别在电气信号线上:
SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI)。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。主从设备间可以实现全双工通信,当有多个从设备时,还可以增加一条从设备选择线。
如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO),一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。
I2C总线是双向、两线(SCL、SDA)、串行、多主控(multi-master)接口标准,具有总线仲裁机制,非常适合在器件之间进行近距离、非经常性的数据通信。在它的协议体系中,传输数据时都会带上目的设备的设备地址,因此可以实现设备组网。
UART总线是异步串口,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件上由两根线,一根用于发送,一根用于接收。
11.什么是DMA
DMA(Direct Memory Access),即直接存储器存取,是一种快速传送数据的机制。数据传递可以从适配卡到内存,从内存到适配卡或从一段内存到另一段内存。
利用它进行数据传送时不需要CPU的参与。每台电脑主机板上都有DMA控制器,通常计算机对其编程,并用一个适配器上的ROM(如软盘驱动控制器上的ROM)来储存程序,这些程序控制DMA传送数据。一旦控制器初始化完成,数据开始传送,DMA就可以脱离CPU,独立完成数据传送。
在DMA传送开始的短暂时间内,基本上有两个处理器为它工作,一个执行程序代码,一个传送数据。利用DMA传送数据的另一个好处是,数据直接在源地址和目的地址之间传送,不需要中间媒介。如果通过CPU把一个字节从适配卡传送至内存,需要两步操作。首先,CPU把这个字节从适配卡读到内部寄存器中,然后再从寄存器传送到内存的适当地址。DMA控制器将这些操作简化为一步,它操作总线上的控制信号,使写字节一次完成。这样大大提高了计算机运行速度和工作效率。
计算机发展到今天,DMA已不再用于内存到内存的数据传送,因为CPU速度非常快,做这件事,比用DMA控制还要快,但要在适配卡和内存之间传送数据,仍然是非DMA莫属。要从适配卡到内存传送数据,DMA同时触发从适配卡读数据总线(即I/O读操作)和向内存写数据的总线。激活I/O读操作就是让适配卡把一个数据单位(通常是一个字节或一个字)放到PC数据总线上,因为此时内存写总线也被激活,数据就被同时从PC总线上拷贝到内存中。
直接内存访问(DMA)方式是一种完全由硬件执行I/O交换的工作方式。DMA控制器从CPU完全接管对总线的控制。数据交换不经过CPU,而直接在内存和I/O设备之间进行。DMA控制器采用以下三种方式:
①停止CPU访问内存:当外设要求传送一批数据时,由DMA控制器发一个信号给CPU。DMA控制器获得总线控制权后,开始进行数据传送。一批数据传送完毕后,DMA控制器通知CPU可以使用内存,并把总线控制权交还给CPU。
②周期挪用:当I/O设备没有 DMA请求时,CPU按程序要求访问内存:一旦 I/O设备有DMA请求,则I/O设备挪用一个或几个周期。
③DMA与CPU交替访内:一个CPU周期可分为2个周期,一个专供DMA控制器访内,另一个专供CPU访内。不需要总线使用权的申请、建立和归还过程。
12.内存碎片产生原因及解决办法
内存碎片通常分为内部碎片和外部碎片:
1.内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片,通常内部碎片难以完全避免;
2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
现在普遍采用的段页式内存分配方式就是将进程的内存区域分为不同的段,然后将每一段由多个固定大小的页组成。通过页表机制,使段内的页可以不必连续处于同一内存区域,从而减少了外部碎片,然而同一页内仍然可能存在少量的内部碎片,只是一页的内存空间本就较小,从而使可能存在的内部碎片也较少
13.UART
UART
14.IIC
IIC
15.SPI
SPI
16.字和字节
总线一般被设计来传输固定大小的一块数据,这块数据被称为字(word),一个字包含的字节数(即字的大小)是各种计算机系统里面的基本参数,而且这个参数在不同的系统里通常是不同的。大多数的现代计算机系统里面,一个字要么是4个字节(32位),要么是8个字节(64位).
结论:
一个字等于多少个字节,与系统硬件(总线、cpu命令字位数等)有关,不应该毫无前提地说一个字等于多少位。
正确的说法:
(1):1字节(byte)= 8位(bit)
(2):在16位的系统中(比如8086微机) 1字 (word)= 2字节(byte)= 16(bit)
在32位的系统中(比如win32) 1字(word)= 4字节(byte)=32(bit)
在64位的系统中(比如win64)1字(word)= 8字节(byte)=64(bit)
17.程序什么时候应该使用线程,什么时候单线程效率高
1)耗时的操作使用线程,提高应用程序响应
2)并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
3)多CPU系统中,使用线程提高CPU利用率
4)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独
立的运行部分,这样的程序会利于理解和修改。
其他情况都使用单线程。
18.winsock建立连接的主要实现步骤
答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()
等待客户端连接。
客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(
),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock
et()关闭套接字。
19.什么是动态链接和静态链接
静态链接:把所有的.o文件链接成一个可执行文件的过程。在程序执行之前完成所有的链结工作。
缺点就是:一个可执行程序里面可能包含某个函数或结构的多个副本,浪费空间。
动态链接:把程序按照模块拆分成几个相对独立的部分,在程序运行时才把他们链接在一起。程序用到哪个函数就去加载哪个模块,节省空间资源。
动态的缺点:每次执行程序都需要进行链接,性能会有一定的损失。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终的EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
采用动态链接库的优点:(1)更加节省内存;(2)DLL文件与EXE文件独立,只要输出接口不变,更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性。
动态链接与静态链接
20.IP组播的优点
组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向 n 个主机发送相同的数据时,发送主机需要分别向 n 个主机发送,共发送 n 次。一个主机用组播协议向 n 个主机发送相同的数据时,只要发送 1 次,其数据由网络中的路由器和交换机逐级进行复制并发送给各个接收方,这样既节省服务器资源也节省网络主干的带宽资源。
与广播协议相比,只有组播接收方向路由器发出请求后,网络路由器才复制一份数据给接收方,从而节省接收方的带宽。而广播方式无论接收方是否需要,网络设备都将所有广播信息向所有设备发送,从而大量占据接收方的接入带宽。
组播可以大大的节省网络带宽,因为无论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播技术的核心就是针对如何节约网络资源的前提下保证服务质量。
总结 近期会整理嵌入式面试相关问题,包含C语言、C++以Linux操作系统。之后会陆续更新基于以上三部分内容中的重点问题进行整理。
如果博客内容有不严谨或错误的部分,请及时评论或私信,本人将第一时间进行补充与更正!

    推荐阅读