linux驱动位置_Linux驱动程序学习一 (续)

继续linux设备驱动程序第二章的学习实践.

一、当前进程

查找当前进程.

在hello.c中增加以下代码:

 printk(KERN_EMERG "the process is "%s", (pid %i)n",
           current->comm,current->pid);

make后,执行以下指令:

insmod ./hello.ko

通过dmesg命令得到当前进程的命令“insmod”,其ID是3364:

ee867e56971ceb889be962bc77cd26c9.png

二 编译和装载

书本对应hello world的makefile如下:

ifeq ($(KERNELRELEASE),)                 

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)

modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    # called from kernel build system: just declare what our modules are
    obj-m := hello.o 
endif

以上编译方式为makefile的驱动程序动态编译,(静态编译即将驱动直接编译进内核,动态编译即为将驱动编译成模块)。如书中所述这种方法方便于内核树之外的模块构造变得容易.

下面按我自己的理解和测试解读makefile代码:

1.这里用了ifeq ($(KERNELRELEASE),) 当KERNELRELEASE为null时执行,否则执行else.

由于只有在Linux源码根目录下的Makefile编译内核时,KERNELRELEASE宏才会被定义.所以当第一次进入makefile时,ifeq ($(KERNELRELEASE),) 为真,执行ifeq以下代码.

2.设置需要使用的内核源码目录路径:

   KERNELDIR ?= /lib/modules/$(shell uname -r)/build 

3.将当前路径赋值给PWD

    PWD := $(shell pwd)                                                                                      

4 modules为makefile的默认目标被执行(因为是第一个),而 modules_install 和clean则是伪目标,执行make时,只执行modules 目标.

modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
        rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

5 .PHONY作用.这里的主要作用是避免在makefile执行的命令和工作目录下的实际文件出现名字冲突.

.PHONY: modules modules_install clean

比如我在工作目录下,建了一个文件clean,此时输入以下命令;

make clean

此时make产生的*.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions文件已删除.

如果将makefile改为

.PHONY: modules modules_install clean

则会提示"clean" is up to date.

0b62ce18379519d87447d4d3173386cf.png

makefile编译过程理解:

1;第一次执行makefile,KERNELRELEASE宏刚开始值为NULL,进入ifeq

2:赋值得到KERNELDIR,PWD

执行第一个目标(默认的目标)modules:

 modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

$(MAKE)即为make命令, -C $(KERNELDIR)即进入内核源码目录编译顶层makefile,此时KERNELRELEASE被赋值. M=$(PWD) modules 表示在构造modules目标之前返回到当前模块源码所在目录,第二次执行makefile.此时KERNELRELEASE已经被赋值,所以执行else部分.

else
    # called from kernel build system: just declare what our modules are
    obj-m := hello.o 

自此得到可装载的模块hello.ko.

三 装载卸载模块

1.linux系统调用

系统调用指的是操作系统提供给用户程序调用的一组特殊接口,是用户空间和内核通讯的普遍按照功能区域,linux系统调用大致分为进程控制,文件访问,系统控制,存储管理,网络管理,进程通讯等,详细说明可以通过man 2 syscalls命令查看manpage说明。

2.加载模块

linux设备驱动有两种加载方式insmod和modprobe。

insmod一次只能加载特定的一个设备驱动。写法为:

        insmod hello.ko

modprobe可一次将有依赖关系的驱动全部加载到内核。驱动被安装在/lib/modules/$(uname -r)/...下。写法为:

       modprob hello     //

注:uname -r是终端中输入uname -r后显示的内核版本,如我的“uname -r”为5.4.0。

加载驱动模块(驱动程序模块名为hello.ko)

方法一insmod:

进入hello.ko驱动模块文件所在的目录(可按自己的喜好放在相应的目录里),然后直接

insmod hello.ko

方法二modprobe:

1)、将hello.ko文件拷贝到/lib/module/#uname -r#/目录下(这里hello.ko必须按要求拷贝到这个指定目录下)

cp     /home/valian/SLAM/linux/hello_world/hello.ko       /lib/modules/5.4.0/

2)、depmod(会在/lib/modules/#uname -r#/目录下生成modules.dep和modules.dep.bb文件,表明模块的依赖关系)

depmod

3)、加载

modprobe hello(注意这里无需输入.ko后缀)

4) 消息打印查找

dmesg|tail -5      显示最后5条

控制台显示界面如图:

0f5f5f601c19fd39f1129f4445ba9251.png

5)、两种方法的区别:

modprobe和insmod都是动态加载驱动模块的,区别在于modprobe可以解决load module时的依赖关系,它是通过/lib/modules/#uname -r/modules.dep文件来查找依赖关系的;而insmod不能解决依赖问题。insmod可以在任何目录下执行,更方便一些。

6)、显示已载入系统的模块:

lsmod

如下图可以看到hello模块已经载入。

590d2f2efb37d8cf8f14480b0772d5d0.png

3.卸载驱动模块

在任何目录下,输入命令

rmmod  hello  //hello是模块的名称,而不需要hello.ko这个文件

四、版本依赖

如果需要编写能够在多个内核版本一起工作的模块,则必须使用宏和#ifdef来构造并编译自己的代码。在linux/version.h找到相关版本定义(linux:不同的版本显示不同,如本文为linux-5.4)

 find / -type f -name "version.h"    // 进入对应文件夹查找version.h文件

查到/usr/src/linux-5.4/usr/include/linux/version.h。打开文件version.h:

vim  /usr/src/linux-5.4/usr/include/linux/version.h

打开version.h内容为:

#define LINUX_VERSION_CODE 328704                            //   本机版本为5.4.0 即 328704=5<<16+4<<8+0
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))    //

五、驱动模块设计

1、所有可装载的模块代码都必须包含以下头文件:

#include <linux/module.h>
#liclude  <linux/init>

2、模块应该指定代码所使用的许可证。因此需要包含MODULE_LICENSE行:

MODULE_LICENSE("GPL");     //"GPL","GPL v2","Dual BSD/GPL"等等其他许可证

为方便于选择不同的许可证,可使用宏定义实现:

#define __license__      "GPL"                        // 设置许可证
MODULE_LICENSE(__license__);

3、可增加描述性定义

MODULE_AUTHOR("**@163.com>");
MODULEDESCRIPTION("Linux Kernel hello module ");
MODUALE_VERSION("drive_1.0.0")

4 初始化、关闭模块

初始化:模块的初始化负责注册模块所提供的任何设施,这里的设施是指一个可以被应用程序访问的新功能,可以是一个完整的驱动程序或仅仅是一个新的软件抽象。

(static) int hello_init(void)
    { 
           /* 初始化 注册代码* /
    }
module_init(hello_init);

清除模块:清除函数在模块被移除之前注销接口并向系统中返回所有资源。函数定义如下:

(static )void hello_exit(void)
{
	  /* 清除代码* /
}
module_exit(hello_exit);

如果一个模块未定义清除函数,则内核不允许卸载该模块。

六、模块参数

当硬件的I/O端口或I/O内存地址位置需要告知给我们的驱动程序时,内核允许驱动程序指定这些参数,并且这些参数可在装载驱动程序模块时改变。

书中提供的hellop.c中,在hello.c模块中增加两个参数改成hellop模块:一个为整数,howmany(次数);一个为字符串,whom。在装载模块时,向whom问候howmany次。参数定义如下:

module_param(howmany, int, S_IRUGO);      // S_IRUGO  只读
module_param(whom, charp, S_IRUGO);       //  S_IRUGO  只读

参数定义为只读,我需要通过自己的实践,完成sysfs对参数的修改,所以在hellop.c中,修改参数定义如下:

module_param(howmany, int, S_IRUGO|S_IWUSR);            //可读可写
module_param(whom, charp, S_IRUGO|S_IWUSR);             //可读可写

装载模块指令如下:

insmod hellop.ko howmany=8 whom="valian"       //

注意:书中少写了hellop后面的.ko,读者调试的时候需要注意。

1.通过sysfs修改参数

在linux中,每一个module(驱动)加载到kernel后,都会在/sys/module目录下生成以该模块命名的一个文件夹,即生成了文件夹,其参数则保存在/sys/module/hellop/parameters。

通过ls命令显示参数:

ls /sys/module/hellop/parameters

得到参数如下howmany,whom:

root@valian-TM1703:/home/valian/SLAM/linux/hellop# ls /sys/module/hellop/parameters
howmany  whom

633f5f4b878c0ff3fc5b9572680bd3c1.png

通过echo修改参数howmany和whom,然后通过cat指令显示修改前和修改后的参数。

root@valian-TM1703:/home/valian/SLAM/linux/hellop# cat /sys/module/hellop/parameters/howmany
8
root@valian-TM1703:/home/valian/SLAM/linux/hellop# cat /sys/module/hellop/parameters/whom
valian
root@valian-TM1703:/home/valian/SLAM/linux/hellop# echo 15 > /sys/module/hellop/parameters/howmany
root@valian-TM1703:/home/valian/SLAM/linux/hellop# cat /sys/module/hellop/parameters/howmany
15
root@valian-TM1703:/home/valian/SLAM/linux/hellop# echo "my son" > /sys/module/hellop/parameters/whom
root@valian-TM1703:/home/valian/SLAM/linux/hellop# cat /sys/module/hellop/parameters/whom
my son

从以上输出可以看出参数howmany由8改为15,whom由"valian"改为"my son"。成功的通过sysfs修改参数。

注意:sysfs文件系统一般是自动加载到/sys下的,如果在根目录下没有找到sysfs,可以通过下面的命令手工加载:

mkdir -p /sysfs                             //创建sysfs目录
mount -t sysfs sysfs /sysfs     //挂载sysfs到/sys目录
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值