本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。说实在的越看视频越感觉他讲的有点乱后续将以他的新版PDF手册为中心,视频作为辅助理解的工具。参考手册为嵌入式Linux应用开发完全手册V5.3_IMX6ULL_Pro开发板。
摘要:这节课是手册上没有的,我也是根据他的补充视频制作的这个总结博客。这节博客主要讲的是,将hello驱动程序在ubuntu上编译后下载到开发板上,以及网络传输时遇到的一系列问题,最终数据传输采用的是adb协议,详细解释hello文件的构造,编译符合开发板内核要求。
摘要关键词:hello驱动,adb下载传输,编译符合开发板内核要求,nfs网络传输弊端
本文详细介绍以下问题,如果你遇到了以下问题,看看我的方案能否解决。
c
1.上传hello驱动程序
2.解读makefile文件代码
3.adb传输hello驱动文件到开发板
首先,是在内核上编译我得设置环境变量。以上代码在从0开始学linux韦东山教程第三章问题小结(3)中讲解过,具体请参考那篇。
python
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
设置完以上后,按照视频中的要求,把从git中拷贝的01_hello_drv 源码文件存放至ubuntu中的nfs_rootfs文件夹(存放哪个文件夹都行),然后输入以下命令行
python
cd nfs_rootfs
ls
cd 01_hello_drv
ls
vi Makefile
vi Makefile:用于在终端中使用 vi 编辑器打开并编辑一个名为 Makefile 的文件。vi 是一个文本编辑器,常用于 Unix 和类 Unix 系统(如 Linux 和 macOS)中。
Makefile 是一个文件,通常用于定义构建自动化的规则,尤其是在 C、C++ 或其他编程语言的项目中。Makefile 指定了如何编译和链接程序,通常用于使用 make 工具来自动化构建过程。
基于以上大概了解了一下,makefile好像就是用来协助make命令,配合编译工作的。
如下图所示注意你的光标位置。得用键盘操作。
移动到此处后删除然后粘贴。
输入以下命令行保存退出,不保存退出就是shift+:+q
javascript
'Shift'+':'+'wq'
本人在pycharm中也打开了01_hello_drv中的makefile文件,理论上在win中更改后然后上传到ubuntu也是可以的。但是我也在思考一个问题,这些代码有什么意义呢?
python
KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f hello_drv_test
obj-m += hello_drv.o

逐行解析一下。chatGPT告诉我这段 Makefile 代码是用来编译和清理一个 Linux 内核模块的。
KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4:这行定义了一个变量 KERN_DIR,它指向的是 Linux 内核源码的路径。这个路径应该是你正在开发或编译内核模块所需要的内核源代码的目录。make 会在这个目录下调用内核的构建系统。(这也是为什么后续我们使用的是make命令,因为make命令直接调用了makefile文件)
all:这定义了 make 的默认目标。如果你只运行 make 命令,它会默认执行 all 目标中的内容。(所以其实我只执行了all这一部分)
make -C (KERN_DIR):-C 选项告诉 make 在指定的目录 (KERN_DIR) 也就是第十行下执行 make,也就是编译内核模块的工作。
M=\pwd:这个部分将当前目录 (pwd` 命令输出的路径) 作为模块的路径传递给内核构建系统。这意味着内核构建系统会将当前目录中的模块源代码和 Makefile 文件一起处理。也就是hello_drv_test.c和hello_drv.c具体是哪个看后续代码。
modules:这是告诉 make 构建内核模块的目标。
$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c:这行是编译一个普通的 C 程序 hello_drv_test.c,生成一个可执行文件 hello_drv_test。
clean:这部分定义了 clean 目标,通常用于清理编译过程中产生的临时文件和对象文件。
make -C (KERN_DIR) M=\\pwd modules clean:这行命令会在指定的内核源码目录 (KERN_DIR) 下执行 make clean,以清理该目录下的内核模块构建输出文件。
obj-m += hello_drv.o:这行是用来指定要编译的内核模块对象文件 hello_drv.o,hello_drv.o 是目标模块的对象文件,意味着将会编译 hello_drv.c 文件,并将其编译为内核模块。
小结一下:输入make命令makefile只会执行all中的内容和obj-m += hello_drv.o,当我输入make clean后只会执行clean中的内容
所以这么写应该也是没有问题的。
如下图所示输入make命令生成hello_drv.ko模块和gcc编译生成hello_drv_test文件。
hello_drv.ko 是一个 内核模块 文件,它由内核构建系统根据你的源代码生成。文件的扩展名 .ko 表示 Kernel Object,即内核对象文件。
如下图所示,在ubuntu中通过adb命令将以上两个文件传输给开发板。
python
adb push hello_drv.ko /root
adb push hello_drv_test /root
上传完成后通过Ubuntu打开开发板,运行模块测试功能。输入以下命令行。
之前输入一些特殊字符如!,出问题可能是它的代码的问题。
python
adb shell
cd /root
ls
insmod hello_drv.ko
./hello_drv_test -w hello,roudragon
./hello_drv_test -r
insmod hello_drv.ko :命令用于将 hello_drv.ko 这个内核模块加载到内核中,使得该模块的功能能够在运行时被内核使用。这通常用于加载设备驱动、文件系统、网络协议模块等,或者用于开发和调试内核模块。
c
#include <stdio.h>
int hello(char *name, int age)
{
if (name == 0)
{
printf("hello, nobody\n");
return -1;
}
else
{
printf("hello, %s, are you %d years old?\n", name, age);
return 0;
}
}
int main(int argc, char **argv)
{
if (argc == 2)
hello(argv[1], 1);
else
hello(0, 1);
return 0;
}
argc = 2为什么等于2,为什么它会直接给了argc?那如果argc是string变量那结果又如何?
argc = 2 是因为在命令行执行程序时,会将命令行参数传递给程序,而 argc 就是这些参数的数量。它的值通常是传递给程序的参数个数,包括程序本身的名字和你输入的其他参数。这里的 argc = 2 表示程序启动时,一共传递了两个参数:一个是程序的名字,另一个是你在命令行输入的其他参数。
具体来说,假设你在命令行输入:
c
./program arg1
argc = 2,因为程序启动时一共传递了两个参数:
argv[0] = ./program (程序的名字)
argv[1] = arg1 (你在命令行输入的其他参数)
argc 的值就代表了传递给程序的参数数量。
如果 argc 是一个 string 变量
argc 是一个整数类型,通常是 int。它的作用是表示命令行参数的数量,而不是一个字符串。如果你把 argc 改成一个 string 类型的变量,它就不能再作为参数的计数器来使用了。 argc 用来表示的是命令行参数的个数,无法直接存储字符串值。
这里 argc 变成了一个 string 类型的变量,它就不再用于表示命令行参数的数量了,而是单纯的存储一个字符串 "./program arg1"。这时,它将没有原来表示参数数量的作用。
如果你需要存储命令行参数的数量,必须保持 argc 为 int 类型。如果你想存储命令行参数的内容,则可以通过 argv 数组中的元素来操作。
网络连接部分,我也尝试了一下,bug太多了,绝对不是我的问题。一会eth0设置好后没有然后设置eth1后就有了如下图。然后我就想着是不是eth1连接的开发板,然后我把eth0关了,然后直接都没了。真的哲学,更何况我还是一个连接校园网的小屁孩呢,那更难受。
通了之后尝试连接电脑,好家伙连不上,但是ubuntu可以和win11联通。
然后发现刚刚连上的ubuntu又连不上了,真的是折磨。
没办法,只能用adb了。adb之前我默认你已经跟着视频学会了把hello.c上传到ubuntu上了,接下来就是编译代码生成hello二进制文件。
首先设置你的编译内核为arm内核。

c
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
然后用arm格式的内核编译上传到nfs_rootfs文件处,其实也没必要放这。
c
arm-buildroot-linux-gnueabihf-gcc -o hello hello.c
cp hello /home/book/nfs_rootfs/

然后我首先在Ubuntu中运行了一下,不行,很正常因为内核不一致。运行出现以下错误是对的。
c
cd /home/book/nfs_rootfs/
./hello

通过adb上传hello文件到开发板
c
book@100ask:~/nfs_rootfs$ adb push hello /root

ls查看一下,然后执行./hello文件
执行完后,我在思考一个问题是不是直接运行这个编译文件就可以了,hello_drv.ko文件模块是不是不会产生影响。然后如下图,我把hello_drv.ko删掉后再执行,依然没有问题。以上就是所有的总结。
每一次学习都会有自己解决不了的问题,如ubuntu中的环境我已经将其设置为内核arm编译但是我使用Linux-4.9.88打开不了hello驱动文件,但是可以在开发板上打开,理论上Ubuntu配置好的环境应该是可以打开的,有明白的人阔以指点一下。