Linux_编译内核并编写一个最基础的字符设备驱动程序

linux内核编译以及字符设备驱动程序的编写

关键词: 内核编译; globalmem字符设备驱动编写; linux; linux设备驱动编写;

字符设备驱动程序主要内容

虚拟内存设备globalmem驱动实现,编写程序,然后将生成的驱动模块插入到驱动之中,接着编写测试程序,对设备globalmem进行测试。(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

事先准备

  • linux-3.2.96内核镜像 传送门
  • Ubuntu 14.04 64位系统(内核版本3.13.0-24-generic
  • 编译环境配置:使用命令
    sudo apt-get install build-essential kernel-package libncurses5-dev

    其中gcc版本为4.8.2(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

内核编译

使用linux-3.2.96内核,下载的是.tar.xz格式,首先将下载的内核移动到/usr/src/目录下(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

sudo mv linux-3.2.96.tar.xz /usr/src

切换到/usr/src/目录下,解压缩内核压缩包。由于下载的压缩包是.tar.xz后缀,解压分为两步:

xz -d linux-3.2.96.tar.xz
tar -xvf linux-3.2.96.tar

进入/usr/src/linux-3.2.96中,清理旧的编译文件

cd /usr/src/linux-3.2.96
make mrproper

现在/usr/src的样子就是这样的:

Linux_编译内核并编写一个最基础的字符设备驱动程序_第1张图片

/usr/src/linux-3.2.96中使用命令复制运行中的内核config到linux-3.2.96文件夹中(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

sudo cp /boot/config-3.13.0-24-generic /usr/src/linux-3.2.96

接着复制现有内核中的.config文件也复制到linux-3.2.96中(以我的Ubuntu 14.04为例,现有内核中的.config即为/usr/src/linux-headers-3.13.0-24-generic/.config

sudo cp /usr/src/linux-headers-3.13.0-24-generic/.config /usr/src/linux-3.2.96

config_file_copy.png

接着运行menuconfig

sudo make menuconfig

Linux_编译内核并编写一个最基础的字符设备驱动程序_第2张图片

选择load an Alternate Configuration file

Linux_编译内核并编写一个最基础的字符设备驱动程序_第3张图片

或者直接用默认的.config,都一样能编译

接着直接make编译,如果不指定参数,make默认单线程编译,所以我加了参数j4,多作业充分利用处理器核心

多作业开始编译

sudo make -j4 all

Linux_编译内核并编写一个最基础的字符设备驱动程序_第4张图片

开始编译:

Linux_编译内核并编写一个最基础的字符设备驱动程序_第5张图片

globalmem字符设备驱动编写及安装挂载

globalmem字符设备驱动编写核心代码如下

int  globalmem_init(void)  
{  
    int  result;  
    dev_t  devno  =  MKDEV(globalmem_major,  0);  

    /*    申请设备号   */  
    if  (globalmem_major)  
        result  =  register_chrdev_region(devno,  1,  "globalmem");  
    else    /*    动态申请设备号(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)    */  
    {  
        result  =  alloc_chrdev_region(&devno,  0,  1,  "globalmem");  
        globalmem_major  =  MAJOR(devno);  
    }      
    if  (result  <  0)  
        return  result;  

    /*    动态申请设备结构体的内存(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved) */  
    globalmem_devp  =  kmalloc(sizeof(struct  globalmem_dev),  GFP_KERNEL);  
    if  (!globalmem_devp)        /*申请失败*/  
    {  
        result  =    -  ENOMEM;  
        goto  fail_malloc;  
    }  
    memset(globalmem_devp,  0,  sizeof(struct  globalmem_dev));  
    globalmem_setup_cdev(globalmem_devp,  0);  
    return  0;  
fail_malloc:  unregister_chrdev_region(devno,  1);  
    return  result;  
}  

void  globalmem_exit(void)  
{  
    cdev_del(&globalmem_devp->cdev);        /*注销  cdev*/  
    kfree(globalmem_devp);            /*释放设备结构体内存  (Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)*/
    unregister_chrdev_region(MKDEV(globalmem_major,  0),  1);  /*释放设备号*/  
} 

完整代码见[传送门]

Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved

globalmem字符设备驱动的编译makefile编写

在编写makefile的时候,我参考了网上的很多写法,都不能很有效地成功编译设备驱动。以下的makefile写法是我自己摸索出来的,在我的测试环境下,保证能编译成功。但也只保证在我的测试环境下,因为我没有测试其他的环境,如Ubuntu 16.04等。

代码如下:

# Makefile (Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)
ifneq ($(KERNELRELEASE),)
# kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := globalmem.o  
else    
    PWD := $(shell pwd)
    KERNEL_VER ?= $(shell uname -r)
    KERNEL_DIR := /lib/modules/$(KERNEL_VER)/build
all:    
    $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
clean:  
    rm ‐rf *.o *~ core .depend .*.cmd *.ko *.mod.c
endif

运行make命令

编译成功:

Linux_编译内核并编写一个最基础的字符设备驱动程序_第6张图片

得到globalmem.ko文件

运行sudo insmod globalmem.ko命令加载模块,通过lsmod命令,发现globalmem模块已被加载。

Linux_编译内核并编写一个最基础的字符设备驱动程序_第7张图片

再通过cat /proc/devices命令查看,发现多出了主设备号为150的globalmem字符设备驱动,如下所示

Linux_编译内核并编写一个最基础的字符设备驱动程序_第8张图片

接着sudo mknod /dev/globalmem c 150 0这一步我不确定是否需要,因为之前我没sudo insmod,可能权限不够所以/dev里面没有globalmem,现在我使用了sudo insmod,接着又mknod/dev里面有globalmem了,所以我不确定是哪条命令的作用。

虚拟的字符设备驱动编写完成,也挂载成功了,该如何验证呢?

两种方法:

方法1:

直接在终端中输入

sudo su
ehco 'gy1' > /dev/globalmem

作用是向字符设备中写入字符数据”gy1”。然后,在终端中继续输入(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

cat /dev/globalmem

作用是从字符设备中读取已经写入完成的数据(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

Linux_编译内核并编写一个最基础的字符设备驱动程序_第9张图片

在方法1中为什么会先出现gy1然后又出现cat: /dev/globalmem: 没有那个设备或地址这个报错?

答:返回错误值才是正确的,因为读的位置已经越界,所以要返回错误。该返回错误的时候返回正确,那就是错误。至于网上说将globalmem.c中的if (p > = GLOBALMEM_SIZE)改成if (p > GLOBALMEM_SIZE)是错误的。输出的错误信息是正常的debug信息,因为cat调用了read两次。如果不想看到错误信息输出,将cat换成more即可

方法2:

编写自己的test.c文件

#include   
#include   
#include   
#include   
//(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

int main()  
{
    int fd;   
    char num[128] = {'\0'};  
    int arg = 2015;  

    //打开字符设备  
    fd = open("/dev/globalmem", O_RDWR); //可读写方式打开设备文件  
    if(fd != -1) {  
        //读取字符设备中的初始值  
        read(fd, num, sizeof(num)); //读取设备变量  
        printf("The globalmem is %s\n", num);  

        //文件指针复位  
        lseek(fd, 0, SEEK_SET);  

        //获取输入数据  
        printf("Please input the num written to globalmem\n");  
        scanf("%s", num);  

        //写入到字符设备中  
        write(fd, num, sizeof(num)); //写设备变量  

        //文件指针复位  
        lseek(fd, 0, SEEK_SET);  

        //从字符设备中读取出来  
        read(fd, num, sizeof(num)); //再次读取刚才写的值  
        printf("The globalmem is %s\n", num);  

        //文件指针复位  
        lseek(fd, 0, SEEK_SET);  

        //清空文件中的数据  
        if (0 > ioctl(fd, 0x01, &arg)) {  
            printf("Call cmd MEM_CLEAR fail\n");  
            perror("open globalmem");  
        }  

        //关闭字符设备  
        close(fd); //关闭设备文件  
    } else {  
        //打开失败  
        printf("Device open failure\n");  
        perror("open globalmem");  
    }  
    return 0;
}

Linux_编译内核并编写一个最基础的字符设备驱动程序_第10张图片

发现读出了之前方法1中输入到globalmem中的字符gy1,并写入了新的字符gy2

至此实验成功。

常见报错troubleshoot

报错1

编译内核过程遇到问题,提示:

ERROR: "__modver_version_show" [drivers/staging/rts5139/rts5139.ko] undefined!
make[1]: *** [__modpost] Error 1 
make: *** [modules] Error 2

解决方法:

sudo gedit /.config

设置CONFIG_RTS5139=n

继续编译,成功:

Linux_编译内核并编写一个最基础的字符设备驱动程序_第11张图片

报错2

globalmem的make过程中提示system.h没有这个文件

Linux_编译内核并编写一个最基础的字符设备驱动程序_第12张图片

解决方法:

在globalmem.c中#include 换为

#include 
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0)
    #include 
#else
    #include 
#endif

报错3

.ioctl部分有错误

globaklmemError2.png

原因

linux 2.6.29和linux 2.6.38的内核在file_operations结构发生了变化,否则在linux 2.6.38内核中,继续使 用.ioctl成员,编译时就会报错:error: unknown field 'ioctl' specified in initializerstruct file_operations结构体定义在include/linux/fs.h文件中。

linux 2.6.38内核取消了原有的ioctl成员,添加来新的成员
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
原来的ioctl 返回值变为
long long (*compat_ioctl) (struct file *, unsigned int, unsigned long);内核空间64位,用户空间32位

warning: initialization from incompatible pointer type出现此种warnning 的原因 “不兼容的指针类型初始化”。是你定义的函数类型与接口函数的类型不一样,如把返回值long定义成了int

这两个问题都对驱动有影响。

解决方法

static const struct file_operations 。。。 = {
。。。。。。
.unlocked_ioctl = 。。。,
。。。。。。
};

报错4

驱动模块编译时出现如下错误:

error: implicit declaration of function 'kmalloc'
error: implicit declaration of function 'kfree'

解决方法

添加如下头文件即可: #include


参考资料

(防止CSDN总因为外链屏蔽我的文章,去掉网址中的¥¥即可)

ht¥¥tp://www¥¥.linuxidc.com/Linux/2016-04/130459.htm
ht¥¥tp://mzqthu¥¥.iteye.com/blog/2001167
ht¥¥tp://387424-student-sina-com¥¥.iteye.com/blog/728021
ht¥¥tp://blog¥¥.sina.com.cn/s/blog_5dbc002d0100h7hj.html
ht¥¥tp://blog¥¥.sina.com.cn/s/blog_85998e3801011fpf.html
http://blog.csdn.net/fang_yang_wa/article/details/55805560
http://blog.csdn.net/djinglan/article/details/7372956
http://blog.csdn.net/xiaowulang20082008/article/details/50586985
ht¥¥tps://forums¥¥.fedoraforum.org/showthread.php?282144-Missing-system-h
http://blog.csdn.net/qiaoliang328/article/details/4874238
http://blog.csdn.net/rocky_zhm/article/details/47274879
http://blog.csdn.net/qiaoliang328/article/details/4874238
ht¥¥tps://www¥¥.cnblogs.com/feisky/archive/2010/05/29/1746885.html

(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)


visitor tracker
访客追踪插件


1

你可能感兴趣的:(学习笔记,Linux,使用中的排错与软件贴士,使用技巧等)