【Linux】【Imx6ull Pro】基于Imx6ull Pro开发板的Linux-C-C++编程记录

一、简介

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$
  1. 动态库 libsimple_math.so 放在当前目录(dynamic_lib/);

  2. 系统默认不会在 "当前目录" 查找动态库(出于安全和规范考虑);

  3. 所以运行 ./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  # 让配置立即生效,无需重启终端
  1. 验证:打开新终端,直接运行 ./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 ...

这里的核心信息:

  1. collect2 是 gcc 的链接器前端,它的命令行中直接包含了 libsimple_math.so(作为输入文件之一,和编译生成的 .o 文件一起参与链接);
  2. 整个编译过程没有报错(比如 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)如何让运行时找到库?(延续之前的解决方法)

既然编译没问题,只需解决「运行时库搜索路径」即可,三选一:

  1. 临时生效(当前终端):

    复制代码
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./

    之后再运行 ./main 就能正常加载库,ldd main 会显示 libsimple_math.so => ./libsimple_math.so。

  2. 永久生效(当前用户):

    复制代码
    echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$(pwd)" >> ~/.bashrc
    source ~/.bashrc
  3. 复制库到系统目录(全局可用):

    复制代码
    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

《Linux 内核编码风格》官方手册最新中译本 - 知乎

(4)硬件平台:100ask Imx6ull Pro。

(5)开发方式:VSCode终端接入ssh链接虚拟机的Ubuntu;Ubuntu安装Samba后在本机Windows映射网络驱动器;Windows的VSCode打开网络驱动器里的文件,即可访问虚拟机的文件;板子的OTG链接到Ubuntu的adb服务;板子的串口链接到Windows的串口终端软件。

相关推荐
赖small强2 小时前
【Linux 内存管理】Linux系统中CPU访问内存的完整机制深度解析
linux·缓存·tlb·内存访问·page table
赖small强3 小时前
【Linux C/C++开发】Linux 系统野指针崩溃机制深度解析
linux·mmu·crash·core dump·野指针
J__M__C4 小时前
WSL2的环境配置(安装+网络配置+基本美化)
linux
学困昇4 小时前
Linux基础开发工具(下):调试器gdb/cgdb的使用详解
linux·运维·服务器·开发语言·c++
liulilittle4 小时前
Linux shell 搜索指定后缀名文件,并复制到指定目录。
linux·服务器·数据库
必胜刻4 小时前
Redis哨兵模式(Linux)
linux·数据库·redis
阿猿收手吧!6 小时前
【Linux】Ubuntu 24安装webbench
linux·运维·ubuntu
生信大表哥6 小时前
如何在服务器上使用 Gemini 3 进行生信分析:从入门到进阶
linux·人工智能·语言模型·数信院生信服务器·生信云服务器
buyutang_6 小时前
Linux 网络编程:深入浅出UDP协议Socket编程规范
linux·网络·udp