一文搞懂系列——动态库的加载方式及应用场景

引文

        我们在工作中经常会遇到动态库链接的问题,因为正常的方式并不能满足我们的场景。常见的问题可以总结如下:

  • 系统路径默认路径、usr/lib、/lib 目录,不会集成第三方动态库。
  • 同名动态库可能在多个路径中存在。

        针对不同的场景,根据链接器的加载逻辑,进行相应的处理。

程序的加载流程

        根据专栏《程序员的自我修养》中的【程序员的自我修养02】初识ELF文件格式-CSDN博客可知,可执行文件的运行流程简述如下:

  1. 操作系统加载ELF的文件头。以检查文件格式、操作权限等属性。
  2. 根据文件头中的段表地址,定位到各个段内容。将各个段映射到虚拟地址中。
  3. 查找依赖的动态库并加载
  4. 进入文件头中的Entry point address。执行业务代码。

        其中链接器查找依赖的动态库并加载,这个流程实质很复杂,后续我会在《程序员的自我修养》专栏中详细介绍。本文我们只关心链接器如何去找动态库

问:链接器如何知道可执行程序依赖哪些动态库呢?

答:ELF文件中有一个段.dynamic。这个段里面保存了动态链接器所需要的基本信息,比如依赖哪些动态库动态链接符号表的位置动态链接重定位表的位置共享对象初始化代码的地址等。我们可以通过readelf -d main命令查看该段内容,如下:

Dynamic section at offset 0xda8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [liba.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [/home/yihua/]
 0x000000000000000c (INIT)               0x5b8
 0x000000000000000d (FINI)               0x794
 0x0000000000000019 (INIT_ARRAY)         0x200d98
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200da0
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x298
 0x0000000000000005 (STRTAB)             0x3f0
 0x0000000000000006 (SYMTAB)             0x2d0
 0x000000000000000a (STRSZ)              182 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x200fb8
 0x0000000000000002 (PLTRELSZ)           24 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x5a0
 0x0000000000000007 (RELA)               0x4e0
 0x0000000000000008 (RELASZ)             192 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x0000000000000018 (BIND_NOW)
 0x000000006ffffffb (FLAGS_1)            Flags: NOW PIE
 0x000000006ffffffe (VERNEED)            0x4c0
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x4a6
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0

        如上可知:main 可执行程序依赖 两个动态库 liba.solibc.so.6。其中黄色字体0x000000000000000f (RPATH)              Library rpath: [/home/yihua/]表示链接器查找动态库的路径,其优先级最高。需要我们关注。其它的参数暂不考虑。

        查看依赖的动态库还可以通过ldd main命令或objdump -x main | grep NEEDED命令。

知道可执行程序依赖哪些动态库口,动态链接器就需要去找这些动态库,查找的方式主要有以下四种。

  • 程序指定路径:rpath
  • 环境变量:LD_LIBRARY_PATH
  • 动态链接器配置文件:/etc/ld.so.conf
  • 系统默认路径:/lib 、 /usr/lib

        其动态链接器加载的顺序分别是rpath --> LD_LIBRARY_PATH -->  /etc/ld.so.conf  --> /lib 、 /usr/lib。

知道四个方式后,我们尝试了解如何使用。本文的示例代码如下:

//a.c
#include<stdio.h>
int a()
{
        printf("i'm liba.a\n");
}
// main.c
#include<stdlib.h>
#include<stdio.h>
extern int a();
int main()
{
        a();
        return 0;
}

编译:

C
yihua@ubuntu:~/test/dynamic$ gcc -FPIC -shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la

集成:分别将 main 和 liba.so 放入到bin 和lib目录下。

Shell
yihua@ubuntu:~/test/dynamic$ tree
.
├── a.c
├── bin
│   └── main
├── lib
│   └── liba.so
└── main.c

运行:

Shell
yihua@ubuntu:~/test/dynamic$ ./bin/main
./bin/main: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory

如上错误,是因为动态链接器没有找到liba.so导致的。可通过以下四种方式修复。

-Wl,-rpath

        该方式是通过在编译阶段,修改main 可执行程序中的dynamic段达到目的。可查看当前main的dynamic段内容:

        是没有RPATH参数的。可通过如下编译命令:

yihua@ubuntu:~/test/dynamic$ gcc -FPIC -shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la -Wl,-rpath=/home/yihua/test/dynamic/lib

再查看dynamic段内容:

运行:

拓展:

        -Wl,-rpath是编译阶段修改可执行程序的rpath参数,但是往往我们在工程中是不太确认最终的集成路径的。可以在集成时,采用chrpath命令,修改可执行程序的rpath参数。如下:

yihua@ubuntu:~/test/dynamic$ chrpath -r ./lib/ bin/main    //修改rpath
bin/main: RPATH=/home/yihua/
bin/main: new RPATH: ./lib/
yihua@ubuntu:~/test/dynamic$ chrpath -l bin/main   //查看rpath
bin/main: RPATH=./lib/

完结撒花~~~

LD_LIBRARY_PATH

        环境变量LD_LIBRARY_PATH是我们最最常用的方式,大部分情况下,我们使用该方式即可。使用方式如下:

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/yihua//test/dynamic/lib。编译如下:

yihua@ubuntu:~/test/dynamic$ gcc -FPIC -shared -o liba.so a.c
yihua@ubuntu:~/test/dynamic$ gcc main.c -o main -L. -la

集成:分别将 main 和 liba.so 放入到bin 和lib目录下。

运行:

完结撒花~~~

/etc/ld.so.conf

        该配置文件是系统动态链接器加载的配置文件。我们可以重新创建一个窗口(目的是关闭上述的LD_LIBRARY_PATH环境变量)。修改/etc/ld.so.conf文件,如下:

运行:

        发现依然没有找到动态库,这是为什么呢?我们可以通过strace ./bin/main命令查看程序的加载流程。输出如下:

yihua@ubuntu:~/test/dynamic$ strace ./bin/main
execve("./bin/main", ["./bin/main"], 0x7ffdfd030ae0 /* 26 vars */) = 0
brk(NULL)                               = 0x5643c65c3000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=104673, ...}) = 0
mmap(NULL, 104673, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ff91b2ed000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/tls/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/tls/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/tls/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/tls", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64-linux-gnu", {st_mode=S_IFDIR|0755, st_size=16384, ...}) = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/tls/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/tls/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/tls/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/tls/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/tls/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/tls", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64-linux-gnu", {st_mode=S_IFDIR|0755, st_size=81920, ...}) = 0
openat(AT_FDCWD, "/lib/tls/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/tls/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/tls/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/tls/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/tls/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/tls", 0x7fffcd77f4e0)        = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/haswell", 0x7fffcd77f4e0)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib/x86_64", 0x7fffcd77f4e0)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
openat(AT_FDCWD, "/usr/lib/tls/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls", 0x7fffcd77f4e0)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/haswell/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/haswell/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/haswell/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/haswell", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7fffcd77f4e0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/liba.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
writev(2, [{iov_base="./bin/main", iov_len=10}, {iov_base=": ", iov_len=2}, {iov_base="error while loading shared libra"..., iov_len=36}, {iov_base=": ", iov_len=2}, {iov_base="liba.so", iov_len=7}, {iov_base=": ", iov_len=2}, {iov_base="cannot open shared object file", iov_len=30}, {iov_base=": ", iov_len=2}, {iov_base="No such file or directory", iov_len=25}, {iov_base="\n", iov_len=1}], 10./bin/main: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
) = 117
exit_group(127)                         = ?
+++ exited with 127 +++

        我们从输出结果,可以知道,动态链接器只加载/etc/ld.so.cache配置文件,并没有加载/etc/ld.so.conf配置文件。因此,我们需要通过ldconfig命令更新ld.so.cache文件内容。

如下:

完结撒花~~~

系统默认路径

        系统默认路径即系统存放动态库的地方,一般为/lib、/usr/lib。我们只需要将动态库放到对应的路径下即可。

        重新打开一个窗口,执行如下命令运行:

        完结撒花~~~

总结

        综上所述,我们知道了动态链接库寻找动态库的四种方式,其中:

        系统默认路径和/etc/ld.so.conf需要系统权限,大部分情况是不能进行修改的。若有相应权限,可以优先使用该方式。

        环境变量LD_LIBRARY_PATH可以解决我们大部分场景,比如引文中的第一个场景。但是它并不解决动态库重名的问题。比如引文中的第二个场景。这时,我们就可以采用-Wl,-rpath 编译选项或chrpath修改可执行程序的rpath参数。

        希望本文能够对您有所帮助,还请三连表示支持哦~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

替代AMS1117-ADJ可调输出线性稳压器(LDO)

1、概 述 PC1117-ADJ/1.2/1.5/1.8/2.5/2.85/3.3/5是最大输出电流为1A的低压降正向稳压器&#xff0c;其中 PC1117-ADJ是可调输出电压版&#xff0c;只需要两个外接电阻即可实现输出电压在1.25V~13.8V范围内的调节&#xff0c;而PC1117-1.2/1.5/1.8/2.5/2.85/3.3/5是固定输出1.…

【陈老板赠书活动 - 19期】-2023年以就业为目的学习Java还有必要吗?

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;赠书活动专栏&#xff08;为大家争取的福利&#xff0c;免费送书&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&am…

vector向量详解,小白快速入门

1.vector是什么 vector名为向量&#xff0c;其实就是一个长度可变的数组 是连续的顺序的储存结构&#xff08;和数组一样的类别&#xff09;&#xff0c;但是有长度可变的特性。 2.vector的初始化 vector<int> v; 一维可变数组&#xff0c;类型为int&#xff0c;名称…

xampp环境安装

XAMPP是完全免费且易于安装的Apache发行版&#xff0c;其中包含Apache、MariaDB、PHP和Perl。 类似XAMPP的服务器套件还有很多&#xff0c;我用过的还有UPUPW&#xff0c;它们都极大的简化了开发环境的配置。 下载链接Download XAMPP 我选的最新的 一路next就安装好了。

Cesium 太阳光晕

Cesium 太阳光晕 基于后处理实现位置动态跟随太阳实际位置可以动态改变颜色 viewer.camera.flyTo({destination: { "x": -2471386.549378386, "y": 4838798.836366257, "z": 3329936.5717575867 },duration: 0,orientation: {heading: Cesium.M…

蓝桥杯真题:分巧克力(二分法)

由题目可知,该题的最终结果具有单调性,边长越大,可分蛋糕越少 可以用二分模板的向右找: 整数二分 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class Main {static int n,k; //n个块蛋糕,k个学生static int N 10…

【开源】基于Vue.js的人事管理系统

文末获取源码&#xff0c;项目编号&#xff1a; S 079 。 \color{red}{文末获取源码&#xff0c;项目编号&#xff1a;S079。} 文末获取源码&#xff0c;项目编号&#xff1a;S079。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员功能模块2.2 普通员工功能模块…

【网络安全】虚假IP地址攻击如何防范?

在当今的网络时代&#xff0c;虚假IP地址攻击已成为一种新型的网络攻击方式&#xff0c;给网络安全带来了极大的威胁。那么&#xff0c;什么是虚假IP地址攻击&#xff1f;又如何进行溯源和防范呢&#xff1f;本文将为您揭开这一神秘面纱。 一、虚假IP地址攻击概述 虚假IP地址攻…

ISP算法简述-BLC

Black Level Calibration, 黑电平矫正 现象 1)在纯黑条件下拍张图&#xff0c;你会发现像素值不为0 2)或者你发现图像整体偏色 这些问题可能是黑电平导致的。 原因 存在黑电平的原因有2个&#xff1a; 1)sensor的电路本身存在暗电流。暗电流主要产生在光电信号转换过程中&#…

quickapp_快应用_生命周期

生命周期 APP的生命周期页面组件的生命周期页面栈页面的生命周期onBackPressonMenuPress踩坑 onRefreshonConfigurationChanged页面滚动 自定义组件的生命周期父子组件初始化生命周期执行顺序 APP的生命周期 App的生命周期在app.ux 中定义的回调函数。 onCreate() {prompt.sh…

Apache solr XXE 漏洞(CVE-2017-12629)

任务一&#xff1a; 复现环境中的漏洞 任务二&#xff1a; 利用XXE漏洞发送HTTP请求&#xff0c;在VPS服务器端接受请求&#xff0c;或收到DNS记录 任务三&#xff1a; 利用XXE漏洞读取本地的/etc/passwd文件 1.搭建环境 2.开始看wp的时候没有看懂为什么是core&#xff0c;然…

动能芯片 | SI3262—高度集成的低功耗SOC芯片 刷卡触摸一体

Si3262是一款高度集成的低功耗SOC芯片&#xff0c;其集成了基于RISC-V核的低功耗MCU和工作在13.56MHz的非接触式读写器模块。 MCU模块具有低功耗、Low Pin Count、宽电压工作范围&#xff0c;集成了13/14/15/16位精度的ADC、LVD、UART、SPI、I2C、TIMER、WUP、IWDG、RTC、TSC等…

【ArcGIS Pro微课1000例】0046:深度学习--汽车检测

本实验讲述ArcGIS Pro中人工智能深度学习应用之–汽车检测。 文章目录 一、学习效果二、工具介绍三、案例实现四、注意事项一、学习效果 采用深度学习工具,可以很快速精准的识别汽车。 案例一: 案例二: 下面讲解GIS软件实现流程。 二、工具介绍 该案例演示的是ArcGIS Pro中…

C++ Easyx 让圆球跟随鼠标移动

目录 下载Easyx 检验 绘制窗口 画圆 响应事件的处理 清除原先绘图 渲染缓冲区 逻辑 代码托管 下载Easyx 在Easyx官网下载大暑版: 检验 写如下代码: 编译运行&#xff0c;如果控制台出现2023字样&#xff0c;代表配置成功: 绘制窗口 进入Eaxy官方网站&#xff0c;点…

51单片机项目(20)——基于51单片机的电机速度PID控制

1.功能设计 使用51单片机&#xff0c;控制电机速度&#xff0c;用了PID算法。有数码管显示实时速度&#xff0c;可以用按键设定速度。数码管也显示设定的预期速度。另外&#xff0c;还可以控制电机的换向和启停。 2.仿真图 3.PID算法介绍 PID算法&#xff0c;即Proportional-I…

【cmake】获取到某个目录下的所有子目录名

整体工程目录结构如下。现打算获取到vac目录下的所有子目录名。 cmake 实现如下: # 设定要遍历的目录&#xff0c;保存到 VAC_INCLUDE_DIR 变量 set(VAC_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/vac)# 获取到当前目录下的所有文件和目录&#xff08;以相对路径的方式&#xff09;&a…

NFTScan | 11.27~12.03 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2023.11.20~ 2023.11.26 NFT Hot News 01/ Web3 教育平台 Open Campus 获 Binance Labs 315 万美元投资 11 月 27 日&#xff0c;Binance Labs 已向社区主导的 Web3 教育平台 Open Campu…

2023-简单点-树莓派中的硬件通讯

树莓派中的通讯方式 串口通讯什么是串口通讯&#xff1f;串口设备的格式串口通讯的特点 tips并行通讯&#xff1f;基于网络的通讯?socket通讯 串口通讯 什么是串口通讯&#xff1f; 串行通信每次传输一个位元数据&#xff0c;并在连续进行单次过程的基础上进行通信。根据数据…

windows 11 家庭版怎样安装docker

这里写自定义目录标题 一、安装wsl1、开启硬件虚拟化2、安装wsl3.升级WSL到WSL24、下载安装linux分支5、测试运行6、出现如下问题7、迁移到非系统盘8、文件资源管理器9、配置网络10、更新和升级 一、安装wsl 1、开启硬件虚拟化 检查是否开启Hyper-V, 适用于Linux的Windows子系…

揭秘:大厂设计师是如何制定UI风格的?

当你碰到一个新的项目或产品战略需要进行重大的改变时&#xff0c;作为UI设计师&#xff0c;你要如何重新思考产品的视觉风格&#xff1f;从何处开始&#xff1f;存在哪些重要注意点&#xff1f;今天我们有幸请到Pixso的设计师&#xff0c;他们将以出租车应用程序的风格设计过程…