嵌入式开发工程师面试题 – C语言部分

嵌入式开发工程师面试题 – C语言部分 

申明:来源于嵌入式开发工程师面试题 – C语言部分 和 网摘)!

1.内核的裁剪怎么裁剪

解析:在linux 下的ubuntu 上面做内核的裁剪,主要讲讲在UI界面做menuconfig 中配置

下面就是UI界面的简介

详细介绍内核配置选项及删改情况 
第一部分:全部删除 
Code maturity level options ---> 代码成熟等级选项 
[]Prompt for development and/or incomplete code/drivers 默认情况下是选择的,这将会在设置界面中显示还在开发或者还没有完成的代码与驱动.不选。

 
第二部分 :除以下选项,其它全部删除 
General setup—〉 
System V IPC (IPC:Inter Process Communication)是组系统调用及函数库,它能让程序彼此间同步进行交换信息。某些程序以及DOS模拟环境都需要它。为进程提供通信机制,这将使系统中各进程间有交换信息与保持同步的能力。有些程序只有在选Y的情况下才能运行,所以不用考虑,这里一定要选。

 
第三部分:除以下选项,其它全部删除 

Loadable module support ---> 可引导模块支持 建议作为模块加入内核 
[] Enable loadable module support 这个选项可以让你的内核支持模块,模块是什么呢?模块是一小段代码,编译后可在系统内核运行时动态的加入内核,从而为内核增加一些特性或是对某种硬件进行支持。一般一些不常用到的驱动或特性可以编译为模块以减少内核的体积。在运行时可以使用modprobe命令来加载它到内核中去(在不需要时还可以移除它)。一些特性是否编译为模块的原则是,不常使用的,特别是在系统启动时不需要的驱动可以将其编译为模块,如果是一些在系统启动时就要用到的驱动比如说文件系统,系统总线的支持就不要编为模块了,否在无法启动系统。 
[]Automatic kernel module loading 一般情况下,如果我们的内核在某些任务中要使用一些被编译为模块的驱动或特性时,我们要先使用modprobe命令来加载它,内核才能使用。不过,如果你选择了这个选项,在内核需要一些模块时它可以自动调用modprobe命令来加载需要的模块,这是个很棒的特性,当然要选Y喽。 
第四部分:全部删除 
Block layer-----〉块设备 


第五部分:除以下选项,其它全部删除 
Processor type and features ---> 处理器类型 
Subarchitecture Type (PC-compatible) ---> 这选项的主要的目的,是使Linux可以支持多种PC标准,一般我们使用的PC机是遵循所谓IBM兼容结构(pc/at)。这个选项可以让你选择一些其它架构。我们一般选择PC-compatible就可以了。 
Processor family(386) : 它会对每种CPU做最佳化,让它跑的好又快,一般来说,你是什么型号的就选什么型号的就好。我选的是386,这样内核会省下不少空间 


第六部分:除以下选项,其它全部删除 
Power management options (ACPI, APM) ---> 电源管理选项 
[ ] Power Management Debug Support 电源管理的调试信息支持,如果不是要调试内核有关电源管理部份,请不要选择这项。 
ACPI Support ---〉高级电源接口配置支持,如果BIOS支持,建议选上这项 
[]Button 这个选项用于注册基于电源按钮的事件,比如power, sleep等,当你按下按钮时事件将发生,一个守护程序将读取/proc/acpi/event,并执行用户在这些事件上定义的动作比如让系统关机。可以不选择,根据自己的需求。

 
第七部分:除以下选项,其它全部删除 
Bus options (PCI, PCMCIA, EISA, MCA, ISA) ---> 总线选项 
[]PCI support 
PCI access mode (Any) ---> PCI外围设备配置,强列建议选Any,系统将优先使用MMConfig,然后使用BIOS,最后使用Direct检测PCI设备。 


第八部分:除以下选项,其它全部删除 
Executable file formats ---> 
Kernel support for ELF binaries ELF是开放平台下最常用的二进制文件,它支持不同的硬件平台。一定要选。 
第九部分:除以下选项,其它全部删除 
Networking 
Networking options ---> 
[]Unix domain sockets 
[]TCP/IP networking 
第十部分:除以下选项,其它全部删除 
Device Drivers --->设备驱动 
Block devices-------〉 
[]Compaq SMART2 support  
[] Compaq Smart Array 5xxx support  
[]Loopback device support 大部分的人这一个选项都选N,因为没有必要。但是如果你要mount iso文件的话,你得选上Y。这个选项的意思是说,可以将一个文件挂成一个文件系统。如果要烧光盘片的,那么您很有可能在把一个文件烧进去之前,看看这个文件是否符合IS09660的文件系统的内容,是否符合您的需求。而且,可以对这个文件系统加以保护。不过,如果您想做到这点的话,您必须有最新的mount程序,版本是在2.5X版以上的。而且如果您希望对这个文件系统加上保护,则您必须有des.1.tar.gz 这个程序。注意:此处与网络无关。建议编译成模块 
[] RAM disk support 
SCSI device support ---> 里面有关于USB支持的,要选择 
[]SCSI device support USB要用,必须选择 
[]legacy /proc/scsi/ support USB要用,必须选择 
[]SCSI disk support USB要用,必须选择 
SCSI Low-level drivers 
[]Serial ATA(SATA) support 
[]Intel PIIX/ICH SATA support  这个必须选择,否则无法产生引导文件 
[]Via SATA support 
Networking device support ---> 这个下面是选网卡驱动,一定要选 
Ethernet(1000mbit)-我的电脑是千兆网卡所以就选这个 
[]broadcom Tigon3support 
Input device support ---> 这个里面要设置你的鼠标键盘什么的 
[]Provide legacy /dev/psaux device 
Graphics support ---> 
[]Support for frame buffer devices 支持Frame buffer的,一定要选择 
USB support ---> 
[]USB device filesystem 这个好象是用U盘必须的 
[]EHCI HCD (USB 2.0) support 有usb2.0就选上把,编译成模块 
[]OHCI HCD support 必须选择,编译成模块 
[]UHCI HCD (most Intel and VIA) support 必须选择,编译成模块 
[]USB Mass Storage support 用U盘必须选择 
USB Human Interface Device (full HID) support 里面选择usb鼠标和usb键盘,如果你有一定选上这个必需选 
HID input layer support 应该选择 
/dev/hiddev raw HID device support如果这里有USB键盘和鼠标选项,一定要选择 

第十一部分:除以下选项,其它全部删除 
file systems --->文件系统 
<*> Second extended fs support  
[*] Ext2 extended attributes  
[*] Ext2 POSIX Access Control Lists  
[*] Ext2 Security Labels  
<M> Ext3 journalling file system support  
[*] Ext3 extended attributes  
[*] Ext3 POSIX Access Control Lists  
[*] Ext3 Security Labels 以上这些肯定是要选择的,linux的标准文件系统 
<M> Kernel automounter support 内核自动挂载的,当然要选 
<M> Kernel automounter version 4 support (also supports v3) 当然要选 
DOS/FAT/NT Filesystems --->  
<M> DOS FAT fs support  
<M> MSDOS fs support  
<M> VFAT (Windows-95) fs support  
<M> NTFS file system support 
Native language support&#61664;语言支持,这里就支持英语和汉语就行了,不多说了 
[]NLS ISO 8859-1 必须选择,这个是关于U盘挂载的。 
CD-ROM/DVD Filesystems ---> 这个是关于挂载ISO文件的,用的话就选。 
<*> ISO 9660 CDROM file system support 
第十二部分: 全部删除 
Instrumentation support  
第十三部分:全部删除 
Kernel hacking --->破解核心?可不是当骸客啦,不选 
第十四部分:全部删除 
Security options ---> 
第十五部分:全部删除 
Cryptographic options --->这是核心支持加密的选项 
第十六部分:全部删除 
Library routines ---> 


  

附: 
内核配置 
  内核配置的方法很多,make config、make xconfig、make menuconfig、make oldconfig等等,它们的功能都是一样的,区别应该从名字上就能看出来,只有make oldconfig是指用系统当前的设置(./.config)作为缺省值。这里用的是make menuconfig。 
  需要牢记:不必要的驱动越多,内核就越大,不仅运行速度慢、占用内存多,在少数情况下、还会引发其他问题。具体步骤如下: 
首先确定shell是bash。 
然后 
$make menuconfig 
有一些默认的符号其含义如下: 
y:加载 
n:不加载 
m:作为模块加载 

可以配置的选项有以下一些: 
1)code maturity level option 代码成熟度 
prompt for development and/or incomplete code/drivers [N/y/?] 
如果有兴趣测试一下内核中尚未最终完成的某些模块,就选y,否则选N,想知道更详细的信息选?会看到联机帮助(以下?的含义相同),N大写表示缺省值。 

2)processor type and features 处理器类型及特性 
Processor family(386,486/Cx486,586/K5/5x86/6x86,Pentium/K6/TSC, PPro/6x86MX)[PPro/6x86MX] 
[]内的是缺省值,我们可以根据前面介绍的uname 命令执行的结果选择。此项如果高于386,那么生成的内核在386机器上将不能启动。 
Math emulation(CONFIG_MATH_EMULATION)[N/y/?] 
需要进行协处理器模拟吗?一般的机器都回n。如果机器已经有硬件的协处理器,那么内核仍将使用硬件,而忽略软件的math-emulation,这将使内核变大变慢。 
MTRR(Memory Type Range Register)support(CONFIG_MTRR)[N/y/?] 
在Pentium、Pro/Pentium II类的系统中可以提高图像写入速度。 
Symmetric multi-processing support(CONFIG_SMP)[Y/n/?] 
如果您的机器有多个处理器,就选y。此时要选中下面的Enhanced Real Time Clock Support 

3)loadable model support 可加载模块支持 
Enable loadable module support(CONFIG_MODULES)[Y/n/?] 
最好选y,不然许多仅供动态加载的模块就不能用了。 
Set version information on all symbols for modules(CONFIG_MODVERSIONS)[N/y/?] 
选N 
Kernel module loader(CONFIG_KMOD)[N/y/?] 

4)general setup 一般设置 
Networking support(CONFIG_NET)[Y/n/?] 
选y吧,现在还有几台计算机不用上网呢? 
PCI support (CONFIG_PCI)[Y/n/?] 
PCI 总线和设备总该有吧。 
PCI access mode(BIOS,Direct,Any)[Any] 
缺省值比较保险,但如果您对您的主板很有信心,就选BIOS。 
PCI quirks (CONFIG_PCI_QUIRKS)[Y/n/?] 
用于修补BIOS中对PCI有影响的BUG,同样,如果您对主板很有信心,就选n。 
Backward-compatible /proc/pci〉(CONFIG_PCI_OLD_PROC)[Y/n/?] 
以前的内核使用/proc/pci,新版内核使用/proc/bus/pci,要保持兼容性就选y。 
MCA support(CONFIG_MCA)[N/y/?] 
查看帮助吧。 
SGI Visual Workstation support(CONFIG_VISWS)[N/y/?] 
您的机器是SGI的吗?是就选y。 
System V IPC(CONFIG_SYSVIPC)[Y/n/?] 
进程间通信函数和系统调用。Linux内核的五大组成部分之一,一定要选。 
BSD Process Accounting(CONFIG_BSD_PROCESS_ACCT)[N/y/?] 
用于启动由内核将进程信息写入文件的用户级系统调用。就看您想不想用它了。 
Sysctl support(CONFIG_SYSCTL)[Y/n/?] 
在内核正在运行的时候修改内核。用8KB空间换取某种方便。别选吧,除非你真的想试试。 
Kernel support for a.out binaries(CONFIG_BINFMT_AOUT)[Y/m/n/?] 
为了能使用以前编译的程序,选y。 
Kernel support for ELF binaries(CONFIG_BINFMT_ELF)[Y/m/n/?] 
为了能使用现在编译的程序,选y。 
Kernel support for MISC binaries(CONFIG_BINFMT_MISC)[Y/m/n/?] 
一般选y,用于支持java等代码的自动执行。 
Parallel port support(CONFIG_PARPORT)[N/y/m/?] 
并口设备,如打印机。 

5)plug and play support 即插即用设备支持 
Plug and Play support (CONFIG_PNP)[N/y/?] 
选y吧。 

6)block devices 块设备 
Normal PC floppy disk support(CONFIG_BLK_DEV_FD)[Y/m/n/?] 
一般的软驱。选y。 
Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support(CONFIG_BLK_DEV_IDE)[Y/m/n/?] 
这几种接口的硬盘、光驱、磁带、软驱。选y。 
Include IDE/ATAPI CDROM support(CONFIG_BLK_DEV_IDECD)[Y/m/n/?] 
CDROM。选y。 

7)networking options 网络选项 
Packet socket (CONFIG_PACHET)[Y/m/n/?] 
按照目前网络发展的状况,选y比较好。当然也可以选其它的。 
Kernel/User netlink socke(CONFIG_NETLINK)[N/y/?] 
内核与用户进程双向通信。选y。 
Network firewalls(CONFIG_FIREWALL)[N/Y/?] 
如果真的需要用防火墙,就选y。 
UNIX domain sockets(confgi_unix)[Y/m/n/?] 
socket 的用处太多了。选y。 
TCP/IP networking(CONFIG_INET)[Y/n/?] 
选y,理由如上一条。 
The IPX protocol (CONFIG_IPX)[N/y/m/?] 
其实并没有那么多人真的需要使用或者学习IPX,所以一般选N。 
Appletalk DDP(CONFIG_ATALK)[N/y/m/?] 
选N,理由同上。 

8)SCSI support SCSI支持,SCSI low-level drives SCSI低级驱动 
根据系统中SCSI设备的实际情况选择。 

9)Networking device support 网络设备支持 
如果用LAN上网,就选择网卡; 
如果用MODEM拨号上网,就要看ISP提供那种服务了,一般都是PPP。 

10)Amateur Radio support 业余收音机支持 
这是什么我不太清楚,所以选N。 

11)ISDN subsystem ISDN子系统 
好像已经有支持ISDN的MODEM了,所以最好先看看自己的MODEM是不是这种,再做选择。 

12)Old CD-ROM dfivers (not SCSI, not IDE) 老式光驱驱动 
一般选N,因为这种设备实在很少见。 

13)Character devices 字符设备 
Virtual terminal(CONFIG_VT)[Y/n/?] 
Linux上一般可以用Alt+F1/F2/F3/F4来切换不同的任务终端,即使在一台计算机上也可以充分使用Linux的多任务能力,一些需要以命令行方式安装合适用的软件如果有虚拟终端的支持就会更方便,因此选y。 
Support for console on virtual terminal(CONFIG_VT_CONSOLE)[Y/n/?] 
选y将支持一个虚拟终端作为控制台。一般为Alt+F1。 
Support for console on serial port(CONFIG_SERIAL)[Y/m/n/?] 
除非真的需要一个串口控制台,否则选n。 
Extended dumb serial driver options(CONFIG_SERIAL_EXTENDED)[N/y/?] 
如果希望使用"dumb"的非标准特性(如HUB6支持),选y,一般选N。 
Non-standard serial port support(CONFIG_SERIAL_NONSTANDARD)[N/y/?] 
非标准串口。一般选N。 
UNIX98 PTY support(CONFIG_UNIX98_PTYS)[Y/n/?] 
PTY指伪终端,一般用户就选n。但如果想用telnet或者xterms作为终端访问主机,并且已经安装了glibc2.1,就可以选y。 
Maximum number of UNIX98 PTYs in use(0-2048)(CONFIG_UNIX98_PTY_COUNT)[256] 
缺省值就可以了。 
Mouse Support(not serial mice)(CONFIG_MOUSE)[Y/n/?] 
PS/2等非串口鼠标选y,否则选N。 

14)Mice 鼠标 
根据自己的鼠标类型选择。 

15)Video for Linux Linux视频 
根据系统中的音/视频捕捉设备选择。 

16)Joystick support 操纵杆 
根据系统中的游戏杆设备选择 

17)Ftape,the floopy tape device driver Ftape设备驱动 
Ftape (QIC-80/Travan)support(CONFIG_FTAPE)[N/y/m/?] 
如果系统中有磁带机,选y。 

18)Filesystems 文件系统 
文件系统的选择要比较仔细,因为其中的一些给某些系统功能提供支持。而且除了proc、ext2等文件系统之外,其它的文件系统(包括下面的网络文件系统)都可以选择为m方式,从而减小内核启动时的体积。 
Quota support(CONFIG_QUOTA)[N/y/?] 
用于给用户划分定量的磁盘空间。如不用此功能就选N。 
DOS FAT fs support(CONFIG_FAT_FS)[N/y/m/?] 
为内核提供FAT支持,多数用户有可能从Linux访问同一系统中的WINDOWS硬盘空间,因此最好选y。 
ISO 9660 CDROM filesystem support(CONFIG_ISO9660_FS)[Y/m/n/?] 
有标准光驱的系统应该选Y。 
Minix fs support(CONFIG_MINIX_FS)[N/y/m/?] 
用于创建启动盘的文件系统,多数应该选y或者m。 
/proc filesystem support(CONFIG_PROC_FS)[Y/n/?] 
虚拟文件系统,必须选Y。 
Second extended fs support(CONFIG_EXT2_FS)[Y/m/n/?] 
Linux标准文件系统,都应该选Y。 

19)Network file systems 网络文件系统 
Coda filesystem support (advanced network fs)(CONFIG_CODA_FS)[N/y/m/?] 
先看帮助再选。 
NFS filesystem support(CONFIG_NFS_FS)[Y/m/n/?] 
选Y或n,能够访问远程NFS文件系统。 
SMB filesystem support(to mount WfW shares etc.)(CONFIG_SMB_FS)[N/y/m/?] 
要访问WINDOWS系统中的共享资源选y。 
NCP filesystem support(to mout NetWare volumes)(CONFIG_NCP_FS)[N/y/m/?] 
如果真的需要访问NetWare文件系统,就选y或者m。 

20)Partion Types 分区类型 
一般用不上;要用请参看帮助。 

21)Console drivers 控制台驱动 
VGA text console(CONFIG_VGA_CONSOLE)[Y/n/?] 
用VGA模式下用文本方式操作Linux,一般选y。 
Video mode selection support(CONFIG_VIDEO_SELECT)[N/y/?] 
大多数系统都不需要这项功能。 

22)Sound 声音 
Sound card support(CONFIG_SOUND)[N/y/m/?] 
如果系统中安装了声卡,就选y(或者m),然后查看帮助。 

23)Kernel hacking 内核监视 
kernel hacking往往会生成非常大或者非常慢(甚至又大又慢)的内核,甚至会引起内核工作不稳定。如果一定要选,那么也最好不要选其中的"development"、"experimental"、"debugging"项。


2 深入理解uboot


解析:Uboot是嵌入式系统中最常用的bootloader,

1什么是ubuoot

uboot可以再很多种cpu架构上运行,同时也支持很多开发板,但是每种cpu架构之间有差别,或者开发板的资源不同,假如在某款开发板上能正常引导启动操作系统的话,并不意味着在其他款就能引导启动,建立一款统一的bootloader几乎是不可能的,但是经过大师们的努力,能够实现通过简单的配置改动,就可以实现引导启动很多操作系统(也就是bootloader移植)(uboot是bootloader中的一种,因为其源码是公开的,广受欢迎)!

2 uboot的体系结构


1 目录树


|--board
|--common
|--cpu
|--disk
|--doc
|--drivers
|--dtt
|--examples
|--fs
|--include
|--lib_arm
|--lib_generic
|--net
|--post
|--rtc
|--tools


1. board:和一些已有开发板有关的文件. 每一个开发板都以一个子目录出现在当前目录中,比如说: leopard2a子目录中存放与我们开发板相关的配置文件.
2. common:实现uboot命令行下支持的命令,每一条命令都对应一个文件。例如bootm命令对应就是cmd_bootm.c。
3. cpu:与特定CPU架构相关目录,每一款Uboot下支持的CPU在该目录下对应一个子目录,比如有子目录arm926ejs就是我们开发板上使用的cpu架构目录。
4. disk:对磁盘的支持。
5. doc:文档目录。Uboot有非常完善的文档,推荐大家参考阅读。
6. drivers:Uboot支持的设备驱动程序都放在该目录,比如各种网卡、支持CFI的Flash、串口和USB等。
7. fs: 支持的文件系统,Uboot现在支持cramfs、fat、fdos、jffs2和registerfs。
8. include:Uboot使用的头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的文件。该目录下configs目录有与开 发板相关的配置头文件,如leopard2a.h。该目录下的asm目录有与CPU体系结构相关的头文件,asm对应的是asmarm.
9. lib_xxxx: 与体系结构相关的库文件。如与ARM相关的库放在lib_arm中。
10. net:与网络协议栈相关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。
11. tools:生成Uboot

的工具,如:mkimage, crc等等

2、uboot的运行过程分析


2.1 启动模式介绍

大多数 Boot Loader 都包含两种不同的操作模式:"启动加载"模式和"下载"模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。

启 动加载(Boot loading)模式:这种模式也称为"自主"(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 BootLoader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。

下载(Downloading)模 式:在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被 BootLoader 保存到目标机的 RAM 中,然后再被 BootLoader 写到目标机上的FLASH 类固态存储设备中。BootLoader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 BootLoader 的这种工作模式。工作于这种模式下的 Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。

UBoot这样功能强大的 Boot Loader 同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。

2.2 运行过程

大 多数bootloader都分为阶段1(stage1)和阶段2(stage2)两大部分,uboot也不例外。依赖于CPU体系结构的代码(如CPU初 始化代码等)通常都放在阶段1中且通常用汇编语言实现,而阶段2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。

U - Boot 编译后的代码定义一般不超过100kB ,并且这100 kB 又分成两个阶段来执行. 第一阶段的代码在start . s 中定义,大小不超过10 kB ,它包括从系统上电后在0x00000000 地址开始执行的部分. 这部分代码运行在Flash 中,它包括对arm926ejs的一些寄存器的初始化和将U - Boot 的第二阶段代码从Flash 拷贝到SDRAM 中. 除去第一阶段的代码,剩下的部分都是第二阶段的代码. 第二阶段的起始地址是在第一阶段代码中指定的,被复制到SDRAM后,就从第一阶段跳到这个入口地址开始执行剩余部分代码. 第二阶段主要是进行一些BSS 段设置,堆栈的初始化等工作,最后会跳转到main -loop 函数中,接受命令并进行命令处理. 图1 给出了U - Boot 的详细的运行过程包括对内核的设置、装载及调用过程。
  • 系统复位进入u-boot stage l的入口点
  • 硬件设备的初始化
  • 为加载uboot stage 2准备ram空间
  • 设置好堆栈
  • 跳转到stage 2的C入口点
  • 初始化本阶段要用到的设备
  • 检查内存映射
  • 将kernel映像和文件映像从flash中读到ram中
  • 为内核设定启动参数
  • 调用内核
      
2.3 本开发板的地址分布(leopard2a)

本目标板是RAM 16M, Flash 8M,具体空间如图所示:

1F12FFFF
Uboot
Uboot_env
Kernel
Rootfs
1F000000
1F020000
1F01FFFF
1F02FFFF
1F030000
1F130000
1F7EFFFF
Kernel
Rootfs
FLASH
SDRAM
0x00030000
0x00130000
0x00430000

可以根据变换后的分区结构,设置
uboot_addr,uboot_addr_end,kernel_addr,kernel_addr_end,rootfs_addr,rootfs_addr_end,
config_addr, config_addr_end等环境变量,调整bootloader。 

SDRAM的调整修改linux-2.4.20_mvl31/drivers/mtd/maps/physmap.c

2.4 运行代码分析

2.4.1 stage 1 

uboot的stage1代码通常放在start.s文件中,它用汇编语言写成,其主要代码包括定义入口,设置异常向量,设置cpu的模式和频率,配置内存区控制寄存器,安装uboot的栈空间,关闭看门狗等。由于本人对ram的汇编不太熟悉,所以这一部分不作具体分析。

2.4.2 stage 2

lib_arm/board.c中的start armboot是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个uboot(armboot)的主函数,该函数主要完成如下操作:

2.4.2.1 调用一系列初始化函数

1. 指定初始函数表:
init_fnc_t *init_sequence[] = {
cpu_init, /* cpu的基本设置 */
board_init, /* 开发板的基本初始化 */
interrupt_init, /* 初始化中断 */
env_init, /* 初始化环境变量 */
init_baudrate, /* 初始化波特率 */
serial_init, /* 串口通讯初始化 */
console_init_f, /* 控制台初始化第一阶段 */
display_banner, /* 通知代码已经运行到该处 */
dram_init, /* 配制可用的内存区 */
display_dram_config,
#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)
checkboard,
#endif
NULL,
};

执行初始化函数的代码如下:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
       if ((*init_fnc_ptr)() != 0) {
         hang ();
       }
}

2. 配置可用的Flash区
flash_init ()

3. 初始化内存分配函数
mem_malloc_init()

4. nand flash初始化
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:");
nand_init();        /* go init the NAND */
#endif 

5. 初始化环境变量
env_relocate ();

6. 外围设备初始化
devices_init()

7. I2C总线初始化
i2c_init();

8. LCD初始化
drv_lcd_init();

9. VIDEO初始化
drv_video_init();

10. 键盘初始化
drv_keyboard_init();

11. 系统初始化
drv_system_init();

2.4.2.2 初始化网络设备

初始化相关网络设备,填写IP、MAC地址等。

1. 设置IP地址
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

2. 设置mac地址 
{
       int i;
       ulong reg;
       char *s, *e;
       uchar tmp[64];

       i = getenv_r ((uchar*)("ethaddr"), tmp, sizeof (tmp));
       s = (i > 0) ? (char*)tmp : NULL;

       for (reg = 0; reg < 6; ++reg) {
         gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
         if (s)
            s = (*e) ? e + 1 : e;
       }
}

2.4.2.3 进入主UBOOT 命令行

进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
for (;;) {
       main_loop ();
}}

3、uboot的移植和测试


3.1 移植的过程

① 在宿主机上建立交叉编译开发环境
② 修改cpu/arm926ejst目录中的文件内容,
主要包含cpu.C,start.S,interrupts.C以及seria1.C,speed.C等文件 
③ 在board目录下创建自己的目标板(开发板)目录leopard2a
在目录下创建leopard2a.C,flash.C,memsetup.S
以及Makefile,u-bot.1ds,config.mk文件
④ 在include/configs目录下创建leopard2a.h 
⑤ 打开u-bot目录下Makefile文件,加入如下两行:
leopard2a_config :    unconfig
@./mkconfig $(@:_config=) arm arm926ejs leopard2a
⑥ 编译。运行命令:
1. make leopard2a_config
2. make
编译成功.生成基本的u—b00t.
⑦ 烧写.把编译成的u-bot.bin
至此移植u-bot过程结束.

3.2 移植主要修改的文件

移植u—boot到开发板上只需要修改和硬件相关的代码即可。这首先就联想到cpu目录下的启动代码,另外参考u—boot/readme文件可知其他还需要修改的主要文件有:

Makefile文件,和include目录下的目标板.h头文件(leopard2a.h),board目录下的目标板.C文件(leopard2a.c),flash.C文件,u-boot.1ds链接文件,以及cpu目录下的串口驱动文件。

具体修改如下:
① cpu/arm926ejst目录下
  • start.S启动代码。
② board/leopard2a
  • leopard2a.C文件。这个文件主要是SDRAM 的驱动程序,主要完成SDRAM 的UPM 表设置,上电初始化。暂时不改。
  • flash.C文件。Flash的驱动程序就在此文件中。
  • memsetup.S文件。
  • config.mk文件.此文件用于设置程序链接的起始地址。
  • u-boot.Ids文件。
③ include/configs目录下leopard2a.h文件.此文件是leopard2a目标板头文件,大多数寄存器参数是在这一文件中设置完成的.

3.3 uboot网络下载功能的添加和RAM调试

在commom/main.c 中的main_loop函数中添加tftp下载的函数,可以通过按钮触发下载rimage,kimage。
在烧录u-boot.bin之前,需要进行ram调试,保证uboot可以在EVB上正常运行。
先下載U-boot 到SDRAM上, 然後執行SDRAM 上的U-boot 程序, 以確認U-boot可以正常執行, Command 如下:
1. “tftp a00000 u-boot.bin” <= 下載程序到SDRAM
2. “go a00000” <= 從SDRAM 執行程序

如果U-boot 可以相容於目前的硬件, 5VT EVB 會重新正常啟動,要是不能正常啟動,表示U-boot 不相容於目前的硬件, 請更換新的u-boot。再重新測試, 直到被測試的U-boot可以正常啟動


3.TCP/IP TCP/UDP原理是什么?

解析:
TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,我们这里只做简单、形象的介绍,你只要做到能够理解这个过程即可
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去! UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。
tcp协议和udp协议的差别 
               TCP        UDP 
是否连接      面向连接     面向非连接 
传输可靠性    可靠         不可靠 
应用场合     传输大量数据  少量数据 
速度         慢           快

4.项目里你做了些什么?怎么做的?为什么要那样做?

解析:具体项目而定!

5.指针与数组的用法及计算

6.单链表和双链表的用法

解析:

单链表就是单向访问,双向链表就是可以双向访问
1、单链表是在元素的节点结构中只能包含一个后继结点指针,不能包含多个指针的。双链表则是包含前驱和后继两个指针的。
2、单链表要求建好后返回第一个节点的指针(或者有头结点用头结点的指针),因为他只能朝后运行,而双链表建好后可以给任意一个节点的指针,因为他可以朝前后两个方向走。知道哪个节点的指针没有多大关系。原则上以第一个节点为

7.虚函数,构造函数,析构函数,复制和赋值函数


解析:这是涉及到所有基础的知识,不熟悉,可参考c++ primerplus 书籍
面试的时候 ,比较喜欢给出头文件,写函数,下面给出函数和说明:
  class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};

String::String(const char *str)//普通构造函数
{
 if(NULL == str)
 {
  m_data = new char[1];
  m_data[0] = ‘\0‘;
 }
 else
 { 
  m_data = new char[strlen(str)+1];
  strcpy(m_data,str);
 }
}

String::~ Stirng(void)//析构函数
{
 delete []m_data;
// delete m_data;
}

String::String(const String &other) //拷贝构造函数
{ 
 m_data = new char[strlen(other.m_data)+1];
 strcpy(m_data,other.m_data); 
}

String & String::operate=(const String &other) //赋值语句
{
 if(this == &other)
 { 
  return *this;
 } 
 delete []m_data;
 
 m_data = new char[strlen(other.m_data)+1];
 strcpy(m_data,other.m_data);
 return *this;
}

说明:
1)构造函数是一种特殊函数,而拷贝构造函数是一种特殊的构造函数。类X的构造函数的第一个参数必须为X&,或者const X&;除了第一个参数外,构造函数要么不存在其他参数,如果存在其他参数,其他参数必须有默认值。一个类可以有多个拷贝构造函数。
      它的形式如下:   X::X(X& x)   X::X(const X& x)   X::X(X& x, int a = 0, int b = 1…) 
2)什么时候会调用拷贝构造函数?  
      以下三种情况出现时,会调用一个类的拷贝构造函数: 
       a) 用一个已经实例化了的该类对象,去实例化该类的另外一个对象;  
       b) 用该类的对象传值的方式作为一个函数的参数;  
       c) 一个函数返回值为该类的一个对象。
        例如:
                CA a;            
              // CA b();   // 不能用这种方式声明CA的对象b!           
               CA c(10, 10);             
               CA d(c);  // 情况1) -> 调用拷贝构造函数            
               int anInt = someFun1(c);   // 情况2) -> 调用拷贝构造函数           
               CA e = someFun2(11, 11); // 情况3) -> 调用拷贝构造函数
3)什么时候必须要显式声明拷贝构造函数?
         如果有成员变量以指针形式存在,涉及动态内存分配等情况下,一定要显式声明拷贝构造函数。要注意到,如果需要显式定义拷贝构造函数,那么  通常都是需要同时定义析构函数(因为通常涉及了动态内存分配),至于是否必须重载操作符“=”,要视情况而定。
        例如:
               class CA  { 
                 public:          
                        Point* _point;   
                 public:   
                       CA(const Point*);     
                       // 需要增加的拷贝构造函数           
                       CA(const CA&);           
                       // 需要增加的析构函数           
                       virtual ~CA();           
                       // 需要增加的拷贝赋值函数           
                       CA& operator = (const CA&); 
               };
               int main(void)  {            
                      Point apoint(1, 2);           
                     CA ca(&apoint);           
                     ca.printCoordinates();             
                     CA cb(ca);    // 调用拷贝构造函数
                     CA cc = cb; // 调用拷贝赋值函数
                     ..............
               }
4)缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数是特殊成员函数。
5)构造函数不能有返回类型,也不能由virtual, const, static 和 volatile来修饰。但可以由inline来修饰,事实上隐式构造函数就是用inline来修饰的。inline表示编译时展开,通常速度块;virtual表示运行时绑定,通常意味着灵活。
6)类中存在虚函数或者有虚基类的情况下需要显式声明构造函数。拷贝构造函数也是如此。

9.冒泡排序

解析:参考冒泡排序 --学习(二)




嵌入式开发工程师面试题 – C语言部分,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。