linux内核驱动入门的第一个实验:globalvar驱动

一,驱动文件的编写,将文件的名字命名为globalvar.c ,源码如下:

#include <linux/module.h> //支持动态加载和卸载模块的头文件
#include <linux/init.h> //最基本的头文件,内核初始化
#include <linux/fs.h>  //文件系统头文件,包括设备注册函数和注销函数等
#include <asm/uaccess.h> //声明了在内核代码和用户空间之间移动数据的函数的头文件
MODULE_LICENSE("GPL"); //模块许可证
#define MAJOR_NUM 1025 //主设备号
#define DEVICE_NAME "globalvar" //设备名称
static char drv_buf[1024]; 

//****************************定义read方法********************************
static ssize_t globalvar_read(struct file *filp, const char *buf ,ssize_t len, loff_t *off)
{
//将drv_buf里的数据从内核空间复制到用户空间
     if (copy_to_user(buf, drv_buf, sizeof(int))) 
     {
          return - EFAULT;
     }
    return sizeof(int);
}

//***************************定义write方法**********************
static ssize_t globalvar_write(struct file *filp,const char *buf,ssize_t len)
{
//将用户空间的数据复制到内核空间
    if (copy_from_user(drv_buf, buf, sizeof(int)))
    {
        return - EFAULT;
    }
    return sizeof(int);
}

//*******初始化设备驱动的 file_operations 结构体**********
struct file_operations globalvar_fops ={	
     .owner = THIS_MODULE,
     .read = globalvar_read,
     .write = globalvar_write,
};

//************模块初始化函数********************
static int __init globalvar_init(void)
{
    int ret;
    //注册设备驱动
    ret = register_chrdev(MAJOR_NUM, DEVICE_NAME, &globalvar_fops);
    if (ret)
    {
        printk("globalvar register failure");
    }
    else
    {
        printk("globalvar register success");
    }
    return ret;
}

//***************模块卸载函数**********************
static void __exit globalvar_exit(void)
{
     //注销设备驱动
     unregister_chrdev(MAJOR_NUM, "globalvar"); //注意新版本的内核中unregister_chrdev函数是没有返回值的
     printk(" globalvar unregister success!\n");
}

module_init(globalvar_init);
module_exit(globalvar_exit);

       驱动的编写和调试,特别是驱动的调试过程中肯定会遇到不少问题,有时候你照着书上的代码打上去,编译的时候也会报错,原因是新的内核版本和旧的内核版本中的有些函数和机制会有些区别,比如上面这个程序中,模块卸载函数里面的unregister_chrdev在新的内核版本里是没有返回值的,所以不准许其他参数和此函数之间有赋值关系,一旦有赋值关系,编译就会报错!遇到问题要冷静,多百度,多请教!


二,编写Makefile文件:

obj-m:=globalvar.o
default:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

这个Makefile文件对其他模块的编译都是通用的,只需将第一行的globalvar改下名字就可以编译其他你想要编译的模块了。注意 default和clean下面的代码一开始时要使用Tab键隔开。


三,编译globalvar模块

        将前面的globalvar.c文件和Makefile文件放在同一文件夹下,我的是放在自己创建的globalvar文件夹里面,进入globalvar文件夹,打开终端,然后登陆root权限,输入make指令编译模块。


四,编写globalvar模块的测试程序,将其名字命名为test_globalvar.c ,源代码如下:

#include<stdlib.h>
#include <stdio.h>
#include <fcntl.h>

void showbuf(int *buf);
int main(void)
{
     int fd,i;
     int buf[100];
     for(i=0;i !=100;++i)
     {
	      buf[i]=i;
     }

      //打开“/dev/globalvar”
     fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
     printf("The fd is :%d \n",fd);
     if (fd != -1 )
     {
	       printf("向/dev/globalvar写入%d 个数字:\n",100);
	       showbuf(buf);
	       write(fd,buf,100);
	       printf("从/dev/globalvar读取%d 个数字:\n",100);
	       read(fd,buf,100);
	       showbuf(buf);	
	       close(fd);
      }
      else
     {
           printf("Device globalvar open failure\n");
      }
     return 0;
}

void showbuf(int *buf)
{
    int i,j=0;
    for(i=0;i<100;i++)
    {
        if(i%5==0)
            printf("\n%4d:",j++);
        printf("%4d",buf[i]);
    }
    printf("\n************************************************\n");
}

在终端输入指令gcc -o test_globalvar.o test_globalvar.c 编译测试文件。


五,使用insmod指令加载globalvar模块,在终端输入insmod globalvar.ko ,没有任何提示的话就是加载成功了,然后我们可以输入指令 cat /proc/devices  ,我们发现里面有1025 globalvar,这说明我们的模块确实是已经加载进内核了!


六,使用mknod指令建立目录项和文件的对应索引节点,在终端输入指令 mknod /dev/globalvar c 1025 0 


七,测试设备globalvar,在终端输入指令 ./test_globalvar 便可以看到我们先是往字符设备globalvar里面写入100个数,然后字符设备globalvar将这写入的100个数读了出来,实现了设备之间的数据交换。




本文出自 “止不住的思考” 博客,请务必保留此出处http://9110091.blog.51cto.com/9100091/1546961

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。