一、简介
NXP i.MX6ULL Pro 工业级嵌入式开发板,系统记录 Linux 环境下 C/C++ 编程语言的实战开发过程。i.MX6ULL Pro 搭载 ARM Cortex-A7 内核,兼具低功耗、高性价比特性,是嵌入式 Linux 开发的主流入门与工程实践载体,其 Linux 系统适配性与硬件资源(GPIO、串口、I2C、SPI 等)的开放性,为底层驱动与上层应用开发提供了典型场景。
本记录以实战为核心,涵盖开发环境搭建(交叉编译工具链配置、开发板系统烧录)、Linux C/C++ 基础编程(文件 I/O、进程 / 线程、网络编程)、底层硬件驱动封装(基于 sysfs / 设备树的外设控制)、调试技巧及工程化编程规范等核心内容。既梳理编程过程中的关键步骤、问题排查思路,也总结嵌入式 Linux C/C++ 开发的通用方法,旨在沉淀基于 i.MX6ULL Pro 开发板的实战经验,为嵌入式开发者提供可复用的编程参考,降低该开发板的 Linux C/C++ 开发门槛。
二、编译内核过程记录
2.1 配置并编译内核
到百问网给的资料里的虚拟机的Linux内核源码(带韦东山老师的100ask_imx6ull_defconfig配置文件)顶层make,注意提前声明arm toolchains和arm arch
bash
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/Linux-4.9.88$ export ARCH=arm
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/Linux-4.9.88$ cd ..
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk$ cd ^[[200~ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-
linux-gnueabihf/bin~^C
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk$ cd ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin$ ls
arm-linux-gnueabihf-addr2line arm-linux-gnueabihf-dwp arm-linux-
gnueabihf-gcc-nm arm-linux-gnueabihf-gprof arm-linux-gnueabihf-
objdump gdbserver
arm-linux-gnueabihf-ar arm-linux-gnueabihf-elfedit arm-linux-
gnueabihf-gcc-ranlib arm-linux-gnueabihf-ld arm-linux-gnueabihf-
ranlib runtest
arm-linux-gnueabihf-as arm-linux-gnueabihf-g++ arm-linux-
gnueabihf-gcov arm-linux-gnueabihf-ld.bfd arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-c++ arm-linux-gnueabihf-gcc arm-linux-
gnueabihf-gcov-tool arm-linux-gnueabihf-ld.gold arm-linux-gnueabihf-size
arm-linux-gnueabihf-c++filt arm-linux-gnueabihf-gcc-6.2.1 arm-linux-
gnueabihf-gdb arm-linux-gnueabihf-nm arm-linux-gnueabihf-strings
arm-linux-gnueabihf-cpp arm-linux-gnueabihf-gcc-ar arm-linux-
gnueabihf-gfortran arm-linux-gnueabihf-objcopy arm-linux-gnueabihf-strip
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin$ pwd
/home/zky/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/100ask_imx6ull-sdk
/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin$ export CROSS_COMPILE=/home/zky/ProjectsHub/Linux_Projects/
100ask_imx6ull_pro_Env/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-
x86_64_arm-linux-gnueabihf/bin
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin$ echo $CROSS_COMPILE
/home/zky/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/100ask_imx6ull-sdk
/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-
gnueabihf/bin$
cd /home/zky/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/100ask_imx6ull-
sdk/Linux-4.9.88
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/Linux-4.9.88$ make 100ask_imx6ull_defconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/basic/bin2c
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/zconf.tab.o
In file included from scripts/kconfig/zconf.tab.c:2578:
In function 'dep_stack_insert',
inlined from 'sym_check_print_recursive' at scripts/kconfig/symbol.c:
1103:3,
inlined from 'sym_check_deps' at scripts/kconfig/symbol.c:1280:3:
scripts/kconfig/symbol.c:1079:19: warning: storing the address of local
variable 'cv_stack' in 'check_top' [-Wdangling-pointer=]
1079 | check_top = stack;
| ~~~~~~~~~~^~~~~~~
scripts/kconfig/symbol.c: In function 'sym_check_deps':
scripts/kconfig/symbol.c:1100:26: note: 'cv_stack' declared here
1100 | struct dep_stack cv_stack;
| ^~~~~~~~
scripts/kconfig/symbol.c:1070:4: note: 'check_top' declared here
1070 | } *check_top;
| ^~~~~~~~~
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/
100ask_imx6ull-sdk/Linux-4.9.88$
2.2 查询卡发板的内核版本号
方法一:
bash
uname -r
方法二:
bash
uname -a
方法三:
bash
cat /proc/version

三、在arm板子上进行wifi连接
开发板连接wifi
bash
killall wpa_supplicant
ifconfig -a
ifconfig wlan1 up
iwlist wlan1scanning
vi /etc/wpa_supplicant.conf
# 输入
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="wifi ssid"
psk="wifi password"
key_mgmt=WPA-PSK
}
wpa_supplicant -B -i wlan1 -c /etc/wpa_supplicant.conf
# 如果是
sh-5.0# wpa_supplicant -B -i wlan1 -c /etc/wpa_supplicant.conf
Successfully initialized wpa_supplicant
rfkill: Cannot open RFKILL control device
# 成功
# 得到ip
udhcpc -i wlan1
sh-5.0# udhcpc -i wlan1
udhcpc: started, v1.31.1
udhcpc: sending discover
udhcpc: sending select for 192.168.3.41
udhcpc: lease of 192.168.3.41 obtained, lease time 86400
deleting routers
adding dns 192.168.3.1
sh-5.0#
wifi连接过程记录:
(1)记录1
bash
[root@100ask:~]# ifconfig
eth0 Link encap:Ethernet HWaddr 00:01:3F:2D:3E:4D
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
eth1 Link encap:Ethernet HWaddr 00:01:1F:2D:3E:4D
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:480 errors:0 dropped:0 overruns:0 frame:0
TX packets:480 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:35520 (34.6 KiB) TX bytes:35520 (34.6 KiB)
wlan0 Link encap:Ethernet HWaddr A8:B5:8E:DA:0E:F4
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:155 overruns:0 frame:0
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:133 (133.0 B) TX bytes:0 (0.0 B)
wlan1 Link encap:Ethernet HWaddr AA:B5:8E:DA:0E:F4
inet addr:192.168.3.41 Bcast:192.168.3.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:219 errors:0 dropped:40 overruns:0 frame:0
TX packets:100 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:22801 (22.2 KiB) TX bytes:11284 (11.0 KiB)
[root@100ask:~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN group default qlen 10
link/can
3: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 00:01:3f:2d:3e:4d brd ff:ff:ff:ff:ff:ff
4: eth1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 00:01:1f:2d:3e:4d brd ff:ff:ff:ff:ff:ff
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
link/sit 0.0.0.0 brd 0.0.0.0
6: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
link/ether a8:b5:8e:da:0e:f4 brd ff:ff:ff:ff:ff:ff
7: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether aa:b5:8e:da:0e:f4 brd ff:ff:ff:ff:ff:ff
inet 192.168.3.41/24 brd 192.168.3.255 scope global wlan1
valid_lft forever preferred_lft forever
[root@100ask:~]#
(2)记录2
bash
# 连接wifi
[root@100ask:/]# killall -9 wpa_supplicant 2>/dev/null
[root@100ask:/]# rm -f /var/run/wpa_supplicant/*
[root@100ask:/]# ip link set wlan1 up
[root@100ask:/]# vi /etc/wpa_supplicant.conf
[root@100ask:/]# wpa_supplicant -B -i wlan1 -c /etc/wpa_supplicant.conf
Successfully initialized wpa_supplicant
rfkill: Cannot open RFKILL control device
[root@100ask:/]# [ 4477.318476] IPv6: ADDRCONF(NETDEV_CHANGE): wlan1: link becomes ready
udhcpc -i wlan1 -q
udhcpc: started, v1.31.1
udhcpc: sending discover
udhcpc: sending select for 192.168.3.41
udhcpc: lease of 192.168.3.41 obtained, lease time 86400
deleting routers
adding dns 192.168.3.1
[root@100ask:/]# ping -c 3 www.baidu.com
PING www.baidu.com (183.240.99.224): 56 data bytes
64 bytes from 183.240.99.224: seq=0 ttl=51 time=40.925 ms
64 bytes from 183.240.99.224: seq=1 ttl=51 time=40.669 ms
64 bytes from 183.240.99.224: seq=2 ttl=51 time=56.227 ms
--- www.baidu.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 40.669/45.940/56.227 ms
[root@100ask:/]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN group default qlen 10
link/can
3: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 00:01:3f:2d:3e:4d brd ff:ff:ff:ff:ff:ff
4: eth1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 00:01:1f:2d:3e:4d brd ff:ff:ff:ff:ff:ff
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
link/sit 0.0.0.0 brd 0.0.0.0
6: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
link/ether a8:b5:8e:da:0e:f4 brd ff:ff:ff:ff:ff:ff
7: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether aa:b5:8e:da:0e:f4 brd ff:ff:ff:ff:ff:ff
inet 192.168.3.41/24 brd 192.168.3.255 scope global wlan1
valid_lft forever preferred_lft forever
[root@100ask:/]# cat /etc/wpa_supplicant.conf
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="abc"
psk="123456789"
key_mgmt=WPA-PSK
}
[root@100ask:/]#
四、基于 Linux 的 GCC 和 ARM GCC 编程学习记录




注:ARM GCC的使用方法与常用的GCC大体上差不多。
4.1 GCC 制作 静态库(.a 文件) 和 动态库(.so 文件)

在 Linux 环境下,GCC 可以通过一系列命令制作 静态库(.a 文件) 和 动态库(.so 文件) 。核心区别:静态库编译时会被 "嵌入" 程序,运行时无需依赖;动态库编译时仅记录引用,运行时需加载库文件(更节省磁盘和内存)。(这个静态库 和动态库我也在CMAKE文章( https://blog.csdn.net/qq_73648789/article/details/154009851?spm=1001.2014.3001.5501)中讲述过后续会使用Makefile或者CMake进行辅助帮助编译程序)
注意:动态库要跟着可执行文件走(运行时再加载),静态库就直接一起嵌入到可执行文件里了
4.1.1 静态库
bash
gcc -c simple_math.c -o simple_math.o
ar -rcs libsimple_math.a simple_math.o
gcc main.c -o main -L./ -lsimple_math
4.1.2 动态库
动态库和静态库的核心区别之一是「运行时依赖」:
- 静态库:编译时会被完整嵌入可执行文件,运行时无需原库文件;
- 动态库:编译时仅记录 "需要调用某个库的某个函数",运行时才会去系统指定的路径中加载动态库文件。
(1)成功步骤
bash
gcc -c -fPIC simple_math.c -o simple_math.o
gcc -shared -o libsimple_math.so simple_math.o
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
gcc main.c -o main -L. -lsimple_math
(2)如果出现当前的错误情况:
bash
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib$ ls
libsimple_math.so main main.c simple_math.c simple_math.h simple_math.o
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib$ ./main sub 3 2
./main: error while loading shared libraries: libsimple_math.so: cannot open shared object file: No such file or directory
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib$ ldd main
linux-vdso.so.1 (0x00007ffe3bfc7000)
libsimple_math.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b6d53600000)
/lib64/ld-linux-x86-64.so.2 (0x00007b6d538e0000)
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib$
-
动态库
libsimple_math.so放在当前目录(dynamic_lib/); -
系统默认不会在 "当前目录" 查找动态库(出于安全和规范考虑);
-
所以运行
./main时,系统找不到libsimple_math.so,直接报错 "无法打开共享对象文件"。ldd main # 查看main依赖的动态库
方法 1:临时生效(终端关闭后失效,适合测试)
通过 export 命令临时添加当前目录到动态库查找路径(LD_LIBRARY_PATH 是系统环境变量,指定动态库的额外查找路径):
bash
# 把当前目录(.)添加到 LD_LIBRARY_PATH 中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
方法 2:永久生效(仅对当前用户,推荐)
把 "添加动态库路径" 的命令写入用户配置文件(~/.bashrc),每次登录终端都会自动生效:
- 编辑
.bashrc文件:
nano ~/.bashrc # 用nano编辑器打开(也可以用vi/vim)
-
在文件末尾添加一行(注意把路径换成你实际的
dynamic_lib目录路径,可通过pwd命令获取):示例:假设你的dynamic_lib目录路径是 /home/zky/ProjectsHub/.../dynamic_lib
用 pwd 命令获取当前目录的绝对路径,替换下面的 <你的动态库目录绝对路径>
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<你的动态库目录绝对路径>
比如你当前目录的绝对路径是 /home/zky/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib,就添加:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/zky/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib
- 保存并生效配置:
source ~/.bashrc # 让配置立即生效,无需重启终端
- 验证:打开新终端,直接运行
./main sub 3 2,即可正常执行。
- 优点:一劳永逸,仅对当前用户生效,不影响其他用户;
- 缺点:如果动态库目录路径改变,需要重新修改
.bashrc。
注:这个方法就是像在linux下开发w800开发板时配置toolchains的路径给到当前用户的终端环境变量当中。
方法 3:系统级生效(所有用户可用,需 root 权限)(相当于放到(root)系统环境变量)
把动态库复制到系统默认的动态库目录(如 /usr/lib 或 /usr/local/lib),所有用户都能直接使用:
- 复制动态库到系统目录(需 sudo 权限):
sudo cp libsimple_math.so /usr/lib
- 更新系统动态库缓存(让系统识别新添加的动态库):
sudo ldconfig
-
直接运行程序,无需额外配置:
./main sub 3 2
-
优点:所有用户、所有终端都能直接使用,无需设置环境变量;
-
缺点:需要 root 权限,可能污染系统库目录(适合长期使用的通用库)。
注:有条件时是存在方法:将静态库或者动态库的API/函数名称dump出来
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/static_lib$ nm -g libsimple_math.a | grep ' T ' | awk '{print $3}'
add
sub
4.2 编译链接库前时、编译链接库时、运行链接静态库/动态库时
4.2.1 编译链接库前时
gcc -o 输出文件名 源文件1.c 源文件2.c ... -I 头文件所在目录路径
gcc -o main main.c simple_math.c -I./
4.2.2 编译链接库时
gcc -o 输出文件名 源文件1.c 源文件2.c ... -I 头文件所在目录路径 -L库所在目录路径 -l指定库名字(不带lib前缀 不带.a.so后缀)
gcc -o main main.c -I./include -L./lib -lsimple_math
| 参数 | 作用 | 对应你的场景 |
|---|---|---|
-I./include |
指定「头文件搜索路径」(找 simple_math.h) |
告诉 gcc 去 ./include 目录找函数声明 |
-L./lib |
指定「库文件搜索路径」(找 libsimple_math.a) |
告诉链接器去 ./lib 目录找库文件 |
-lsimple_math |
指定「要链接的库名」(库名简化规则) | 库文件名为 libxxx.a,简化为 xxx(去掉 lib 和 .a) |
4.2.3 运行链接静态库/动态库时
需要指定库的环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
| 场景 | 编译命令模板 |
|---|---|
| 链接当前目录静态库 | gcc -o 输出名 源文件.c -lsimple_math |
| 链接指定目录静态库 | gcc -o 输出名 源文件.c -I头文件目录 -L库目录 -lsimple_math |
| 链接当前目录动态库 | gcc -o 输出名 源文件.c -lsimple_math(运行需配置 LD_LIBRARY_PATH) |
| 链接指定目录动态库 | gcc -o 输出名 源文件.c -I头文件目录 -L库目录 -lsimple_math(运行需配置 LD_LIBRARY_PATH) |
如果要问为什么运行时也要库的路径支持,看下面的差别:
(1)在编译过程相同的前提下,指定库的环境变量后,ldd main 有simple_math库的位置,能执行。

(2)在编译过程相同的前提下,如果复制代码到一个新的文件夹,开一个新终端,同时不指定库的环境变量(不使用export和-L./ -lsimple_math),不能执行。(ldd是在找运行时需要的库文件路径)

(3)同时这里对于编译时指出动态库的位置和运行时却没有找到动态库的位置的区别再进行解释一次
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib_NO_LD_LIBRARY_PATH$ ldd main
linux-vdso.so.1 (0x00007ffcc41b5000)
libsimple_math.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000775d24a00000)
/lib64/ld-linux-x86-64.so.2 (0x0000775d24cbb000)
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib_NO_LD_LIBRARY_PATH$ gcc -o main main.c libsimple_math.so -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04)
COLLECT_GCC_OPTIONS='-o' 'main' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'main-'
/usr/libexec/gcc/x86_64-linux-gnu/13/cc1 -quiet -v -imultiarch x86_64-linux-gnu main.c -quiet -dumpdir main- -dumpbase main.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccvVcmMx.s
GNU C17 (Ubuntu 13.3.0-6ubuntu2~24.04) version 13.3.0 (x86_64-linux-gnu)
compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/13/include
/usr/local/include
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
Compiler executable checksum: 38987c28e967c64056a6454abdef726e
COLLECT_GCC_OPTIONS='-o' 'main' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'main-'
as -v --64 -o /tmp/ccX1HY0o.o /tmp/ccvVcmMx.s
GNU汇编版本 2.42 (x86_64-linux-gnu) 使用BFD版本 (GNU Binutils for Ubuntu) 2.42
COMPILER_PATH=/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/13/:/usr/libexec/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/13/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/13/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'main' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'main.'
/usr/libexec/gcc/x86_64-linux-gnu/13/collect2 -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccsCCyZ6.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o main /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. /tmp/ccX1HY0o.o libsimple_math.so -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
COLLECT_GCC_OPTIONS='-o' 'main' '-v' '-mtune=generic' '-march=x86-64' '-dumpdir' 'main.'
zky@zky-virtual-machine:~/ProjectsHub/Linux_Projects/100ask_imx6ull_pro_Env/Imx6ull_Pro_Examples_Workspaces/EX1_Linux_Application_Layer/E2_options/dynamic_lib_NO_LD_LIBRARY_PATH$
结论:编译可执行文件 main 时,gcc 成功找到了 libsimple_math.so 动态库 ------ 你的编译链接阶段完全没问题,最终生成了可执行文件 main;而 ldd main 显示 libsimple_math.so => not found,是「运行时」的库搜索问题,和「编译时」无关。
1)关键证据:从 gcc -v 输出日志验证「编译时找到库」
你用 gcc -o main main.c libsimple_math.so -v 编译时,日志的最后阶段(collect2 链接步骤)明确显示:
collect2 ... -o main ... /tmp/ccX1HY0o.o libsimple_math.so ...
这里的核心信息:
- collect2 是 gcc 的链接器前端,它的命令行中直接包含了 libsimple_math.so(作为输入文件之一,和编译生成的 .o 文件一起参与链接);
- 整个编译过程没有报错(比如 ld: cannot find libsimple_math.so),最终成功生成了 main 可执行文件 ------ 这是最直接的证据:如果编译时没找到库,链接器会直接报错终止,无法生成 main。
简单说:编译时,你直接在命令行中指定了 libsimple_math.so 的路径(当前目录),gcc 不需要通过 -L 搜索,直接使用了这个库文件完成链接 。
2)为什么 ldd main 显示「not found」?(编译时 vs 运行时的区别)
ldd 工具查看的是「运行时」动态库的依赖搜索结果,和「编译时」是两回事:
- 「编译时」:你明确告诉了 gcc 库文件的位置(当前目录的 libsimple_math.so),所以链接器能找到并完成链接;
- 「运行时」:系统加载 main 时,会按默认的「动态库搜索路径」(如 /lib、/usr/lib 等)找 libsimple_math.so,但当前目录不在系统默认搜索路径中,所以 ldd 显示「找不到」。
举个通俗的例子:
- 编译时:你把库文件「直接递给了编译器」,编译器拿到就用了;
- 运行时:系统需要自己「去固定的文件夹里找库」,但没找到你放在「当前文件夹」里的库。
3)补充:如果编译时没找到库,会发生什么?
如果编译时 gcc 没找到 libsimple_math.so,会直接报链接错误,无法生成 main,比如:
ld: cannot find libsimple_math.so: No such file or directory
collect2: error: ld returned 1 exit status
而你的编译过程没有任何这类错误,说明编译时库是找到的。
4)如何让运行时找到库?(延续之前的解决方法)
既然编译没问题,只需解决「运行时库搜索路径」即可,三选一:
-
临时生效(当前终端):
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./之后再运行 ./main 就能正常加载库,ldd main 会显示 libsimple_math.so => ./libsimple_math.so。
-
永久生效(当前用户):
echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$(pwd)" >> ~/.bashrc source ~/.bashrc -
复制库到系统目录(全局可用):
sudo cp libsimple_math.so /usr/lib
总结
- 编译时:成功找到 libsimple_math.so(证据:无链接错误、生成了 main、collect2 日志包含库文件);
- 运行时:找不到库(原因:当前目录不在系统动态库默认搜索路径);
- 解决:按上面的方法配置运行时路径,即可正常运行 main。
4.2.4 补充:强制链接静态库(若同时存在 .a 和 .so)
如果目录下同时有 libsimple_math.a 和 libsimple_math.so,链接器会默认选动态库,若想强制链接静态库,用 -static 参数:
gcc -o main main.c -I./ -L./ -static -lsimple_math
4.3 基于Linux内核提供内核态的文件流操作进一步封装到自定义的用户态的文件流操作,即Linux应用开发
4.3.1 查看并参考内核代码
查看man手册使用方法
man man

(1)在手册第二页找到IO操作函数
man 2 open

注:如果没有就翻页找或者问AI。
这里的手册里的open操作是封装好了的内核代码,具体的实现终究还是要去下载对应版本的源码才能知道细节,但是实际开发中,比如:这种供应商只给了手册和动态库(没有源码)的开发情况也不是没有,也可以提前适应一下。
实现如下:

4.3.2 实现open.c
(1)打开文件代码
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s <filename>\n", argv[0]);
return -1;
}
int fd = open(argv[1], O_RDWR);
if (fd < 0) {
printf("open %s failed, errno = %d\n", argv[1], errno);
printf("open %s failed, errno = %s\n", argv[1], strerror(errno));
perror("open");
return -1;
}
while (1) {
sleep(10);
}
close(fd);
return 0;
}
(2)查看运行进程下的对应线程,以及打开的文件描述符对应的文件是否是我们打开的文件

(3)创建不存在的文件并指定文件权限
bash
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s <filename>\n", argv[0]);
return -1;
}
int fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
printf("open %s failed, errno = %d\n", argv[1], errno);
printf("open %s failed, errno = %s\n", argv[1], strerror(errno));
perror("open");
return -1;
}
while (1) {
sleep(10);
}
close(fd);
return 0;
}
查看当前用户创建文件被限制的权限项
zky@zky-virtual-machine:/proc/25401/fd$ umask
0002
注:板子上的Linux应用也是差不多的调用内核的接口,使用arm gcc编译在板子上跑起来。
4.3.3 实现write.c
等待持续更新记录中
(如有侵权,请联系删除)
注意:
(1)参考的是韦东山老师的开放手册。
(2)提示一下,以前的文章当中就说过微软官方的c/c++ vscode插件,当导入了大工程文件时,会马上分析大量的索引,会非常吃内存,有时会导致非常卡顿,这里注意一下。

(3)如果要做正式Linux开发,一定要规范Linux代码风格,一定要重视,因为这是通用规范。
Linux 内核代码风格 --- The Linux Kernel documentation
(4)硬件平台:100ask Imx6ull Pro。
(5)开发方式:VSCode终端接入ssh链接虚拟机的Ubuntu;Ubuntu安装Samba后在本机Windows映射网络驱动器;Windows的VSCode打开网络驱动器里的文件,即可访问虚拟机的文件;板子的OTG链接到Ubuntu的adb服务;板子的串口链接到Windows的串口终端软件。