【Linux】【Kernel】一个简单的内核模块例子

 

试试第一个hello world模块添加:

1.本地主机的参数

zhangjun@zhangjun-virtual-machine:~$ uname -a

Linux zhangjun-virtual-machine 4.4.0-31-generic #50~14.04.1-Ubuntu SMP
Wed Jul 13 01:07:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

zhangjun@zhangjun-virtual-machine:~$ ls
/usr/src/linux-headers-4.4.0-31-generic/arch    Documentation  include 
Kconfig   mm              scripts   tools   zfs

block   drivers        init     kernel    Module.symvers  security 
ubuntu

certs   firmware       ipc      lib       net             sound     usr

crypto  fs             Kbuild   Makefile  samples         spl       virt

 

1,在添加drivers/char/hello.c

2.编写内核模块文件

编写自己的内核模块文件myModule.c,内容如下:

#include  <linux/kernel.h>

#include <linux/module.h>

#include <linux/types.h>

static int __init myModule_init(void)

{

    printk(KERN_INFO”myModule init.\r\n”);

        return 0;

}

 

static void __exit myModule_exit(void)

{

    printk(KERN_INFO”myModule exit.\r\n”);

}

module_init(myModule_init);

module_exit(myModule_exit);

MODULE_LICENSE(“GPL”);

 

/*********************************************************************************
 *      Copyright:  (C) 2014
zhouguangfeng<zhouguangfeng91@gmail.com>
 *                  All rights reserved.
 *
 *       Filename:  hello.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(08/09/2014)
 *         Author:  zhouguangfeng <zhouguangfeng91@gmail.com>
 *      ChangeLog:  1, Release initial version on “08/09/2014 06:23:18
PM”
 *                 
 ********************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

3.编写Makefile

#kernel代码的路径

KERN_DIR = /usr/src/linux-headers-4.4.0-31-generic/   

all:

         make -C $(KERN_DIR) M=`pwd` modules   

clean:   

         make -C $(KERN_DIR) M=`pwd` modules clean   

obj-m += myModule.o

MODULE_LICENSE (“Dual BSD/GPL”);

4.编译

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# make

make -C /usr/src/linux-headers-4.4.0-31-generic/     M=`pwd`
modules   

make[1]: 正在进入目录 `/usr/src/linux-headers-4.4.0-31-generic’

  CC [M]  /home/zhangjun/zj_driver/myModule/myModule.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      /home/zhangjun/zj_driver/myModule/myModule.mod.o

  LD [M]  /home/zhangjun/zj_driver/myModule/myModule.ko

make[1]:正在离开目录 `/usr/src/linux-headers-4.4.0-31-generic’

 

static int __init hello_init (void)
{
    printk (KERN_INFO “Hello world\n”);
    return 0;
}

5.安装驱动

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule#
insmod myModule.ko

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# cat
/proc/modules

myModule 16384 0 – Live 0xffffffffc03d4000 (OE)

nls_utf8 16384 1 – Live 0xffffffffc016f000

isofs 40960 1 – Live 0xffffffffc03f4000

vmw_vsock_vmci_transport 28672 1 – Live 0xffffffffc03cc000

vsock 36864 2 vmw_vsock_vmci_transport, Live 0xffffffffc0165000

bnep 20480 2 – Live 0xffffffffc0178000

rfcomm 69632 8 – Live 0xffffffffc050b000

nfsd 315392 13 – Live 0xffffffffc05e4000

auth_rpcgss 57344 1 nfsd, Live 0xffffffffc0542000

nfs_acl 16384 1 nfsd, Live 0xffffffffc038b000

nfs 249856 0 – Live 0xffffffffc05a6000

binfmt_misc 20480 1 – Live 0xffffffffc0393000

lockd 90112 2 nfsd,nfs, Live 0xffffffffc052b000

grace 16384 2 nfsd,lockd, Live 0xffffffffc0386000

sunrpc 331776 19 nfsd,auth_rpcgss,nfs_acl,nfs,lockd, Live
0xffffffffc04b9000

fscache 61440 1 nfs, Live 0xffffffffc04a9000

coretemp 16384 0 – Live 0xffffffffc03c7000

kvm_intel 167936 0 – Live 0xffffffffc057c000

kvm 532480 1 kvm_intel, Live 0xffffffffc0426000

snd_ens1371 28672 2 – Live 0xffffffffc0365000

snd_ac97_codec 131072 1 snd_ens1371, Live 0xffffffffc0405000

irqbypass 16384 1 kvm, Live 0xffffffffc0381000

crct10dif_pclmul 16384 0 – Live 0xffffffffc037c000

gameport 16384 1 snd_ens1371, Live 0xffffffffc0373000

crc32_pclmul 16384 0 – Live 0xffffffffc034b000

ac97_bus 16384 1 snd_ac97_codec, Live 0xffffffffc0400000

snd_pcm 106496 2 snd_ens1371,snd_ac97_codec, Live 0xffffffffc03d9000

aesni_intel 167936 0 – Live 0xffffffffc039d000

vmw_balloon 20480 0 – Live 0xffffffffc036d000

snd_seq_midi 16384 0 – Live 0xffffffffc0360000

snd_seq_midi_event 16384 1 snd_seq_midi, Live 0xffffffffc035b000

aes_x86_64 20480 1 aesni_intel, Live 0xffffffffc0355000

lrw 16384 1 aesni_intel, Live 0xffffffffc0321000

gf128mul 16384 1 lrw, Live 0xffffffffc0350000

snd_rawmidi 32768 2 snd_ens1371,snd_seq_midi, Live
0xffffffffc0342000

glue_helper 16384 1 aesni_intel, Live 0xffffffffc033d000

ablk_helper 16384 1 aesni_intel, Live 0xffffffffc0277000

cryptd 20480 2 aesni_intel,ablk_helper, Live 0xffffffffc031b000

joydev 20480 0 – Live 0xffffffffc0315000

input_leds 16384 0 – Live 0xffffffffc0307000

serio_raw 16384 0 – Live 0xffffffffc0233000

snd_seq 69632 2 snd_seq_midi,snd_seq_midi_event, Live
0xffffffffc032b000

snd_seq_device 16384 3 snd_seq_midi,snd_rawmidi,snd_seq, Live
0xffffffffc0326000

snd_timer 32768 2 snd_pcm,snd_seq, Live 0xffffffffc030c000

btusb 45056 0 – Live 0xffffffffc02fb000

btrtl 16384 1 btusb, Live 0xffffffffc0272000

btbcm 16384 1 btusb, Live 0xffffffffc0160000

btintel 16384 1 btusb, Live 0xffffffffc0145000

bluetooth 516096 25 bnep,rfcomm,btusb,btrtl,btbcm,btintel, Live
0xffffffffc027c000

snd 81920 11
snd_ens1371,snd_ac97_codec,snd_pcm,snd_rawmidi,snd_seq,snd_seq_device,snd_timer,
Live 0xffffffffc021e000

soundcore 16384 1 snd, Live 0xffffffffc006b000

nfit 32768 0 – Live 0xffffffffc01f0000

vmwgfx 229376 3 – Live 0xffffffffc0239000

ttm 94208 1 vmwgfx, Live 0xffffffffc01d8000

drm_kms_helper 143360 1 vmwgfx, Live 0xffffffffc01fa000

drm 360448 6 vmwgfx,ttm,drm_kms_helper, Live 0xffffffffc017f000

shpchp 36864 0 – Live 0xffffffffc0156000

fb_sys_fops 16384 1 drm_kms_helper, Live 0xffffffffc0151000

syscopyarea 16384 1 drm_kms_helper, Live 0xffffffffc014c000

vmw_vmci 65536 2 vmw_vsock_vmci_transport,vmw_balloon, Live
0xffffffffc0134000

sysfillrect 16384 1 drm_kms_helper, Live 0xffffffffc00fc000

sysimgblt 16384 1 drm_kms_helper, Live 0xffffffffc00b8000

i2c_piix4 24576 0 – Live 0xffffffffc0120000

8250_fintek 16384 0 – Live 0xffffffffc00f7000

parport_pc 36864 0 – Live 0xffffffffc00ae000

ppdev 20480 0 – Live 0xffffffffc0065000

mac_hid 16384 0 – Live 0xffffffffc003d000

lp 20480 0 – Live 0xffffffffc0043000

parport 49152 3 parport_pc,ppdev,lp, Live 0xffffffffc0030000

hid_generic 16384 0 – Live 0xffffffffc004d000

usbhid 49152 0 – Live 0xffffffffc0127000

hid 118784 2 hid_generic,usbhid, Live 0xffffffffc0102000

psmouse 122880 0 – Live 0xffffffffc00d8000

mptspi 24576 2 – Live 0xffffffffc00cd000

mptscsih 40960 1 mptspi, Live 0xffffffffc00be000

mptbase 102400 2 mptspi,mptscsih, Live 0xffffffffc0072000

e1000 135168 0 – Live 0xffffffffc008c000

scsi_transport_spi 32768 1 mptspi, Live 0xffffffffc0052000

ahci 36864 1 – Live 0xffffffffc005b000

libahci 32768 1 ahci, Live 0xffffffffc0027000

pata_acpi 16384 0 – Live 0xffffffffc000f000

fjes 28672 0 – Live 0xffffffffc001f000

vmw_pvscsi 24576 0 – Live 0xffffffffc0014000

vmxnet3 57344 0 – Live 0xffffffffc0000000

安装驱动应该会调用myModule_init函数,那么应该有打印输出,但是实际上没有;

 

static void __exit hello_exit (void)
{
    printk (KERN_INFO “Goodbye world\n”);
    return;
}

6.卸载驱动

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# rmmod
myModule.ko

 

module_init (hello_init);
module_exit (hello_exit);

7.模块安装时的打印

步骤5在安装myModule.ko的时候终端上并没有打印出信息,但是实际上打印的信息写入文件/var/log/kern.log,可以通过tail命令查看该文件最后的条目。

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# tail
-f /var/log/kern.log

Jan 15 11:17:16 zhangjun-virtual-machine kernel: [ 5755.442589]
myModule: module verification failed: signature and/or required key
missing – tainting kernel

Jan 15 11:17:16 zhangjun-virtual-machine kernel: [ 5755.448875]
myModule init.

Jan 15 11:21:12 zhangjun-virtual-machine kernel: [ 5991.177018]
myModule exit.

 

其中 printk是内核特有的打印函数,类似与printf,只是它不依赖库文件,KERN_INFO为

8.关于printk函数

Linux 内核中通过printk输出信息,信息的类型在文件/usr/src/
linux-headers-4.4.0-31-generic
/include/linux/printk.h中(有些源码在文件include\linux\kernel.h中)定义。

/*

 * These can be used to print at the various log levels.

 * All of these will print unconditionally, although note that
pr_debug()

 * and other debug macros are compiled out unless either DEBUG is
defined

 * or CONFIG_DYNAMIC_DEBUG is set.

 */

#define pr_emerg(fmt, …) \

         printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)

#define pr_alert(fmt, …) \

         printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)

#define pr_crit(fmt, …) \

         printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)

#define pr_err(fmt, …) \

         printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)

#define pr_warning(fmt, …) \

         printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)

#define pr_warn pr_warning

#define pr_notice(fmt, …) \

         printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)

#define pr_info(fmt, …) \

         printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

如果不指定,默认是 DEFAULT_MESSAGE_LOGLEVE,和KERN_WARNING一样。 
root@vickytong:/usr/src/ linux-headers-4.4.0-31-generic# find . -name
“*.h” | xargs grep “DEFAULT_MESSAGE_LOGLEVE”

./include/generated/autoconf.h: #define
CONFIG_MESSAGE_LOGLEVEL_DEFAULT 4

 

打印优先级

9.交叉编译

有时候我们需要将模块移植到其他芯片方案的主机(或开发板)上运行,那么需要交叉编译。交叉编译需要修改Makefile,主要是修改kernel代码的路径和编译工具。kernel代码的路径要修改为目的主机系统的源代码路径,编译工具gcc要修改为目的主机芯片架构对应的编译工具。 设置编译器,与编译内核用的编译器要一致。下面为编译驱动时,在terminal终端设置环境变量如下:

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule#
source /opt/Xilinx/SDK/2015.4/settings64.sh

 

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule#
export ARCH=arm

 

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# 
export CROSS_COMPILE=arm-xilinx-linux-gnueabi-

 

说明:在目前ZedBoard上跑的linux系统,使用该编译器似乎也能编译驱动插入到内核中去(后面做gpio驱动时仔细验证):

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule#
export ARCH=arm

 

root@zhangjun-virtual-machine:/home/zhangjun/zj_driver/myModule# 
export CROSS_COMPILE= arm-linux-gnueabihf-

 

makefile文件修改如下:

#kernel代码的路径

KERN_DIR = /home/zhangjun/linux 

obj-m := myModule.o

all:

     make -C $(KERN_DIR) ARCH=arm M=`pwd` modules 

clean:     

     rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions

 

 

2,修改drivers/char/Kconfig文件,添加在make
menuconfig的选项

10.下载、安装和卸载

         在Zedboard上的linux挂在nfs,在Terminal上做如下操作:

analog@analog:~$ sudo mount -t nfs 192.168.0.25:/home/zhangjun /mnt/

mount.nfs: /mnt is busy or already mounted

analog@analog:~$ cd /mnt

analog@analog:/mnt$ cd zj_driver

analog@analog:/mnt/zj_driver$ ls

driver_demo  myModule

analog@analog:/mnt/zj_driver$ cd myModule

analog@analog:/mnt/zj_driver/myModule$ ls

Makefile   Module.symvers  myModule.c   myModule.ko     myModule.mod.o

Makefile~  modules.order   myModule.c~  myModule.mod.c  myModule.o

analog@analog:/mnt/zj_driver/myModule$ insmod myModule.ko

insmod: ERROR: could not insert module myModule.ko: Operation not
permitted

analog@analog:/mnt/zj_driver/myModule$ sudo su

root@analog:/mnt/zj_driver/myModule# insmod myModule.ko

root@analog:/mnt/zj_driver/myModule# cat /proc/modules

myModule 737 0 – Live 0xbf000000 (O)

root@analog:/mnt/zj_driver/myModule# rmmod myModule.ko

 

#
# Character device configuration
#

11.添加模块参数

有时候需要从用户态传参数给内核模块,这时候在内核模块中需要通过宏module_param声明参数。本例增加两个参数,一个是字符串参数cString,一个是int型参数iInt,修改后代码如下:

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/types.h>

 

static char* cString = NULL;

static int iInt = 0;

module_param(cString, charp, 0644);

module_param(iInt, int, 0644);

 

static int __init myModule_init(void)

{

    printk(KERN_INFO”myModule init, cString = %s, iInt = %d\r\n”,
cString, iInt);

    return 0;

88bifa必发娱乐,}

 

static void __exit myModule_exit(void)

{

    printk(KERN_INFO”myModule exit.\r\n”);

}

module_init(myModule_init);

module_exit(myModule_exit);

MODULE_LICENSE(“GPL”);

 

交叉编译后,下载,安装命令如下:

/tmp # insmod myModule.ko cString=helloworld iInt=100 
myModule init, cString = helloworld, iInt = 100

 

menu “Character devices”

source “drivers/tty/Kconfig”

# add  by zhouguangfeng 2014.8.9 for “Hello
world” 

config FL_HELLO
    tristate “CCTE2440 Hello Driver”
    depends on ARCH_S3C2440
    help
    FL2440 Hello Module.

3,修改drivers/char/Makefile,编译添加的hello.c

obj-$(CONFIG_FL_HELLO) += hello.o

4,make menuconfig选项

[*] Enable loadable module support
 —> 

[*]  
Module unloading  
//需要支持,否则已经加载的模块无法卸载载

  Device Drivers  —>

Character
devices  —>

<M>
FL2440 Hello Driver
 //刚才添加的Kconfig选项,这里编译为模块

5,make之后,下载内核与文件系统,挂在hello.ko模块

Copyright (C) 2014
zhouguangfeng<zhouguangfeng@gmail.com>
dm9000 dm9000 eth0: link up, 100Mbps, full-duplex, lpa 0xCDE1
zhouxiaoxing login: root
>: ls
apps     data     etc      info     lib      mnt      root     sys    
 usr
bin      dev      hello    init     linuxrc  proc     sbin     tmp    
 var
>: ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes
64 bytes from 192.168.1.1: seq=0 ttl=64 time=2.592 ms
64 bytes from 192.168.1.1: seq=1 ttl=64 time=0.685 ms
64 bytes from 192.168.1.1: seq=2 ttl=64 time=0.658 ms

— 192.168.1.1 ping statistics —
9 packets transmitted, 9 packets received, 0% packet loss
round-trip min/avg/max = 0.658/0.896/2.592 ms

>: tftp -gr
hello.ko 192.168.1.3
hello.ko             100%
|*******************************|  2365  
0:00:00 ETA
>: ls
apps      dev       hello.ko  lib       proc      sys       var
bin       etc       info      linuxrc   root      tmp
data      hello     init      mnt       sbin      usr

>: insmod
hello.ko 

Hello world

>: rmmod hello 

Goodbye
world