在 Ubuntu 上开发驱动通常并不需要下载内核的源码(不需要改内核源码的情况下),下载内核头文件就可以了。下载的头文件中自带内核的 Makefile 文件。 查询系统对应的内核头文件包: dpkg-query -s linux-headers-$(uname -r) 安装: sudo apt-get install linux-headers-$(uname -r) 内核头文件会被安装在 /usr/src 驱动的 Makefile 简例: obj-m := helloworld.o KDIR := /usr/src/linux-headers-4.15.0-36-generic PWD := $(shell pwd) all: modules modules: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend *.symvers .*.cmd *.ko *.mod.c .tmp_versions $(TARGET)...
Makefile 简介
make 命令 作用是读入一个名为 Makefile 的文件,然后执行这个文件中指定的指令。 Makefile Makefile 告诉 make 怎样执行一系列的指令去依靠源文件生成一个目标文件。Makefile 中声明了一个依赖关系的列表,这个列表应当包含所有文件(无论是源文件或者目标)作为输入 。 Makefile 文件一般命名为 Makefile 或 makefile ,如果 m 小写有时候可能会出现错误。 $@ 表示当前目标文件的名字 $^ 表示用空格隔开的所有依赖文件 $< 表示第一个依赖文件 驱动的 Makefile 与一般应用程序的 Makefile 有所不同,驱动的 Makefile 要指定内核源代码的位置。 KDIR := /home/matt/linux-3.2.0-psp04.06.00.11 $(MAKE) -C $(KDIR) M=$(PWD) modules $(MAKE) 为自带的变量 -C $(KDIR) 指明跳转到内核源码目录下读取那里的 Makefile M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的 Makefile 方便起见一般都会先定义编译器链接器: CC = gcc LD = gcc 正则表达式表示目录下所有 .c 文件,相当于:SRCS = main.c a.c b.c SRCS = $(wildcard *.c) OBJS 表示 SRCS 中把列表中的 .c 全部替换为 .o,相当于:OBJS = main.o a.o b.o OBJS = $(patsubst %c, %o, $(SRCS)) 可执行文件的名字: TARGET = Hello .PHONE 伪目标,具体含义百度一下一大堆介绍 .PHONY:all clean 要生成的目标文件: all: $(TARGET) 第一行依赖关系:冒号后面为依赖的文件,相当于 Hello: main.o a.o b.o 第二行规则:$@ 表示目标文件,$^ 表示所有依赖文件,$< 表示第一个依赖文件 $(TARGET): $(OBJS) $(LD) -o $@ $^ 上一句目标文件依赖一大堆 .o 文件,这句表示所有 .o 都由相应名字的 .c 文件自动生成 %o:%c $(CC) -c $^ make clean 删除所有 .o 和目标文件: clean: rm -f $(OBJS) $(TARGET) 注意:命令前必须以 tab 键开头,不能是...
Linux 内核打印 printk
内核中没有 C 库,所以不能使用 printf 打印。但它提供了类似的 printk 函数,printk 函数输出的字符串前加一个带尖括号的整数来控制打印级别,如 printk("<6>Hello, world!\n")。级别数值越小,优先级越高,其紧急和严重程度就越高。然而,需要注意的是,并不是所有级别的消息都会进行输出,而是根据 printk 的打印级别进行过滤。 printk 打印级别宏定义: #define KERN_EMERG KERN_SOH "0" /* system is unusable */ #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */ #define KERN_CRIT KERN_SOH "2" /* critical conditions */ #define KERN_ERR KERN_SOH "3" /* error conditions */ #define KERN_WARNING KERN_SOH "4" /* warning conditions */ #define KERN_NOTICE KERN_SOH "5" /* normal but significant condition */ #define KERN_INFO KERN_SOH "6" /* informational */ #define KERN_DEBUG KERN_SOH "7" /* debug-level messages */ #define KERN_DEFAULT KERN_SOH "d" /* the default kernel loglevel */ 内核日志打印级别查看: cat /proc/sys/kernel/printk 4 4 1 7 该文件包含四个数字值,分别表示: 控制台日志级别(Console log level):只有级别高于设定值的日志消息才会被显示在控制台上(值要小于该值)。 默认的消息日志级别(Default message log level):用于打印没有优先级的消息,即当 printk 没有指定消息级别时,将使用该默认级别。 最低的控制台日志级别(Minimum console log level):控制台日志级别可被设置的最小值,也就是最高的优先级。 默认的控制台日志级别(Default console log level):控制台日志级别的缺省值。 修改内核日志打印级别: echo "6 4 1 7" | sudo tee /proc/sys/kernel/printk 修改后的值重启后会恢复,如果要保持,可以将它添加到启动脚本中,以便在系统启动时重新设置。 printk 打印的是控制台,也就是 /dev/console。而图形界面中的终端,其实是把 stdin,stdout,stderr 三个文件重定向了一下。所以 printk 是不能在图形界面中的终端中显示的,当然可以在 /var/log/syslog 或者用 dmesg 查看。 不够打印级别的信息会被写到日志中可通过 dmesg 命令来查看。 dmesg 命令: 实时监控 dmesg 的日志输出: watch "dmesg | tail -20"...
创建一个 Linux 内核模块实例
源代码: #include <linux/init.h> //包含模块初始化、清除函数 #include <linux/module.h> //包含许多符号和函数的定义,模块的定义 #include <linux/kernel.h> static int __init hello_init(void) { printk(KERN_ALERT "Hello World!\n"); //内核中的打印只有printk,而且分打印级别 return 0; } static void __exit hello_exit(void) { printk(KERN_ALERT "Goodbye, see you again!\n"); } module_init(hello_init); //函数声明,把代码段放到init段;module_init是该程序的入口函数 //当系统启动init程序的时候,执行module_init()里的函数 module_exit(hello_exit); //当系统启动exit程序的时候,执行module_exi()里的函数 //没有module声明的函数(普通函数)会放到代码段(文本段) MODULE_AUTHOR("Matt <matt@mculoop.com>"); //声明作者 MODULE_DESCRIPTION("Linux Kernel Hello World Module (C) 2018"); //模块描述 MODULE_LICENSE("GPL"); //声明LICENSE Makefile: # To build modules outside of the kernel tree, we run "make" # in the kernel source tree; the Makefile these then includes this # Makefile once again. # This conditional selects whether we are being included from the # kernel Makefile or not. # called from kernel build system: just declare what our modules are obj-m := helloworld.o # Assume the source tree is where the running kernel was built # You should set KERNELDIR in the environment if it's elsewhere KERNELDIR ?= /home/matt/test/kernel/linux-3.2.0-psp04.06.00.11 # The current directory is passed to sub-makes as argument PWD := $(shell pwd) all: modules modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend *.symvers .*.cmd *.ko *.mod.c .tmp_versions $(TARGET) 编译: 编译前应当确保已经编译过内核,因为要使用内核的Makefile。 mak...
Linux 内核模块的相关操作命令
lsmod 查看已经安装的模块 lsmod Module Size Used by binfmt_misc 20480 1 vmw_vsock_vmci_transport 28672 2 vsock 36864 3 vmw_vsock_vmci_transport snd_ens1371 28672 2 snd_ac97_codec 131072 1 snd_ens1371 gameport 16384 1 snd_ens1371 ac97_bus 16384 1 snd_ac97_codec ... modinfo 显示模块信息 modinfo nfs.ko filename: /home/matt/test/kernel/linux-3.2.0-psp04.06.00.11/fs/nfs/nfs.ko license: GPL author: Olaf Kirch <okir@monad.swb.de> depends: sunrpc,lockd intree: Y vermagic: 3.2.0 mod_unload modversions ARMv7 p2v8 parm: cache_getent:Path to the client cache upcall program (string) parm: cache_getent_timeout:Timeout (in seconds) after which the cache upcall is assumed to have failed (ulong) parm: enable_ino64:bool parm: nfs4_disable_idmapping:Turn off NFSv4 idmapping when using 'sec=sys' (bool) depends 显示出了模块的依赖项 insmod 加载指定位置的模块 insmod /path-to-file/nfs.ko 如果依赖模块没有安装会提示 Unknown symbol... 使用时要指定模块的绝对路径。 rmmod 卸载驱动模块 rmmod <module_name> 注意其中 ”module_name” 是 lsmod 显示的模块名称,而不是对应的 ko 文件名 modprobe 用于挂载内核模块,挂载模块时不用指定模块文件的路径,也不用带文件的后缀。 modprobe nfs 相比 insmod 更智能些,不过限定了 ko 文件的位置。 实例: modprobe nfs [ 207.024183] RPC: Registered named UNIX socket transport module. [ 207.030461] RPC: Registered udp transport module. [ 207.035397] RPC: Registered tcp transport module. [ 207.040317] RPC: Registered tcp NFSv4.1 backchannel transport module. 卸载模块: modprobe -r nfs modprobe: remove[ 145.687621] RPC: Unregistered named UNIX socket transport module. 'sunrpc': Resou[ 145.694428] RPC: Unregistered udp transport module. rce temporarily [ 145.700954] RPC: Unregistered tcp transport module. unavailable [ 145.707460] RPC: Unregistered tcp NFSv4.1 backchannel transport module. depmod 用于分析可载入模块的相依性,供 modprobe 在安装模块时使用。 depmod 通过读取 /lib/modules/$(uname -r) 目录下的每一个模块来创建一个记录模块相依性的列...
Linux 查看 flash 分区命令
/proc/mtd 可以显示出所有挂载和未挂载的分区,但不显示文件系统类型。 cat /proc/mtd mtd0: 00080000 00020000 "MLO" mtd1: 00200000 00020000 "U-Boot" mtd2: 00580000 00020000 "Kernel" mtd3: 00800000 00020000 "File System" mtd4: 07000000 00020000 "app" df 可以查看已经挂载的分区和文件系统类型。 df -a Filesystem 1K-blocks Used Available Use% Mounted on ubi0:rootfs 4584 4576 8 100% / devtmpfs 127488 0 127488 0% /dev ramfs 0 0 0 0% /var ramfs 0 0 0 0% /tmp proc 0 0 0 0% /proc sysfs 0 0 0 0% /sys devpts 0 0 0 0% /dev/pts tmpfs 127592 0 127592 0% /dev/shm ubi1_0 89796 32 89764 0% /app fdisk 可以显示出所有挂载和未挂载的分区,但不显示文件系统类型。 fdisk -l Disk /dev/mtdblock0: 0 MB, 524288 bytes 255 heads, 63 sectors/track, 0 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk /dev/mtdblock0 doesn't contain a valid partition table Disk /dev/mtdblock1: 2 MB, 2097152 bytes 255 heads, 63 sectors/track, 0 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk /dev/mtdblock1 doesn't contain a valid partition table Disk /dev/mtdblock2: 5 MB, 5767168 bytes 255 heads, 63 sectors/track, 0 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk /dev/mtdblock2 doesn't contain a valid partition table Disk /dev/mtdblock3: 8 MB, 8388608 bytes 255 heads, 63 sectors/track, 1 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk /dev/mtdblock3 doesn't contain a valid partition table Disk /dev/mtdblock4: 117 MB, 117440512 bytes 255 heads, 63 sectors/track, 14 cylinders Units = cylinders of 16065 * 512 = 8225280 bytes Disk /dev/mtdblock4 doesn't contain a valid partition tabl...
解决错误 modinfo can't open '...modules.dep' No such file or directory
在 ARM 开发板上查看模块信息提示题中的错误: modinfo: can't open '/lib/modules/3.2.0/modules.dep': No such file or directory 解决办法是: 1、创建文件夹:/lib/modules/$(uname -r) 2、cp xx.ko /lib/modules/3.2.0/ 3、depmod 4、mv /lib/modules/3.2.0/modules.dep.bb /lib/modules/3.2.0/modules.dep 5、modinfo xx.ko 这下就正常了 又发现: 其它模块不用重复这样做 为什么其它模块不需要这样做的?是不是只要有 modules.dep 这么一个文件名的文件存在于这里就可以来了? 再次发现: 在这里创建一个空文件就行了! touch modules.dep 那么,在 /lib/modules/3.2.0/ 中没有 ko 文件的时候,直接执行 depmod 命令是不是也可以产生空文件?答案是:是的,不过产生的是 modules.dep.bb 。为什么不直接产生 modules.dep 呢?还不清楚。有一点是可以肯定的,modinfo 使用前提是存在 modules.dep,空的就行,模块信息是从 ko 文件中获取的,跟 modules.dep 没关系。不过,为什么非要存在 modules.dep 文件呢?这也还不清楚。 我在 Ubuntu 18.04 中做了测试,内核版本是 4.15.0-36,执行 depmod 没有 modules.dep.bb 产生,直接产生 modules.dep,并且文件的内容格式与 3.2.0 中的也不一...
JDK 安装与配置
JDK (Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。 SE (JavaSE,Standard Edition,标准版),是我们通常用的一个版本,从 JDK 5.0 开始,改名为 Java SE。 JDK 包含的基本组件包括: javac – 编译器,将源程序转成字节码 jar – 打包工具,将相关的类文件打包成一个文件 javadoc – 文档生成器,从源码注释中提取文档 jdb – debugger,查错工具 java – 运行编译后的java程序(.class后缀的) appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器。 Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件。 Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。 Jconsole: Java进行系统调试和监控的工具 1、下载并安装 JDK https://www.oracle.com/technetwork/java/javase/downloads/index.html Java SE 8 与 JDK1.8 是等效的。 2、配置环境变量 对于 Java 程序开发而言,主要会使用 JDK 的两个命令:javac.exe、java.exe。要想直接执行,需要配置路径。 单击“计算机-属性-高级系统设置”,单击“环境变量”。在“系统变量”栏下单击“新建”,创建新的系统环境变量。 (1) 新建 JAVA_HOME,变量值:C:\Program Files\Java\jdk1.8.0_191(即JDK的安装路径) (2) 编辑 Path,在原变量值的最后面加上:;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin (3) 新建 classpath, 变量值:.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\tools.jar 说明:jdk 需要配置三个环境变量; 1.5 之后可以不再设置 classpath,但建议保留 classpath 设置。 重启使环境变量生效。 3、测试 打开 CMD 执行 javac -version javac 1.8.0_1...