一文入门交叉编译

前言:

在阅读本文之前,你哦需要了解makefile文件的编写规则,这里我们推荐两篇入门:

Makefile 规则-CSDN博客

Makefile 快速入门-CSDN博客

编译定义

编译是指将源代码文件(如C/C++文件)经过预处理、编译、汇编和链接等步骤,转换为可执行文件的过程。将源代码转换成机器代码的过程称为编译(Compile),编译的工作需要编译器(Complier)来完成。

本地编译

定义: 


本地编译是指在当前的编译平台上,生成能在当前平台上运行的可执行文件。

例如,在x86平台上,使用x86平台上的工具,开发针对x86平台本身的可执行程序,这个编译过程称为本地编译。
以一个简单的例子来说明本地编译,假设有一个hello.c文件,它包含以下内容

验证程序


#include <stdio.h>
int main()
{
printf("Hello, world!\n");
return 0;
}

我们想要在x86平台上进行本地编译,并在x86平台上运行这个程序。

编译: 

gcc -o hello hello.c

运行:


./hello



演示


交叉编译

定义


交叉编译是指在当前的编译平台上,生成能在体系结构不同的另一种目标平台上运行的可执行文件。例如,在x86平台上,使用针对ARM平台的工具,开发针对ARM平台的可执行程序,这个编译过程称为交叉编译。

 一句话 --> 这本平台上编译生成其他平台能运行的可执行文件。 

准备


想要在x86平台上进行交叉编译,并在ARM平台上运行这个程序。首先需要在家目录下的.bashrc最后配置添加交叉编译工具链:

进入配置文件
vi .bashrc


添加:


export PATH=$PATH:/home/$(whoami)/orangepi-build/toolchains/gcc-arm-9.2-2019.12-
x86_64-aarch64-none-linux-gnu/bin


验证 -- 查看版本号:


执行aarch64-none-linux-gnu-gcc --version 可以看到对应的版本号:

-------------------------

file命令补充

定义

file 命令是一个在 Unix 和类 Unix 系统(如 Linux)中常用的命令,用于确定文件的类型。通过检查文件的内容、魔数(magic number,位于文件开头的几个字节,用于标识文件类型)或其他特定的签名,file 命令可以准确地告诉你一个文件是什么类型的。

作用

  • file 命令主要用于确定文件的MIME类型或更一般的文件类型。
  • 它对于检查未知文件特别有用,因为它可以帮助你了解文件的内容而不需要打开或执行它。
  • 它也可以用来检查文件的编码(如文本文件的字符集)。

格式

file 命令的基本格式如下:

bash复制代码

file [选项] 文件名...

选项

file 命令有一些选项,用于改变其输出或行为。以下是一些常用的选项:

  • -b:只显示文件类型,不显示文件名。
  • -i:显示MIME类型的输出,而不是更详细的描述。
  • -z:尝试查看压缩文件的内容。
  • -L:对于符号链接,显示链接指向的文件的内容类型,而不是链接文件本身。
  • --mime-type:与 -i 选项相似,但仅显示MIME类型,不包括字符集信息。

示例

  1. 显示文件的类型:

bash复制代码

file example.txt
# 输出可能是:example.txt: ASCII text
  1. 显示多个文件的类型:

bash复制代码

file file1.jpg file2.pdf
# 输出可能是:
# file1.jpg: JPEG image data, JFIF standard 1.01
# file2.pdf: PDF document, version 1.4
  1. 显示MIME类型:

bash复制代码

file -i example.txt
# 输出可能是:example.txt: text/plain; charset=us-ascii
  1. 只显示类型,不显示文件名:

bash复制代码

file -b example.txt
# 输出可能是:ASCII text

通过 file 命令,你可以轻松地识别文件类型,这对于处理未知文件或确定文件是否已被篡改特别有用。

case1: 简单的交叉编译演示

在x86平台上进行交叉编译

编译

执行如下命令  (前提已经安装,配置了编译工具)

aarch64-none-linux-gnu-gcc -o hello hello.c

file命令查看类型

利用file命令可以看到编译出来的程序是ARM aarch64的二进制程序

运行

这时候需要将该文件拷贝到比如香橙派等ARM开发板上运行, 在X86宿主机上是无法正常运行的

拷贝到目标平台 -- arm 平台(香橙派)上

scp hello orangepi@192.168.1.28:/home/orangepi

 在orangepi上能运行,见下图:

执行命令运行


这样就实现了交叉编译

==================================================

case2: 交叉编译wiringOP库:  -- 对库文件的交叉编译

目的

在x86平台上面编译后,拷贝到orangepi 的根目录即可调用


编译完放 file 查看类型正确后,就压缩一下传到香橙派上即可使用


压缩:
tar -zcvf _install.tar.gz _install

用配置出来的 交叉编译工具编译移植wiringOP库:

0.下载wiringPi文, 然后进到wiringOP  文件夹

git clone https : //github.com/orangepi-xunlong/wiringOP // 下载源码
cd wiringOP // 进入文件夹

1. 修改build.sh脚本,在echo "WiringPi Library" 之前添加:

目的 -- 在当前目录下面创建,_install/usr/local 文件夹(路径),并且分别生成bin,include,lib文件夹

mkdir $PWD/_install/usr/local/bin -p
mkdir $PWD/_install/usr/local/include -p
mkdir $PWD/_install/usr/local/lib -p

mkdir - p 补充

定义

mkdir -p 是一个在 Unix 和类 Unix 系统(如 Linux)中常用的命令,用于创建目录(或文件夹)。-p 选项是该命令的一个非常有用的参数。

功能

mkdir -p 命令允许你创建多级目录,即使上级目录不存在。如果上级目录不存在,mkdir 会自动创建它们。如果没有 -p 选项,当上级目录不存在时,mkdir 会报错。

示例
  1. 创建一个单层目录

如果你只想创建一个单层目录,你可以简单地使用 mkdir 命令,不需要 -p 选项。

 

bash复制代码

mkdir mydir
  1. 创建多级目录

如果你想要创建一个多级目录,如 dir1/dir2/dir3,并且 dir1 和 dir2 还不存在,你可以使用 mkdir -p

 

bash复制代码

mkdir -p dir1/dir2/dir3

使用 -p 选项,mkdir 会首先创建 dir1,然后在 dir1 内部创建 dir2,最后在 dir2 内部创建 dir3。如果其中任何一个目录已经存在,mkdir -p 不会报错,而是继续创建剩余的目录。

注意事项
  • 如果使用 mkdir(不带 -p)尝试创建多级目录,并且上级目录不存在,你会收到一个错误消息。
  • mkdir -p 不会覆盖已经存在的目录或文件。如果尝试创建的目录已经存在,mkdir -p 会静默地成功退出,不会显示任何错误或警告。
  • mkdir -p 对于脚本编写特别有用,因为你可以确保多级目录结构在继续执行脚本之前被正确创建,而无需担心目录是否已经存在。


2. 修改devLib/Makefile、gpio/Makefile、wiringPiD/Makefile

可以借助 vscode的ctrl + shift +f (能快速查找,并且一键修改)

  将所有Makefile中的CC := gcc 改成 CC := aarch64-none-linux-gnu-gcc  

( 将本地编译需要用到的全部 gcc,修改为交叉编译需要用的命令 aarch64-none-linux-gnu-gcc)


3. 修改devLib/Makefile、gpio/Makefile、wiringPiD/Makefile 、wiringPi/Makefile

修改DESTDIR?=/usr 替换为DESTDIR?= $(shell pwd)/../_install/usr

// shell pwd 拿到当前路径 

// 我们希望在他的上层目录下面直接创建_install/usr/路径


4. 修改wiringPi/Makefile   的软连接


1、将

$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION)   $(DESTDIR)/lib/libwiringPi.so
修改为:

$Q ln -sf  $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION)   $(DESTDIR)$(PREFIX)/lib/libwiringPi.so

//我们前面修改了 DESTDIR  路径,这里如果还用默认的)$(DESTDIR)就找不到目标文件了,

需要加上$(PREFIX)指定到正确的路径


修改devLib/Makefile

将$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPiDev.so.$(VERSION) $(DESTDIR)/lib/libwiringPiDev.so
修改为:
$Q ln -sf  $(DESTDIR)$(PREFIX)/lib/libwiringPiDev.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libwiringPiDev.so

//修改利用同上


5. devLib/Makefile

INCLUDE = -I. 修改为INCLUDE = -I. -I$(DESTDIR)$(PREFIX)/include

//添加 -I 参数允许你指定编译器应该在哪些额外的目录中查找这些头文件。

注意-I 参数后面直接跟的是目录的路径,而不是头文件的路径。并且,你可以使用多个 -I 参数来指定多个搜索目录。当编译器查找一个头文件时,它会按照 -I 参数指定的目录顺序进行搜索,并在找到文件后停止搜索。如果没有在 -I 指定的目录中找到文件,编译器会回退到默认的系统头文件搜索路径中查找。

6. 然后执行:


./build

然后tree 就能看到我们生成的文件/夹

7.file  查看文件类型

验证教程编译成功

 编译过后我们可以看到什么生成的gpio 形式

8.压缩整合文件 

9.拷贝到orangepi上

10.验证


后面我们就可以把_INSTALL里的内容拷贝到香橙派的根目录下,然后执行

sudo     ldconfig
作用:  配置运行需要的一些列软链接
就可以使用该库了,可以使用

sudo gpio readlall 

交叉编译 wiringPi库成功 --> 在×86平台 上编译,然后移植到 arm 平台 orangepi上面能直接运行 

sudo    ldconfig补充:

sudo ldconfig 是一个在 Unix 和类 Unix 系统(如 Linux)中用于更新动态链接器(如 ld.so 或 ld-linux.so)运行时的绑定和缓存的命令。

 ldconfig 命令的简要说明和其主要功能:

  1. 更新共享库缓存:当系统管理员添加、删除或修改了共享库(.so 文件)时,ldconfig 会更新动态链接器的缓存。这个缓存通常存储在 /etc/ld.so.cache 文件中,它包含了系统中所有共享库的位置和版本信息。
  2. 搜索指定目录:默认情况下,ldconfig 会搜索 /lib/usr/lib 以及在 /etc/ld.so.conf 文件和 /etc/ld.so.conf.d/ 目录下的配置文件中指定的目录。这些目录通常包含系统的共享库文件。
  3. 重新排序和删除:当调用 ldconfig 时,它会按照链接器的配置文件的顺序对库进行排序,并且还会从缓存中删除不再需要的库的条目(例如,已经被删除或移动的文件)。
  4. 需要超级用户权限:由于 ldconfig 需要修改系统级的缓存文件,因此通常需要超级用户(root)权限来执行。这就是为什么你会看到 sudo ldconfig 这样的命令。
  5. 使用场景:当你安装了新的库或更新了现有的库时,通常建议运行 sudo ldconfig 来确保动态链接器知道这些新的或更新过的库的位置。
  6. 错误处理:如果 ldconfig 在尝试更新缓存时遇到问题(例如,因为某些库文件损坏或丢失),它通常会显示一个错误消息,但通常会继续更新剩余的库。

总之,sudo ldconfig 是一个用于更新动态链接器缓存的命令,以确保系统能够正确地找到和使用共享库。

======================================

交叉编译 项目(以智能垃圾分类为例):

观前提醒

对这个项目不太了解的朋友推荐先看这篇: 垃圾分类最终篇 -- 添加网络控制功能-CSDN博客

希望对这个项目有更多了解的朋友,还可以去专栏了解详细内容:智能垃圾桶项目_mx_jun的博客-CSDN博客


 
0. 创建文件夹:


inc --  存放所有头(.h)文件

src -- 存放所有源(.c)文件
3rd -- 存放 这个工程依赖的第三方的头文件和库


--------------

1.下载需要的库文件,编译链接文件

增加 3rd 目录,用于存放 wiringOP python3.10 等第三方依赖库和头文件。需增加如下几个依赖库 .
首先,从香橙派上利用 apt download 下载依赖包的头文件和库文件,并拷贝到宿主机里:

在orangepi 上面安装好后,我们就可以拷贝到我们的宿主家 -- Ubuntu上面

执行以下命令下载:

apt download zlib1g zlib1g-dev libpython3.10 libpython3.10-dev libexpat1 libexpat1-dev libcrypt1 libcrypt-dev

如下命令拷贝到 ×86 平台-- 宿主机


scp *deb mxjun@192.168.88.132:/home/mxjun

说明:

apt  download  -- 只下载,不安装 
apt  install  --  既下载,又安装


.deb -- .deb 文件是 Debian 软件包格式的文件

2.在×86平台上面解压文件

deb 包的解压命令是 dpkg

解压文件
 dpkg -x   .deb文件  存放位置 

3.编写Makefile 文件(细节活)

然后去makefile里面加上所有的头文件路径


Makefile 带注释形式  :带注释形式 

CC  :=  aarch64-none-linux-gnu-gcc
# SRC -- 存放所有的 .c 文件
SRC :=  $(shell find src -name *.c)
# INC --  存放所有的 头文件 (包括自己写的 和 第三方)
INC := ./inc \
    ./3rd/usr/local/include \
    .3rd/usr/include \
    ./3rd/usr/include/python3.10 \
    ./3rd/usr/include/aarch64-linux-gnu/python3.10 \
    .3rd/usr/include/aarch64-linux-gnu

#  把需要包含的 .c 文件,替换为.o 文件
OBJ := $(subst src/.,obj/,$(SRC:.c=.o))


#  创建目标 , 并且指定存放位置
TARGET  =  obj/garbage

#   -I./inc  -- 存放头文件路径
CFLAGS := $(foreach item,$(INC),-I$(item))

# -I 指定的 第三方连接  库文件路径
LIBS_PATH := ./3rd/usr/local/lib \
             ./3rd/lib/aarch64-linux-gnu \
             ./3rd/usr/lib/aarch64-linux-gnu \
             ./3rd/usr/lib/python3.10 \

# -L ./3rd/usr/local/LIBS
LDFLAGS := $(foreach item,$(LIBS_PATH),-L$(item))

#  指定我们要链接的库
LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt


#  生成obj文件夹,里面包含源文件对应的.o文件
obj/%.o:src/%.CC
    mkdir -p obj
    $(CC) -o $@ -c $< $(CFLAGS)

#  依赖obj 下面的.o文件 编译
$(TARGET) : $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)    $(LIBS)

compile : $(TARGET)

clean: rm $(TARGET) obj &(OBJ) -rf

# 打印调试信息

debug:
    echo $(CC)
    echo $(SRC)
    echo $(INC)
    echo $(OBJ)
    echo $(TARGET)
    echo $(CFLAGS)
    echo $(LDFLAGS)
    echo $(LIBS)


.PHONY: clean compile debug 

=========================


纯净版本:

CC := aarch64-linux-gnu-gcc
SRC := $(shell find src -name "*.c")
INC := ./inc \
    ./3rd/usr/local/include \
    ./3rd/usr/include \
    ./3rd/usr/include/python3.10 \
    ./3rd/usr/include/aarch64-linux-gnu/python3.10 \
    ./3rd/usr/include/aarch64-linux-gnu
OBJ := $(subst src/,obj/,$(SRC:.c=.o))
TARGET=obj/garbage
CFLAGS := $(foreach item, $(INC),-I$(item)) # -I./inc -I./3rd/usr/local/include
LIBS_PATH := ./3rd/usr/local/lib \
    ./3rd/lib/aarch64-linux-gnu \
    ./3rd/usr/lib/aarch64-linux-gnu \
    ./3rd/usr/lib/python3.10
LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs
LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt
obj/%.o:src/%.c
    mkdir -p obj
    $(CC) -o $@ -c $< $(CFLAGS)

$(TARGET) :$(OBJ)
    $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
compile : $(TARGET)
clean:
    rm $(TARGET) obj $(OBJ) -rf
debug:
    echo $(CC)
    echo $(SRC)
    echo $(INC)
    echo $(OBJ)
    echo $(TARGET)
    echo $(CFLAGS)
    echo $(LDFLAGS)
    echo $(LIBS)
.PHONY: clean compile debug

4. 验证:


编译通过后,到obj 发现已经生成了我们的可执行文件


5. 拷贝到我们的香橙派中:

scp garbage orangepi@192.168.1.11:/home/orangepi

6.执行:

(请确保相关服务已经打开,比如我程序里面调用的是 mjpg-stream 来实现拍照功能,那么执行之前,这个服务纪就要确保打开)

-l(l)  -I(i) 参数补充

-l(l) 参数补充

定义:

在 GCC(GNU Compiler Collection)中,-l 参数用于指定在链接阶段要链接的库(library)。当你编译一个程序并且它依赖于某个库时,你需要告诉 GCC 在链接阶段将这个库包含进来。

-l 参数后面通常跟着库的名字,但需要注意的是,你不需要指定库文件的前缀(如 lib或后缀(如 .so.a 等)。GCC 会自动根据系统配置和库文件的实际位置来查找这些库。

例如,如果你的程序依赖于数学库 libm.so(或 libm.a,取决于你的系统和编译选项),你应该在编译命令中使用 -lm 而不是 -llibm

以下是一个简单的示例:

 

bash复制代码

gcc myprogram.c -o myprogram -lm

在这个例子中,myprogram.c 是你的源代码文件,-o myprogram 指定输出文件的名称,而 -lm 告诉 GCC 在链接阶段链接数学库。

需要注意的是,链接库的顺序有时很重要。如果库之间有依赖关系,你需要先链接依赖的库,然后再链接依赖于它的库。但是,对于大多数常见的库(如数学库、标准 C 库等),这个顺序通常不是问题。

此外,如果你知道库文件的实际路径,你也可以使用 -L 参数来指定库的搜索路径。例如:

 

bash复制代码

gcc myprogram.c -o myprogram -L/path/to/libs -lmylib

在这个例子中,-L/path/to/libs 告诉 GCC 在 /path/to/libs 目录下搜索库文件,而 -lmylib 指定要链接的库名为 mylib(即 libmylib.so 或 libmylib.a)。

-I (-i)参数补充

在 gcc(GNU Compiler Collection)中,-I 参数(注意是大写的 I)用于指定编译器搜索头文件(header files)的额外目录。

当你在 C 或 C++ 代码中包含(#include)一个头文件时,编译器需要找到这个头文件的实际位置以便能够正确地编译代码。默认情况下,编译器会在一些标准的位置(如 /usr/include 和 /usr/local/include)中查找头文件。但是,如果你的头文件不在这些标准位置中,你就需要使用 -I 参数来告诉编译器在哪里可以找到这些头文件。

例如,如果你的头文件位于 /home/user/myproject/include 目录中,你可以在编译时添加 -I/home/user/myproject/include 来告诉编译器在那里查找头文件:

 

bash复制代码

gcc -I/home/user/myproject/include myfile.c -o myprogram

在这个例子中,编译器会在 /home/user/myproject/include 目录中查找 myfile.c 中包含的任何头文件。

-I 参数对于组织大型项目或当头文件位于非标准位置时特别有用。通过使用 -I 参数,你可以确保编译器能够找到你的项目所需的所有头文件,而不管它们实际存储在哪里。

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

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

相关文章

如何从0深入PostgreSQL内核写一个执行器算子?

如何从0深入PostgreSQL内核写一个执行器算子&#xff1f; 大家好&#xff0c;我叫光城&#xff0c;昨天分享了一个主题&#xff1a;如何从0深入PostgreSQL内核写一个执行器算子&#xff1f;今天来总结一下&#xff0c;本篇文章的直播回放可以在b站观看&#xff0c;点击原文或者…

[PS小技能学习]抠图和切图

详情见视频教程&#xff1a;PS小技巧--抠图与切图 今天我们来学习如何使用PS对表情包合辑进行抠图和裁剪保存 1、首先&#xff0c;将图片导入&#xff0c;双击图层新建一个图层 2、然后点击工具栏的魔棒工具&#xff0c;再点击顶部菜单栏的添加到选区 3、点击图片的空白区域即…

IMU状态预积分功能实现与测试

IMU状态预积分功能实现与测试 前言实现IMU状态预积分类测试程序验证预积分与直接积分的效果结果 前言 预积分&#xff1a;是一种十分常见的IMU数据处理方法。 与传统的IMU运动学积分不同&#xff0c;预积分可以将一段时间内的IMU测量数据累积&#xff0c;建立预积分测量&#…

两院院士泌尿外科专家吴阶平教授

吴阶平&#xff08;1917-2011&#xff09;&#xff0c;男&#xff0c;江苏常州人&#xff0c;1933年天津汇文中学毕业&#xff0c;保送到北平燕京大学医预科&#xff0c;1937年毕业于北平燕京大学获理学士学位&#xff0c;1942年毕业于北平协和医学院获医学博士学位&#xff0c…

银行卡归属地查询API接口快速对接

银行卡归属地查询API接口指的是通过银行卡号查询该银行卡详细信息&#xff0c;包括银行卡名称、卡种、卡品牌、发卡行、编号以及归属地等信息&#xff0c;支持一千多家银行返回归属地信息&#xff0c;那么银行卡归属地查询API接口如何快速对接呢&#xff1f; 首先找到有做银行…

SpringBoot集成Kafka开发

4.SpringBoot集成Kafka开发 4.1 创建项目 4.2 配置文件 application.yml spring:application:name: spring-boot-01-kafka-basekafka:bootstrap-servers: 192.168.2.118:90924.3 创建生产者 package com.zzc.producer;import jakarta.annotation.Resource; import org.spri…

Thread类及常见方法

目录 1.Thread类概念 2.Thread的常见构造方法 3.Thread的几个常见属性 4.启动一个线程—start( ) 5.中断一个线程 1.使用自定义的变量来作为标志位 2.使用interrupt() 3.观察标志位是否被清除 6.等待一个线程-join() 7.获取当前线程引用 8.休眠当前线程 1.Thread类概…

GitHub Copilot 简单使用

因为公司安全原因&#xff0c;并不允许在工作中使用GitHub Copilot&#xff0c;所以&#xff0c;一直没怎么使用。最近因为有一些其它任务&#xff0c;所以&#xff0c;试用了一下&#xff0c;感觉还是很不错的。&#xff08;主要是C和Python编程&#xff09; 一&#xff1a;常…

探索洗牌算法的魅力与杨辉三角的奥秘:顺序表的实际运用

目录 目录 前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 洗牌算法 准备工作 买一副牌 洗牌 发牌 测试整体 &#x1f3af;&#x1f3af;很重要的一点 杨辉三角 总结 前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ …

06_电子设计教程基础篇(学习视频推荐)

文章目录 前言一、基础视频1、电路原理3、模电4、高频电子线路5、电力电子技术6、数学物理方法7、电磁场与电磁波8、信号系统9、自动控制原理10、通信原理11、单片机原理 二、科普视频1、工科男孙老师2、达尔闻3、爱上半导体4、华秋商城5、JT硬件乐趣6、洋桃电子 三、教学视频1…

分布式与一致性协议之Raft算法与一致哈希算法(一)

Raft算法 Raft与一致性 有很多人把Raft算法当成一致性算法&#xff0c;其实它不是一致性算法而是共识算法&#xff0c;是一个Multi-Paxos算法&#xff0c;实现的是如何就一系列值达成共识。并且&#xff0c;Raft算法能容忍少数节点的故障。虽然Raft算法能实现强一致性&#x…

相机知识的补充

一&#xff1a;镜头 1.1MP的概念 相机中MP的意思是指百万像素。MP是mega pixel的缩写。mega意为一百万&#xff0c;mega pixel 指意为100万像素。“像素”是相机感光器件上的感光最小单位。就像是光学相机的感光胶片的银粒一样&#xff0c;记忆在数码相机的“胶片”&#xff…

如何使用Go语言进行并发安全的数据访问?

文章目录 并发安全问题的原因解决方案1. 使用互斥锁&#xff08;Mutex&#xff09;示例代码&#xff1a; 2. 使用原子操作&#xff08;Atomic Operations&#xff09;示例代码&#xff1a; 3. 使用通道&#xff08;Channels&#xff09; 在Go语言中&#xff0c;进行并发编程是常…

buuctf-misc-23.FLAG

23.FLAG 题目&#xff1a;stegsolve得出PK-zip文件&#xff0c;改后缀名为zip,解压后查看文件类型为ELF 使用kali-strings或者ida获取flag 点击Save Bin将其另存为一个zip文件 而后解压我们另存的这个1234.zip文件后&#xff0c;可以得到 我们用ida打开它&#xff0c;打开后就…

《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

1、概述 源码放在文章末尾 该项目实现了带动画、带交互的折线图&#xff0c;包含如下特点&#xff1a; 动态增删数值 自适应显示坐标轴数值 鼠标悬浮显示十字对准线 鼠标靠近点自动贴附 支持直线与平滑曲线效果 自定义点的显示类型与大小 自适应点的数值显示位置 根据指定锚点…

stm32f103c8t6学习笔记(学习B站up江科大自化协)-PWR电源控制

PWR简介 PVD可用在电池供电或安全要求比较高的设备&#xff0c;如果供电电压在逐渐下降&#xff0c;在电压过低的情况下可能会导致内外电路出现不确定的错误。为了避免不必要的错误&#xff0c;可以在电源电压过低的情况下&#xff0c;提前发出警告并关闭较为危险的设备 关闭的…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

【C++题解】1039. 求三个数的最大数

问题&#xff1a;1039. 求三个数的最大数 类型&#xff1a;多分支结构 题目描述&#xff1a; 已知有三个不等的数&#xff0c;将其中的最大数找出来。 输入&#xff1a; 输入只有一行&#xff0c;包括3个整数。之间用一个空格分开。 输出&#xff1a; 输出只有一行&#…

uni-app scroll-view隐藏滚动条的小细节 兼容主流浏览器

开端 想写个横向滚动的列表适配浏览器&#xff0c;主要就是隐藏一下滚动条在手机上美观一点。 但是使用uni-app官方文档建议的::-webkit-scrollbar在目标标签时发现没生效。 .scroll-view_H::-webkit-scrollbar{display: none; }解决 F12看了一下&#xff0c;原来编译到浏览…

Day27:阻塞队列、Kafka入门、发送系统通知、显示系统

阻塞队列BlockingQueue BlockingQueue 解决线程通信的问题。阻塞方法:put、take。 生产者消费者模式 生产者:产生数据的线程。消费者:使用数据的线程。 &#xff08;Thread1生产者&#xff0c;Thread2消费者&#xff09; 实现类 ArrayBlockingQueueLinkedBlockingQueuePr…