嵌入式笔试面试问题积累

问题(欢迎补充和提供答案)

1.高级IO和低级IO的区别?
  • 大多数文件IO只需要5个函数:open、read、write、lseek以及close。低级IO也称为不带缓冲的I/O,不带缓冲指的是每个I/O函数都调用内核中的一个系统调用。低级IO函数不是ISO C的组成部分,它们是POSIX和single UNIX Specification的组成部分,后者相当于ISO C的超集。
  • 高级IO库可以处理很多细节,例如缓冲区分配,以优化长度执行I/O等,提供缓冲的目的是尽可能减少使用read和write调用的次数,标准IO库提供了三种类型的缓冲:全缓冲、行缓冲和不带缓冲。
2.fp从用户态到内核态在整个操作系统中的调用流程
  • 当打开一个流时,标准IO函数fopen返回一个指向FILE对像的指针。该对象通常是一个结构,它包含了标准IO库为管理该流所需要的的信息,包括:用于实际IO的文件描述符、指向用于该流缓冲区的指针、缓冲区的长度,当前在缓冲区中的字符数以及出错标志等等
  • 当用户空间的open等函数进行内核空间后,开始运行用于实现系统调用的处理程序函数,这些函数的名称前缀为sys_。内核将控制权转移给处理程序例程后,控制流进入平台无关的代码,比如read函数在系统调用函数sys_read之后,会将控制权传递给一个更通用的内核辅助函数vfs_read()函数,这个vfs_read是不依赖与特定CPU或体系结构的通用文件操作函数。处理完在返回结果时,无需特别的操作,简单的return后接返回值即可。
3.pcb,tcb,资源分配,栈共享实现

PCB:进程控制块,主要表示进程状态,它是一个由结构task_struct所定义的数据结构,该结构的内容可以分解为以下几个部分:

  • 状态和执行信息,如待决信号、使用的二进制格式、进程ID号(PID)、到父进程及其他有关进程的指针、优先级和程序执行有关的时间信息。
  • 有关已经分配虚拟内存的信息。
  • 进程身份凭据,如用户ID、组ID以及权限等。
  • 使用的文件包含程序代码的二进制文件、以及进程所处理的所有文件的文件系统信息。
  • 线程信息记录该进程特定于CPU的运行时间数据。
  • 在与其他应用程序协作时所需的进程间通信有关的信息。
  • 该进程所用的信号处理程序,用于响应到来的信号。
    其中进程状态可以是下列值:
    TASK_RUNNING:进程处于可运行状态,这并不意味着已经实际分配了CPU。进程可能会一直等到调度器选中它,该状态确保进程可以立即运行,而无需等待外部事件。
    TASK_INTERRUPTIBLE:针对等待某事件或其他资源的睡眠进程设置的。在内核发送信号给该进程表明事件已经发生时,进程状态变为TASK_RUNNING,它只要调度器选中该进程即可恢复执行。
    TASK_UNINTERRUPTIBLE:用于因内核指示而停用的睡眠进程。它们不能由外部信号唤醒,只能由内核亲自唤醒。
    TASK_STOPPED:表示进程特意停止运行,例如,由调试器暂停。
    TASK_TRACE:本来不是进程状态,用于从停止的进程中,将当前调试的那些(使用ptrace机制)与常规的进程区分开来。

TCB:线程控制块,可以将线程控制块理解为进程控制块的组成和附属。
资源分配:资源分配和调度的基本单位是进程。
栈共享实现:数组有两个端点,两个栈有两个栈顶,让一个栈的栈底为数组的始端,即下标为0处,另一个栈为数组的末端,即下标为数组长度n-1处。
这样,两个栈如果增加元素,就是两端点向中间延伸。top1和top2是栈1和栈2的栈顶指针,只要它们两个不见面,两个栈就可以一直使用。
在这里插入图片描述

4.CAS指的是什么?

CAS是一种无锁的非阻塞算法的实现。它是一种乐观锁技术,可以通过碰撞检测来确定是否实时更新被其他方面的干扰,在这种情况下,操作失败可以失败或重试。而悲观锁技术要求保证没有其他线程将在某些操作之间干扰。

5.程序为什么从main函数开始,谁调用的,没有操作系统又是谁调用的

main只是开发工具所规定的一个特殊函数名称而已。它既不是程序的入口,也不是必须要有的函数。 程序的入口点记录在可执行文件中的一个数据,该数据标明程序从哪个位置开始执行,这个数据是连接程序的时候由link.exe确定的,可以把程序的入口点 指定为任意函数,甚至可以自己编辑可执行文件修改程序的入口点。在默认情况下,link.exe会连接开发工具中带有的一个obj文件,并设置该obj中 的固定函数为程序的入口点,这个函数执行后会做一些初始化的事情,然后调用main函数。在执行连接的时候,如果不连接这个obj,程序中就可以没有 main函数。
在操作系统下调用main函数的过程如下:
(1)内核加载可执行文件,并建立text/data/bss/stack。此外,内核为参数和环境变量分配页,并将它们压入用户模式栈。
(2)GCC通过crtbegin.o/crtend.o/gcrt1.o来建立程序。另外的默认库默认是动态链接的。可执行文件的开始地址是_start的地址。
(3)控制传递给_start以后,_start从由内核设置的栈中获取参数和环境变量信息,然后调用__libc_start_main。
(4)__libc_start_main初始化必要的数据结构,尤其是C库(比如malloc)和线程环境,然后调用用户的main函数。值得注意的是,__libc_start_main认为main函数的签名是:
int main(int argc, char ** argv, char ** env)。
(5)main函数的返回值由__libc_start_main接收,并传递给exit。
没有操作系统时,从CPU角度来看,将要执行的指令地址放在程序计数器(PC)里,一个程序需要执行,总需要一个入口地址。通用的可执行文件格式(ELF/PE)总会指定一个入口地址。
参考如下链接:
C语言必须从main函数开始执行吗,main函数之后还可以执行其他语句吗?
为什么C程序一定要从main开始执行?

6.写一个管道通信的例子

管道是由调用pipe函数而创建的:

#include <unistd.h>
int pipe(int filedes[2]);

其中,由filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。写一个创建从父进程到子进程的管道,并且父进程由该管道向子进程传输数据的例子。

#include <unistd.h>
#include <stdio.h>
int main()
{
	int n;
	int fd[2];
	pid_t pid;
	char line[20];
	if (pipe(fd) < 0) {
		printf("pipe error!\n");
	}
	if ((pid = fork())<0) {
		printf("fork error!\n");
	} else if (pid >0) {
		close(fd[0]);
		write(fd[1], "Hello world!\n",14);
	} else {
		close(fd[1]);
		n = read(fd[0], line, 14);
		write(STDOUT_FILENO, line, n);
	}
	return 0;
}

7.用过什么面向对象的知识,举例说明
8.介绍ARM体系

ARM——体系架构

9.指令集哪几种,流水线工作方式,为什么流水线好?

ARM指令集分为跳转指令、数据处理指令、程序状态寄存器(PSR)指令、加载/存储指令、协处理指令和异常产生指令。
ARM 流水线技术

10.中断机制,有哪些中断种类,怎么中断,为什么会有中断?

中断可以分为如下两种类别:

  • 同步中断和异常:由CPU自身产生,针对当前执行的程序。异常可能因种种原因触发:由于运行时发生的程序设计错误(除0),或由于出现了异常的情况和条件,致使处理器需要外部的帮助才可处理。
  • 异步中断:由外部设备产生,可能发生在任意时间。

如果CPU当前不处于核心态,则发起从用户态到核心态的切换。接下来,在内核中执行一个专门的例程,称为中断服务例程。

11.Linux内核内存管理,进程调度及同步的一些问题?
12.IIC总线起始和终止信号,IIC仲裁

13.OSI协议栈和TCP协议栈
在这里插入图片描述
OSI(Open Systems Interconnection)开放系统互联模型将网络划分为7层。TCP/IP模型将其合并为4层,每层都只能与紧邻的层通信。各层执行的任务如下:
主机到网络层负责将信息从一台计算机传输到远程计算机。如果几台计算机共享同一传输线路,网络接口卡必须有一个唯一的ID号,称之为MAC地址。
网络层指在网络中的任何计算机之间交换数据的任务,所述计算机不一定是直接连接的。网络层还负责将传输的数据划分为特定长度的分组,负责分配网络中唯一的地址,以便计算机可以彼此通信。
传输层任务是在两个建立了链路的计算机上,控制应用程序之间的数据传输。
应用层表示从应用程序视角来看的网路连接。在两个应用程序之间建立通信连接之后,应用层负责传输实际的内容。

14.应用层协议有哪些?

HTTP、FTP等。

15.TCP和UDP有什么区别?

UDP用于发送数据报,TCP可以建立安全的、面向连接的服务。
从UDP的分组结构可以看出,其中只包括了数据长度和净菏的内容。而TCP的分组结构中包括了序列号、控制标志等内容。序列号制定了TCP分组在数据流中的位置,控制标志包括urg(紧急)、ack(确认)、psh(推)、rst(重置)、syn(同步)和fin。用于检查、建立和结束连接。

16.http怎么实现?

HTTP是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。实现方式参照三次握手过程。
客户端进程的套接字状态为CLOSED,而服务器套接字的状态是LISTEN。建立TCP连接的过程需要交换3个TCP分组,因而称为三次握手。操作如下:

  • 客户端通过向服务器发送SYN来发出连接请求。客户端的套接字状态由CLOSED变为SYN_SENT。
  • 服务器在一个监听套接字上接收到连接请求,并返回SYN和ACK。服务器套接字的状态由LISTEN变为SYN_RECV。
  • 客户端套接字接收到SYN/ACK分组后,切换到ESTABLISHED状态,表明连接已经建立,一个ACK分组被发送到服务器。
  • 服务器接收到ACK分组,也切换到ESTABLISHED状态。这就完成了两端的连接建立工作,可以开始数据交换。
17.select、poll和epoll的区别

select单个进程可监视的fd数量被限制,能监听的端口数量有限。poll没有最大连接数的限制,但是select和poll需要一直遍历整个fd集合,而epoll会把哪个流发生了怎样的IO事件通知给我们,实际上是事件驱动的。

18.sed,awk命令的用法

sed:过滤和转换文本的流编辑器。
语法:
sed [OPTION]… {script-only-if-no-other-script} [input-file]…
参数说明:
-e script, --expression=script:将脚本添加到要执行的命令中
-f script-file, --file=script-file:将脚本文件的内容添加到要执行的命令中
–follow-symlinks:当在某个位置处理时遵循符号链接
-i[SUFFIX], --in-place[=SUFFIX]:在place处编辑文件
-l N, --line-length=N:为“l”命令指定所需的换行程度
-s, --separate:将文件视为单独的而不是单个连续的长流
-u, --unbuffered:从输入文件中加载最小数量的数据并刷新输出缓冲区更频繁
动作说明:
a\:文本追加文本,每个嵌入的换行符前面都有一个反斜杠。
i\:文本插入文本,每个嵌入的换行符前面都有一个反斜杠。
r filename:追加从文件名读取的文本。
R filename:追加从文件名读取的行。每次调用该命令都会从文件中读取一行。
c\:文本将所选行替换为文本,其中每个嵌入的换行符前面都有一个反斜杠。
d:删除模式空间,开始下一个循环。
D:删除模式空间中的第一条嵌入换行符。开始下一个循环,但如果模式空间中仍有数据,则跳过从输入中读取。
h/H:h复制/追加模式空间以保留空间。
g/G:将保持空间复制/追加到模式空间。
x:交换保持空间和模式空间的内容。
l:以“视觉清晰”的形式列出当前行。
n/N:在模式空间中读取/追加下一行输入。
s/regexp/replacement:尝试将regexp与模式空间匹配。如果成功,则替换与替换匹配的部分。替换可以包含特殊字符&指匹配的模式空间的那部分,特殊转义符1到9指regexp中相应的匹配子表达式。
w filename:将当前模式空间写入文件名。
W filename:将当前模式空间的第一行写入文件名。
参考:Linux sed 命令

在testfile文件的第四行后添加一行,并将结果输出到标准输出,在命令行提示符下输入如下命令:
sed -e 4a\newLine testfile
首先查看testfile中的内容如下:
$ cat testfile #查看testfile 中的内容
HELLO LINUX!
Linux is a free unix-type opterating system.
This is a linux testfile!
Linux test
使用sed命令后,输出结果如下:
$ sed -e 4a\newline testfile #使用sed 在第四行后添加新字符串
HELLO LINUX! #testfile文件原有的内容
Linux is a free unix-type opterating system.
This is a linux testfile!
Linux test
newline
以行为单位的新增/删除
将 /etc/passwd 的内容列出并且列印行号,同时,请将第 2~5 行删除!
[root@www ~]# nl /etc/passwd | sed ‘2,5d’
1 root: x:0:0:root:/root:/bin/bash
6 sync: x:5:0:sync:/sbin:/bin/sync
7 shutdown: x:6:0:shutdown:/sbin:/sbin/shutdown
…(后面省略)…
sed 的动作为 ‘2,5d’ ,那个 d 就是删除!因为 2-5 行给他删除了,所以显示的数据就没有 2-5 行罗~ 另外,注意一下,原本应该是要下达 sed -e 才对,没有 -e 也行啦!同时也要注意的是, sed 后面接的动作,请务必以 ‘’ 两个单引号括住喔!
只要删除第 2 行
nl /etc/passwd | sed ‘2d’
要删除第 3 到最后一行
nl /etc/passwd | sed ‘3,KaTeX parse error: Expected 'EOF', got '#' at position 50: …样! [root@www ~]#̲ nl /etc/passwd…d’
在第二行后(亦即是加在第三行)加上『drink tea?』字样!
[root@www ~]# nl /etc/passwd | sed ‘2a drink tea’
1 root: x:0:0:root:/root:/bin/bash
2 bin: x:1:1:bin:/bin:/sbin/nologin
drink tea
3 daemon: x:2:2:daemon:/sbin:/sbin/nologin
…(后面省略)…
那如果是要在第二行前
nl /etc/passwd | sed ‘2i drink tea’
如果是要增加两行以上,在第二行后面加入两行字,例如 Drink tea or … 与 drink beer?
[root@www ~]# nl /etc/passwd | sed ‘2a Drink tea or …
drink beer ?’
1 root: x:0:0:root:/root:/bin/bash
2 bin: x:1:1:bin:/bin:/sbin/nologin
Drink tea or …
drink beer ?
3 daemon: x:2:2:daemon:/sbin:/sbin/nologin
…(后面省略)…

awk命令:
参照:AWK命令

19.Linux下检查内存状态的命令
  • free命令可以显示系统内存的使用情况,包括物理内存、交换内存和内核缓冲区内存。
  • vmstat是virtual memory statistics(虚拟内存统计)的缩写,可以对操作系统的虚拟内存、进程、CPU活动进行监控,是对系统整体情况的统计。
  • top命令可以查看正在运行的进程和系统负载信息,包括CPU负载、内存使用、各个进程所占系统资源等。
  • cat /proc/meminfo,/proc/meminfo是了解系统内存使用状况的主要接口,“free”、“vmstat”等命令就是通过它获取数据的。
  • ps aux命令可以查看系统中各个进程的运行情况,包括了进程占用的内存。
18.重写memcpy函数需要注意哪些问题?
#include <iostream>
using namespace std;
void *Memcpys(void *dest, const void *src, size_t size);
int main(int argc, char *argv[])
{
	char buf[100]= "abcdefghijklmn";
	Memcpys(buf, buf+3, 4);
	printf("%s\n",buf+3);
	return 0;
}
void *Memcpys(void *dest, const void *src, size_t size)
{
	char *pSrc;
	char *pDest;
	if(src == NULL || dest == NULL)
		return NULL;
	else {
		if(src < dest && ((char*)src + size) > (char*)dest ) {
			pSrc = (char *)src + size -1;
			pDest = (char*)dest +size -1;
			while(size--) {
				*pDest-- = *pSrc--;
			}
		
		}
		else {
			pSrc = (char *)src;
			pDest = (char*)dest;
			while(size--) {
				 *pDest++ = *pSrc--;
			}
		}
	}
	return pDest;	
}

需要注意地址重叠的情况,即内存复制的起始地址加上复制内容的长度大于目的地址时的操作应和其他情况进行区分,这种情况需要从后往前复制。

19.arm和dsp有什么区别

ARM具有比较强的事务管理功能,可以用来跑界面以及应用程序等,其优势主要体现在控制方面,它的速度和数据处理能力一般,但是外围接口比较丰富,标准化和通用性做的很好,而且在功耗等方面做得也比较好,所以适合用在一些消费电子品方面;而DSP主要是用来计算的,比如进行加密解密、调制解调等,优势是强大的数据处理能力和较高的运行速度。由于其在控制算法等方面很擅长,所以适合用在对控制要求比较高的场合,比如军用导航、电机伺服驱动等方面。如果只是着眼于嵌入式应用的话,嵌入式CPU和DSP的区别应该只在于一个偏重控制一个偏重运算了
原文链接:深入了解DSP和ARM的关系(相同与区别)

20.spi总线有几条线

SPI总线有4条线,SPI是一种高速的,全双工的、同步的通信总线,并且在芯片的管脚上只占用4根线。它以主从方式工作,通常有一个主设备和一个或多个从设备。4条线如下:

  • SDO:主设备数据输出,从设备数据输入。
  • SDI:主设备数据输入,从设备数据输出。
  • SCLK:用来为数据通信提供时钟同步信号,由主设备产生。
  • CS:从设备使能信号,由主设备控制。
21.fork调用后有几个返回值,返回哪些值?

fork调用一次有两个返回值:向父进程返回子进程的ID,向子进程中返回0。

22.用过哪些总线协议,写一写串口数据的传输过程,详细说一说项目中使用串口通信时的帧结构,导致串口数据出现错误的原因有哪些,如何避免?
23.中断的执行过程,如果一个子函数既在主函数中被调用,又在中断函数中调用,会出现什么问题,如何避免这些问题的发生?(函数重用)
  • 尽量使用可重用函数,重用理解为一个函数在同时多次调用。在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的。
  • 在进程中断期间,系统会保存和恢复进程的上下文,然而恢复的上下文仅限于返回地址,CPU寄存器之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer等并不在保护之列,所以如果这些值在函数中断期间发生了改变,那么当函数回到断点继续执行时,结果就不可预料了。
  • 打个比方,比如malloc,将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
  • 满足下面条件之一的多数是不可重入函数:
    (1)使用了静态数据结构;
    (2)调用了malloc或free;
    (3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
    (4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现)。
    参考:函数可重入与不可重入
24.用两个栈模拟一个队列

用两个栈模拟实现一个队列的基本思路是:用一个栈作为存储空间,另一个栈作为输出缓冲区,入队时把元素按顺序压入两栈模拟的队列,出队时按入队的顺序出栈即可。

25.对ARM处理器的LDR/STR架构的理解

MOV指令可完成从另一个寄存器、被移位的寄存器或立即数赋值到目的寄存器。但是数据从RAM到CPU寄存器之间的移动只能通过LDR/STR指令来完成,
LDR指令的格式:
LDR{条件} 目的寄存器, <存储器地址>
作用:将存储器地址所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。
举例:

  • ldr r0, 0x12345678, 就是把0x12345678这个地址中的值存放到r0中。
  • ldr r0, [r1, #8], 将地址r1 + 8的字数据读入r0存储器中。
  • ldr r0, [r1], #8 将r1的数据读入到r0,并将r1 + 8的值存入r1。

STR指令的格式:
STR{条件} 源寄存器, <存储器地址>
作用:用亍从源寄存器中将一个字数据传送到存储器中。
举例:

  • str r0, [r1, #8] 将r0中的字数据读入以r1 + 8为地址的存储器中
    这里就是说r1的地址为0x00001000,r0装的是0x00aa,将0x00aa装入0x00001000 +8的地址里,也就是0x00001008地址。

  • str r0, [r1], #8 将r0的字数据读入r1,并将r1 +8的值存入r1.也就是将r0的字数据读入的是以r1中数据为地址的存储器中,比如r1中装的是0x00001000,r0是0x00aa,通俗的说就是把0x00aa读入以0x0001000的地址里面。

原文链接:记录自已学习之ARM汇编语言ldr和str

26.常见的缓存策略如FIFO、LRU等指的是什么?

漫画:什么是LRU算法?讲的挺好的

  • LRU(Least Recently
    Used)算法基于一种假设:长期不被使用的数据,在未来被用到的几率也不大,因此,当数据所占内存达到一定阈值时,我们要移除掉最近最少被使用的数据。
    详细算法如下:
    1.新数据插入到链表头部
    2.每当缓存命中(即缓存数据被访问),则将数据移到链表头部。
    3.当链表满的时候,将链表尾部的数据丢弃。
  • 除了LRU,常见的缓存策略还有另外两种:先进先出策略FIFO、最少使用策略LFU(Least Frequently Used)。
  • FIFO在数据结构上使用队列来实现。
  • LFU算法算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。
    具体实现如下:
    1 新加入数据插入到队列尾部(因为引用计数为1);
    2 队列中的数据被访问后,引用计数增加,队列重新排序;
    3 当需要淘汰数据时,将已经排序的列表最后的数据块删除。
27.brk和sbrk函数的用法

在这里插入图片描述
brk() 和 sbrk() 改变 “program brek” 的位置。
brk和sbrk是malloc的底层实现,用于分配开辟内存,但是brk是系统调用,而sbrk不是,sbrk调用了brk的用法。
用法:

 #include <unistd.h>

       int brk(void *addr);

       void *sbrk(intptr_t increment);
  • brk() sets the end of the data segment to the value specified by
    addr, when that value is reasonable, the system has enough memory, and the process does not exceed its maximum data size.
  • sbrk() increments the program’s data space by increment bytes.
    Calling sbrk() with an increment of 0 can be used to find the current location of the program break.
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值