多核异构核间通信-mailbox/RPMsg 介绍及实验

news2024/10/2 8:24:25

1. 多核异构核间通信

由于MP157是一款多核异构的芯片,其中既包含的高性能的A7核及实时性强的M4内核,那么这两种处理器在工作时,怎么互相协调配合呢? 这就涉及到了核间通信的概念了。

IPCC (inter-processor communication controller)用于处理器间的数据交换的通知。 它提供了一种非阻塞的信号机制,并提供原子的方式进行信号发布和信息检索。 注意,核间通信的共享内存缓冲区是在MCU的SRAM中分配的,它不是IPCC外设的一部分。

1.1. 外设简述

IPCC外设提供了硬件支持,来管理两个处理器实例之间的处理器间通信。每个处理器拥有特定的寄存器区域和中断。 有点像硬件信号量的功能。

IPCC提供了六个双向通道信号。每个通道分为两个子通道,每个子通道提供从“发送方”处理器到“接收方”处理器的单向信号:

  • P1_TO_P2子通道(从P1发到P2)

  • P2_TO_P1子通道(从P2发到P1)

子通道中包括如下功能:

  • 一个标志位,用于标识通道正在被占用和空闲的两种状态,这个标志被“发送方”处理器设置为被占用,并被“接收方”处理器清除。

  • 两个相关的中断(所有通道都共享):

    • RXO: RX通道被占用,连接到“接收器”处理器。

    • TXF: TX通道空闲,连接到“发送”处理器。

  • 带多路复用的中断掩码功能。

IPCC支持以下信道的操作模式:

  • 单工通信方式:

    • 仅使用一个子信道。

    • 单向消息:“发送者”处理器将通信数据发布到内存中后,它将通道状态标志设置为已占用。当消息被处理时,“接收者”处理器清除该标志。

  • 半双工通讯方式:

    • 仅使用一个子信道。

    • 双向消息:“发送者”处理器将通信数据发布到内存中后,它将通道状态标志设置为已占用。当消息被处理并且响应在共享内存中可用时,“接收器”处理器将清除该标志。

  • 全双工通讯方式:

    • 子通道用于异步模式。

    • 通过将子通道状态标志设置为占用,任何处理器都可以异步发布消息。当消息被处理时,“接收者”处理器清除该标志。可以将这种模式视为给定通道上两个单工模式的组合。

核间通信的模型如下:

1.2. 框架简述

IPCC作为核间通信的桥梁,它仅承担着通知的角色,负责消息的分发、中断的处理等。

实际上,IPCC外设这个角色只是多核异构核间通信中的一块,在我们使用多核异构核间通信时,往往不仅希望使用到核间的消息通知,还希望能在不同的核心中进行数据的交互(比如M4核进行实时的AD数据采集处理,完成后,M4核可通过异构的框架将数据呈递给A7核,A7核再进行更复杂的应用)。那么在这个需求的驱动下,就出现了一些框架相互配合使用的情况了,下面我们就给大家介绍这些内核框架。

1.2.1. RemoteProc framework

远程处理器框架(RPROC、RemoteProc)允许不同的平台/体系结构控制(打开电源,加载固件,关闭电源)远程处理器,同时抽象出硬件差异。此外,它还提供监视和调试远程协处理器的服务。

以MP157为例,其RemoteProc可分为两块,分别是A7核端、M4核端:

remoteproc:这是远程处理器框架的通用部分(在MP157中为A7核端)。

它的作用是:

  • 将ELF固件加载到远程处理器内存中。

  • 解析固件资源表以设置关联的资源(例如IPC,内存分割和跟踪)。

  • 控制远程处理器的执行(启动,停止…)。

  • 提供监视和调试远程固件的服务。

stm32_rproc:这是远程处理器平台驱动程序(在MP157中为M4核端)。

它的作用是:

  • 将stm32特定的功能(回调)注册到RPROC框架。

  • 处理与远程处理器关联的平台资源(例如寄存器,看门狗,复位,时钟和存储器)。

  • 通过邮箱框架将通知(通知)转发到远程处理器。

ST官方参考资料:

Linux RPMsg framework overview - stm32mpu

1.2.2. RPMsg framework

此小节为大家简述有关Linux RPMsg框架的内容。RPMsg框架是一个基于virtio的消息总线,它允许本地处理器与系统上可用的远程处理器通信。

此框架在多核异构中承担的角色如下图:

Linux RPMsg框架是在virtio框架顶层上实现的消息传送框架,其用于主机和远程处理器进行通信。它基于virtio vring,可通过共享内存向远程CPU发送消息或从远程CPU接收消息。

这些vring是单向的,一个vring专用于发送到远程处理器的消息,另一个vring用于从远程处理器接收的消息。此外,共享缓冲区需要在两个处理器都可见的内存空间中创建。

当新消息在共享缓冲区中等待时,会使用到另一个框架 Linux Mailbox framework ,该框架将用于通知对应的Core。

依靠这些框架,RPMsg框架实现了基于不同通道的通信。通道可被文本名称标识,并有一个本地(“源”)的RPMsg地址和一个远程(“目的”)的RPMsg地址。

在远程处理器端(MP157则为M4核),也必须使用RPMSG框架。RPMSG框架的实现存在几种解决方案,ST建议使用OpenAMP方案,并在SDK中给出了示例。

Github OpenAMP框架 .

简单来说,MP157的A7核与M4核,通过一个标准的RPMsg框架来建立起联系,完成数据传递。

具体原理可以参考:

RPMsg-Messaging-Protocol .

RPMsg-Communication-Flow .

Linux内核源码目录给出的rpmsg client的示例代码位置如下:

samples/rpmsg/rpmsg_client_sample.c

rpmsg框架Linux内核驱动源码位于:

drivers/rpmsg

ST官方参考资料:

Linux remoteproc framework overview - stm32mpu

1.2.3. Mailbox framework

此小节为大家简述有关Linux邮箱框架的内容。邮箱框架涉及异构多核系统的处理器间通信。

此框架的结构如下图:

邮箱框架被用于内核间进行消息或信号的交换,常用于主机和协处理器间。邮箱由以下模块组成:

  • 一个邮箱控制器(mailbox controller),依赖于硬件平台实现,比如MP157的IPCC外设:

    • 它负责配置和处理来自IPCC外围设备的IRQ。

    • 它为邮箱客户端提供了通用API。

  • 一个邮箱客户端(mailbox client),负责发送或接收消息。

关于此框架的权威描述,在内核文档中的如下目录:

Documentation/mailbox.txt

一般而言mailbox controller和client都由芯片厂商来负责实现,因为这依赖于外设。 我们更常关注的,则是mailbox client的创建和使用。

ST实现的mailbox client代码位置如下:

drivers/remoteproc/stm32_rproc.c

在内核中还给出了一份mailbox client的示例驱动代码, 代码通过debugfs子系统,将mailbox的操作暴露给了用户空间, 用户可以直接通过debugfs来使用mailbox,进行消息在不同内核中的传递。

mailbox框架的设备树描述可参考内核源码文档:

Documentation/devicetree/bindings/mailbox/mailbox.txt

一个简单的mailbox client设备树节点,可以参考内核源码目录:

Documentation/devicetree/bindings/mailbox/sti-mailbox.txt

内核源码目录给出的mailbox client的示例代码位置如下:

drivers/mailbox/mailbox-test.c

ST官方参考资料:

Linux Mailbox framework overview - stm32mpu

1.2.4. 框架小结

前面介绍了三个框架,它们并不是独立工作的,而是相互协调的,彼此关联。 我们可以通过两张图来查看它们之间的关系。

以RemoteProc框架为主视角出发:

可以理清三个框架的关系,RemoteProc可以说是骨架,关联到了RPMsg框架、Mailbox框架。

1.3. 设备树插件描述

1.3.1. IPCC设备树节点

设备树节点位于arch/arm/boot/dts/stm32mp157c.dtsi

IPCC设备树节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ipcc: mailbox@4c001000 {
    compatible = "st,stm32mp1-ipcc";
    #mbox-cells = <1>;
    reg = <0x4c001000 0x400>;
    st,proc-id = <0>;
    interrupts-extended =
        <&intc GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
        <&intc GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
        <&exti 61 1>;
    interrupt-names = "rx", "tx", "wakeup";
    clocks = <&rcc IPCC>;
    wakeup-source;
    power-domains = <&pd_core>;
    status = "disabled";
};

使用节点位于arch/arm/boot/dts/stm32mp157a-basic.dts

使用IPCC设备树节点

1
2
3
&ipcc {
    status = "okay";
};

设备树中的compatible = “st,stm32mp1-ipcc”属性,会匹配到 drivers/mailbox/stm32-ipcc.c 驱动程序,驱动程序中会创建一个mbox controller。

1.3.2. A7<–>M4 rproc设备树节点

设备树节点位于arch/arm/boot/dts/stm32mp157c.dtsi

rproc设备树节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
m4_rproc: m4@0 {
    compatible = "st,stm32mp1-rproc";
    #address-cells = <1>;
    #size-cells = <1>;

    ranges = <0x00000000 0x38000000 0x10000>,
            <0x30000000 0x30000000 0x60000>,
            <0x10000000 0x10000000 0x60000>;
    resets = <&rcc MCU_R>;
    reset-names = "mcu_rst";
    st,syscfg-pdds = <&pwr 0x014 0x1>;
    st,syscfg-holdboot = <&rcc 0x10C 0x1>;
    st,syscfg-tz = <&rcc 0x000 0x1>;
    st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
    status = "disabled";

    m4_system_resources {
        compatible = "rproc-srm-core";
        status = "disabled";
    };
};

使用节点位于arch/arm/boot/dts/stm32mp157a-basic.dts

使用rproc设备树节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
&m4_rproc {
    memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>,
            <&vdev0vring1>, <&vdev0buffer>;
    mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>;
    mbox-names = "vq0", "vq1", "shutdown";
    interrupt-parent = <&exti>;
    interrupts = <68 1>;
    interrupt-names = "wdg";
    wakeup-source;
    recovery;
    status = "okay";
};

设备树中的compatible = “st,stm32mp1-rproc”属性,会匹配到 drivers/remoteproc/stm32_rproc.c 驱动程序,驱动程序中会创建一个mbox client,并基于RemoteProc、RPMsg框架与mbox client进行关联。

1.4. 实验代码简述

这里我们就简单讲解一下M4核端的代码和一些概念,更详细的内容则需大家自己研究了。

rpmsg框架下有通信端点的概念,数据在两个端点间传输。端点间的数据传输是rpmsg框架下数据传输最原始的形式, 我们可以在原始的数据传输形式上再做一层封装,抽象出一些特定类型的设备。

每个端点注册的底层实现,就是一个内核设备的注册(使用的是平台总线模型),故注册的端点设备, 可以利用到驱动的Probe功能(具体实现详见 drivers/rpmsg/rpmsg_core.c 300行后内容)。

在M4端,通过调用openamp库中的 OPENAMP_create_endpoint 函数,并在调用时指定参数name(即为设备名称), 即可在内核中注册一个对应的rpmsg框架平台设备,该设备最终可以通过name(设备名称)来匹配到相应的A7端内核驱动:

所以Linux rpmsg框架下使用平台总线模型与端点通讯的方式结合,给一些需要有特殊操作的自定设备, 提供了支持的可能。比如异构间的通讯,可以封装成串口通讯模型。

在我们提供的M4内核固件的代码中,注册了两种Linux内核自带的rpmsg框架下, 原生支持的设备模型,这两种设备类型是rpmsg-tty-channel、rpmsg-client-sample:

  • rpmsg-tty-channel: tty终端设备,对应内核驱动源码 drivers/rpmsg/rpmsg_tty.c,此驱动模块默认被编译进内核。

  • rpmsg-client-sample: 框架原生的通讯方式测试设备(放在内核里作为演示该框架的Demo提供的),对应内核驱动源码 samples/rpmsg/rpmsg_client_sample.c,此驱动默认被编译成模块, 并放置在文件系统 /lib/modules/4.19.94-stm-r1/kernel/samples/rpmsg/rpmsg_client_sample.ko 中, 当设备与驱动发生匹配时,系统会自动insmod该驱动模块。

  • 还有一种字符设备模型,rpmsg_chrdev,源码位于 drivers/rpmsg/rpmsg_char.c ,我们的代码中未实验,可自行研究。

M4核的代码中,创建上述两个rpmsg设备的代码如下:

  • rpmsg-client:rpmsg-client设备的示例,使用的是rpmsg框架下原生的异构通讯方式,调用openamp的原生操作函数,比如OPENAMP_send、receive等即可通讯。 在注册设备时,传入的 RPMSG_SERVICE_NAME 即为 rpmsg-client-sample ,故会在Linux系统中注册一个名为rpmsg-client-sample的设备, 并且会自动匹配对应名称的内核模块(前面有述)。

  • rpmsg-tty:rpmsg-tty的示例,则在原生的通讯方式上,注册成了tty设备的模型,并将rpmsg的通讯封装成了tty的通讯形式, 更符合串口通信的操作、方便使用,比如在M4核端,就有VIRT_UART_Transmit的串口发送函数, rpmsg-tty设备注册的设备文件,会映射到A7核端的Linux文件系统下的 /dev/ttyRPMSGx 。

在M4核的代码中,还初始化了usart3作为M4内核的Log输出串口,我们可以通过串口模块接入开发板上的usart3,来查看M4内核输出的Log。 最终工程代码会被用于生成ELF固件,ELF固件即为程序,会运行在MP157的M4内核上。

综上,通过原生的rpmsg框架设备、 /dev/ttyRPMSGx 节点以及M4内核使用的usart3资源,我们就可以进行简单的实验了。 本实验的代码也比较简单,这里就讲解这么多。

1.5. 实验准备

由于多核异构的框架是与处理器的架构紧密联系在一起的,所以一般这些框架驱动会由芯片厂商为我们提供好。 野火MP157开发板默认开启了这些驱动支持,并且开启了对应的设备树,我们直接进行使用就可以了。

在前面我们提到了,M4内核要与A7内核通讯需要共用一个框架,那么M4内核的运行的程序里, 就需要有对应的框架代码,这个为大家提供的工程中已经包含。 最终我们将代码生成的ELF固件,通过A7内核的remoteproc子系统加载到M4内核上, 即可做好前期的准备工作。

生成ELF固件的工程代码位于 \linux_driver\framework_ipcc\STM32Cube_FW_MP1_V1.2.0\Projects\STM32MP157C-EV1\Applications\OpenAMP\OpenAMP_raw 目录下, 感兴趣可自行研究,工程可用MDK或CubeIDE打开(在工程目录中由对应文件夹)。

MP157-M4内核的使用可参考:

[野火]Cortex-M4内核开发实战指南-基于STM32MP157

重要

在M4核的代码中,还初始化了usart3,实验前请务必将usart3的设备树插件关闭。

1.6. 实验操作

M4核的固件我们已经成功编译并放在了/linux_driver/framework_ipcc目录下, 我们将M4核的固件 OpenAMP_raw_CM4.elf 上传至Linux文件系统的 /lib/firmware/ 目录。此目录存放着Linux系统中会使用到的各种固件。

执行如下命令指定M4内核加载的固件,默认在root用户下操作:

# 进入remoteproc子系统目录
cd /sys/class/remoteproc/remoteproc0
# 导入M4内核固件名称
echo OpenAMP_raw_CM4.elf > firmware

在同一目录下,执行如下命令可启动停止M4内核:

# 启动M4内核
echo start > state
# 停止M4内核
echo stop > state

启动M4内核后信息如下:

M4内核加载固件并启动后,在串口终端中打印出了一些信息,我们通过串口模块接入usart3引脚, 再打开串口调试助手设置波特率为115200,可以看到M4固件初始化的usart3作为串口printf出来的信息, 为 [INFO ]M4 send to A7 : hello world! , 并且A7端的驱动也打印出了 rpmsg_client_sample virtio0.rpmsg-client-sample.-1.0: incoming msg 1 (src: 0x0) 说明M4核及A7核驱动正常工作了。

此外,输入lsmod,我们还可以看到演示设备创建后,对应被动态加载的驱动模块, rpmsg_client_sample :

下面,我们进行第二个设备测试,通过前面现象中的LOG,我们可以看到被枚举出的tty设备节点 /dev/ttyRPMSG0 节点, 我们就通过该节点测试tty设备的功能,输入如下命令:

echo "hello M4 core , i'm A7!" > /dev/ttyRPMSG0

实验现象如下所示:

上图为A7通过虚拟的tty终端设备,转发到M4内核的消息内容, 最终通过M4核固件的串口Log功能打印出来对应信息。

自此,所有实验结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/337960.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven_第四章 使用Maven:IDEA环境

目录第一节 创建父工程第二节 配置Maven信息第三节 创建Java模块工程第四节 创建Web模块工程1、创建模块2、修改打包方式3、Web 设定4、借助IDEA生成web.xml5、设置 Web 资源的根目录6、测试6.1 创建文件6.2 配置tomcat第五节 其他操作1、在IDEA中执行Maven命令①直接执行②手动…

代码随想录算法训练营第24天25天|● 77. 组合● 216.组合总和III ● 17.电话号码的字母组合

77组合 看完题后的思路 void f&#xff08;数组&#xff0c;startIndex&#xff09;递归终止 if&#xff08;startIndex数组长度||path.sizek&#xff09;{ if(path.sizek){ 加入} }递归 for&#xff08;&#xff1b;startIndex<num.size&#xff1b;startIndex&#xff0…

为什么分库分表

系列文章目录 文章目录系列文章目录前言一、什么是分库分表二、分库分表的原因分库分表三、如何分库分表3.1 垂直拆分1.垂直分库2、垂直分表3.2 水平拆分水平分库水平分表水平分库分表的策略hash取模算法range范围rangehash取模混合地理位置分片预定义算法四、分库分表的问题分…

华为动态二进制翻译工具(ExaGear)

你还在为liunix x86程序移植到ARM环境而烦恼吗&#xff1f;你们现在您的福利来了&#xff0c;ExaGear可以解决您的烦恼&#xff0c;让您试下零代码迁移运行Linux x86程序。ExaGear是华为自研动态二进制翻译工具&#xff0c;通过在运行时&#xff0c;将x86应用指令翻译为ARM64指…

备战金三银四,软件测试面试题(全)

1.B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行&#xff0c;可以实现跨平台&#xff0c;客户端零维护&#xff0c;维护成本低&#xff0c;但是个性化能力低&#xff0c;响应速度较慢 C/S响应速度快&#xff0c;安全性强&#xff0c;一般应用于局域网中&#xff0c;因…

leetcode: Two Sum

leetcode: Two Sum1. 题目1.1 题目描述2. 解答2.1 baseline2.2 基于baseline的思考2.3 优化思路的实施2.3.1 C中的hashmap2.3.2 实施2.3.3 再思考2.3.4 最终实施3. 总结1. 题目 1.1 题目描述 Given an array of integers nums and an integer target, return indices of the …

Fluent Python 笔记 第 4 章 文本和字节序列

Python 3 明确区分了人类可读的文本字符串和原始的字节序列。隐式地把字节序列转换成 Unicode 文本已成过去。本章将要讨论 Unicode 字符串、二进制序列&#xff0c;以及在二者之间转 换时使用的编码。 没啥可看的&#xff0c;就一句话&#xff0c;一定不能依赖默认编码&#x…

DP优化 - 斜率优化

假设当前的 DP 方程为 fimin⁡0≤j<i{−K(i)X(j)Y(j)}F(i)f_i\min\limits_{0\leq j< i}\{-K(i)X(j)Y(j)\} F(i)fi​0≤j<imin​{−K(i)X(j)Y(j)}F(i) 或 fimax⁡0≤j<i{−K(i)X(j)Y(j)}F(i)f_i\max\limits_{0\leq j< i}\{-K(i)X(j)Y(j)\} F(i)fi​0≤j<im…

Node.js笔记-Express(基于Node.js的web开发框架)

目录 Express概述 Express安装 基本使用 创建服务器 编写请求接口 接收请求参数 获取路径参数(/login/2) 静态资源托管-express.static&#xff08;内置中间件&#xff09; 什么是静态资源托管&#xff1f; express.static() 应用举例 托管多个静态资源 挂载路径前缀…

车厢调度(train)(栈)

目录 题目描述 解题思路&#xff1a; 代码部分&#xff1a; 题目描述 有一个火车站&#xff0c;铁路如图所示&#xff0c;每辆火车从A驶入&#xff0c;再从B方向驶出&#xff0c;同时它的车厢可以重新组合。假设从A方向驶来的火车有n节&#xff08;n≤1000&#xff09;&…

Revit中关于屋顶编辑线移动的问题

一、Revit中关于屋顶编辑线移动的问题 在绘制屋顶的时候&#xff0c;如果出现有稍微偏差的时候&#xff0c;个别习惯移动编辑线&#xff0c;这种方法是不可取的&#xff0c;接下来为大家介绍一下这种方法的问题所在。 首先我们绘制几面这样的墙体&#xff0c;主要做测试用的&am…

锁升级之Synchronized

Synchronized JVM系统锁一个对象里如果有多个synchronized方法&#xff0c;同一时刻&#xff0c;只要有一个线程去调用其中的一个synchronized方法&#xff0c;其他线程只能等待&#xff01;锁的是当前对象&#xff0c;对象被锁定后&#xff0c;其他线程都不能访问当前对象的其…

流程引擎之发展史及对比总结

流程引擎渊源市场上比较有名的开源流程引擎有 jBPM、Activiti、Camunda、Flowable 和 Compileflow。其中 jBPM、Activiti、Flowable、camunda 四个框架同宗同源&#xff0c;祖先都是 jbpm4&#xff0c;开发者只要用过其中一个框架&#xff0c;基本上就会用其它三个。而 Compile…

SOFA Weekly|SOFANew、本周贡献 issue 精选

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展欢迎留言互动&#xff5e;SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&#…

基于Gromacs配体修饰自由能FPE计算(手动版)

基于Gromacs配体修饰自由能FPE计算(手动版) 本教程来自于https://github.com/huichenggong/Learning-Computation-with-Chenggong/tree/main/CC_news_008_ddG_uniFEP 我们将要使用的系统来自这篇论文 配体和受体pdb文件 A. 介绍 在本教程中&#xff0c;我们将使用非平衡自…

使用开源实时监控系统 HertzBeat 5分钟搞定 Mysql 数据库监控告警

使用开源实时监控系统 HertzBeat 对 Mysql 数据库监控告警实践&#xff0c;5分钟搞定&#xff01; Mysql 数据库介绍 MySQL是一个开源关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。MySQL 是最流行的开源关系型数据库管理系统之…

VHDL语言基础-时序逻辑电路-锁存器

目录 锁存器的设计&#xff1a; RS锁存器&#xff1a; 真值表&#xff1a; 电路结构图&#xff1a; RS锁存器的仿真波形如下&#xff1a; D锁存器&#xff1a; D锁存器的仿真波形如下&#xff1a; 锁存器的设计&#xff1a; 为了与触发器相类比&#xff0c;我们先介绍锁…

奇舞周刊第 481 期 数据不够实时:试试长连接?

记得点击文章末尾的“ 阅读原文 ”查看哟~下面先一起看下本期周刊 摘要 吧~奇舞推荐■ ■ ■数据不够实时&#xff1a;试试长连接&#xff1f;在特定场景下&#xff0c;我们往往需要实时的去获取最新的数据&#xff0c;如获取消息推送或公告、股票大盘、聊天消息、实时的日志和…

面试(九)小米C++开发一面 21.11.02

1、局部变量与全局变量的区别?可以同名嘛? 首先是作用域: 局部变量只在变量声明的代码块范围内生效 全局变量在其声明后的所有位置都能访问到 在局部变量与全局变量同名的情况下,全局变量会被屏蔽掉,只会使用局部变量的内容 2、extern 当在a.c中想要使用b.c中的函数fu…

【Mac OS】JDK 多版本切换配置

前言 由于不同的项目可能需要使用的 JDK 版本不一样&#xff0c;所以在系统中配置多个 JDK 版本&#xff0c;并且能随时切换&#xff0c;是一个必要的配置。 查看已安装的 JDK 版本 /usr/libexec/java_home -V框框1是执行的命令 框框2是当前系统下所有的 JDK 版本 框框3是当…