嵌入式系统--面经

1. Linux系统启动流程是怎么样的(GRUB引导)?

加载BIOS–>加载MRB主引导记录–>加载GRUB主引导程序–>加载内核–>内核启动(加载完内核后,GRUB 将控制权转交给内核,开始执行操作系统的启动流程。内核会初始化硬件、文件系统和设备驱动,并执行初始化进程(init)以启动用户空间的系统服务和进程。)

linux启动流程

2. 嵌入式系统启动流程(Uboot)

  1. CPU 启动:当嵌入式设备开机后,CPU 会执行预定义的启动流程。这个流程通常包括初始化 CPU 寄存器、加载引导代码到内存中等。

  2. 引导设备加载:CPU 会从预定义的启动设备(如闪存、SD 卡、网络等)中加载引导加载器(u-boot)的镜像文件到内存中。这个镜像文件通常是通过 JTAG、串口、网络等方式加载到设备中。

  3. u-boot 初始化:加载完 u-boot 镜像后,CPU 将控制权交给 u-boot,u-boot 开始执行。u-boot 会初始化设备硬件、外设、内存等,并设置一些系统参数和环境变量。

  4. 引导加载:u-boot 会根据预定义的引导顺序(如配置文件中的引导顺序)从指定的启动设备中加载操作系统内核镜像到内存中。这个内核镜像可以是 Linux 内核、Android 内核或其他操作系统内核。

  5. 内核启动:加载完内核镜像后,u-boot 将控制权传递给操作系统内核,开始执行内核启动流程。内核会初始化硬件、文件系统、设备驱动等,并启动用户空间的初始化进程。

uboot引导内核启动主要向内核传递三个参数R0,R1,R2,第一个参数R0,默认为0。第二个参数,R1,CPU ID,告诉内核板载CPU的型号。第三个参数R2,告诉内核映像文件存在什么地方,板子还剩多少内存空间。这些参数的传递都是以tag_list的方式传递的。

3. linux系统各个组成

Linux系统主要由以下几个组成部分构成:

  • 内核(Kernel):Linux内核是操作系统的核心,负责管理硬件资源、提供系统调用接口、调度进程、管理内存等核心功能。

  • Shell:Shell是用户与Linux内核交互的界面,它接收用户输入的命令并将其传递给内核执行。常见的Shell包括Bash、Zsh、Fish等。

  • 文件系统(File System):Linux文件系统负责管理计算机存储设备上的文件和目录,常见的文件系统包括Ext4、Btrfs、XFS等。

  • GNU工具集:GNU工具集是一组由GNU计划开发的开源工具,包括文本编辑器(如Emacs)、编译器(如GCC)、调试器(如GDB)等,这些工具提供了丰富的功能,使得Linux系统能够进行各种开发和管理任务。

  • 图形用户界面(Graphical User Interface,GUI):GUI是一种通过图形化界面与计算机交互的方式,Linux系统中常见的GUI包括X Window System(X11)和Wayland等。

  • 应用程序:Linux系统支持运行各种应用程序,包括办公软件、开发工具、娱乐软件等,用户可以根据需求安装和使用不同的应用程序。

内核组成

4. Linux系统遇到意外崩溃的情况怎么办?

  1. 发生崩溃时,应该会出现崩溃信息,根据崩溃信息去查找解决问题
  2. 分析日志文件:日志文件是排查系统崩溃的重要工具,通过分析系统日志文件可以查找系统崩溃的原因;常见的日志文件目录:/var/log/syslog、/var/log/messages、/var/log/kern.log等 查看日志的方法
  3. 检查硬件问题,可以检查内存、硬盘、cpu等硬件组件是否正常工作,可以通过sudo lshw 查看
  4. 更新驱动程序和内核
  5. 排除软件问题:如果系统在特定的应用程序或操作期间死机,尝试确定是否是由于特定的软件问题引起的。在这种情况下,尝试重新安装、更新或卸载相关软件。
  6. 执行系统维护:定期执行系统维护操作,如清理临时文件、优化磁盘空间、检查文件系统等,以确保系统处于良好状态。

Linux系统死机

死机一种解决办法

Linux系统内存升高

解决linux系统内存升高问题

5. Linux驱动基础

设备驱动

驱动基础编程

驱动基础、源码目录结构、根文件系统

Linux设备驱动开发详解:基于最新的Linux4.0内核
Linux设备驱动程序(中文版第三版)

Linux驱动程序的开发与调试

Linux驱动程序的开发流程

  1. 设备和驱动的注册

    • 驱动程序需要先注册设备和驱动,以进行设备的控制和管理。可以通过Linux内核提供的API如platform_driver_registermisc_register来注册设备和驱动。
  2. 设备的初始化和资源分配

    • 在驱动程序中进行设备的初始化和资源的分配,包括内存分配、IO端口映射、中断注册等。可以使用内核提供的函数如kmallocioremap等进行相应的操作。
  3. 设备的IO操作和中断处理

    • 驱动程序需要实现设备的IO操作和中断处理函数。根据设备的不同接口和功能,可以使用内核提供的IO操作函数如readlwritel等进行读写操作,以及注册和处理中断的函数如request_irqirq_handler等。
  4. 设备的释放和注销

    • 在驱动程序退出或设备不再使用时,需要进行设备的释放和注销,包括资源的释放、中断的注销等。可以使用内核提供的函数如kfreeiomem_unmap等进行相应的操作。

Linux驱动程序的调试方法

6. 编译Linux内核流程

  1. 获取源代码:首先,你需要获取 Linux 内核的源代码。你可以通过官方网站下载稳定版本的源代码,也可以使用版本控制系统如 Git 获取。

  2. 安装编译工具链:在编译 Linux 内核之前,你需要安装适用于目标架构的编译工具链。这通常包括 GCC 编译器、GNU Make、以及其他必要的开发工具。

  3. 配置内核:进入 Linux 内核源代码目录,在终端中运行 make menuconfig 命令,这会打开一个配置菜单,你可以在这个菜单中选择内核的编译选项。在这个配置菜单中,你可以配置各种内核功能、设备驱动、文件系统支持等。

  4. 进行编译:配置完内核后,使用 make 命令进行编译。你可以使用 -j 参数指定并行编译的进程数量,以加快编译速度。例如:make -j4。

  5. 安装内核:编译完成后,使用 make modules_install install 命令安装内核。这会将编译好的内核文件复制到适当的位置,并更新启动加载程序(如 GRUB)的配置文件。

  6. 重启系统:完成安装后,重新启动系统并选择新编译的内核启动。

7. 进程和线程

进程和线程
在这里插入图片描述
在这里插入图片描述
进程间通信的方式
进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。
在这里插入图片描述

线程共享
在这里插入图片描述
在这里插入图片描述

多线程和多进程

多线程的实现方法:

  1. 继承 Thread 类
  2. 实现 Runnable 接口再 new Thread(YourRunnableOjbect)

多线程的同步:

  • 线程间的同步方法大体可分为两类:用户模式和内核模式
    • 用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。
  • 在多线程环境下,如果两个线程同时访问并修改同一个变量,可能会导致竞态条件(Race Condition)的问题。竞态条件指的是多个线程对共享资源的访问顺序导致程序的输出结果不确定或不符合预期。
    可能会出现的问题:
    • 数据不一致性(Inconsistent Data): 由于两个线程同时修改a变量,其中一个线程的操作可能会被另一个线程覆盖,导致最终a的值不符合预期。
    • 竞态条件(Race Condition): 如果两个线程同时读取a的值,并根据读取的值进行修改,最终结果可能与预期不符。例如,线程A读取a的值,然后线程B也读取a的值,然后两个线程都根据自己读取的值进行修改,这样就可能导致数据不一致性。
    • 未定义的行为(Undefined Behavior): 如果两个线程同时对a进行读取和写入操作,且没有使用同步机制(如互斥锁、信号量等),则可能会导致未定义的行为,这意味着程序的行为在不同的环境下可能会产生不同的结果。
    解决方案
    • 采用同步机制包括互斥锁(Mutex)、信号量(Semaphore)、条件变量(Condition Variable)
      在这里插入图片描述
      锁(Lock)是一种同步机制,用于控制对共享资源的访问,确保在任何时候只有一个线程可以访问该资源,从而避免竟态和数据不一致性问题。
      锁的分类
      • 互斥锁(Mutex Lock)是一种特定类型的锁,它提供了互斥访问的机制,即在任何时候只允许一个线程持有锁,其他线程必须等待直到锁被释放。互斥锁常用于保护临界区(Critical Section),即一段访问共享资源的代码区域,以确保在同一时间只有一个线程可以执行临界区代码,从而避免竟态和数据不一致性。
      • 读写锁(Read-Write Lock): 读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。这种锁的设计可以提高读取操作的并发性能,适用于读多写少的场景。
      • 自旋锁(Spin Lock): 自旋锁是一种不会导致线程阻塞的锁,当一个线程尝试获取自旋锁时,如果锁已被其他线程持有,则该线程将循环等待(自旋),直到锁被释放。自旋锁适用于短期持有的情况,避免了线程切换的开销,但在长时间持有的情况下可能会导致性能下降。
      • 信号量(Semaphore): 信号量是一种更加通用的同步原语,用于控制对一组资源的访问。信号量有一个计数值,当计数值大于等于0时,表示资源可用;当计数值小于0时,表示资源不可用,线程需要等待。信号量可用于控制线程数量、实现互斥访问等。
      • 条件变量(Condition Variable): 条件变量通常与互斥锁一起使用,用于线程之间的等待和通知。当某个条件不满足时,线程可以等待条件变量的信号,一旦条件满足,其他线程可以通过发出信号来唤醒等待的线程。
      • 屏障(Barrier): 屏障用于控制多个线程在某个点同步执行。当所有线程都到达屏障点时,屏障打开,所有线程同时继续执行。

多线程和多进程
在这里插入图片描述
什么时候用多线程?什么时候用多进程?

  • 需要频繁创建销毁的优先用线程
  • 需要进行大量计算的优先使用线程
  • 强相关的处理用线程,弱相关的处理用进程

什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。

  • 可能要扩展到多机分布的用进程,多核分布的用线程
  • 都满足需求的情况下,用你最熟悉、最拿手的方式

7. socket、tcp

socket
在这里插入图片描述

  • TCP/IP 协议栈共分为 4 层,可以理解为数据收发分成了 4 个层次化过程,通过层次化的方式来解决问题。数据链路层、网络层(IP)、传输层(TCP、UDP)以及应用层(HTTP、FTP、SMTP)。
    在这里插入图片描述
  • TCP提供了可靠的、面向连接的数据传输服务,适用于对数据可靠性要求较高的场景;
  • 而UDP提供了简单的、无连接的数据传输服务,适用于实时性要求高、数据量小的场景。
    在这里插入图片描述
    在这里插入图片描述
  • 阻塞I/O适用于对实时性要求不高的场景,而非阻塞I/O适用于对实时性要求高、需要充分利用资源的场景。
  • 非阻塞I/O通常需要结合事件驱动模型(如epoll、select、poll等)或多线程/多进程来实现,以实现高效的I/O处理。

8. gdb、交叉编译

gdb
u-boot

9. MQTT

MQTT

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力跟上的码农小酥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值