从第一个linux模块做起—字符驱动

来源:互联网 发布:淘宝网怎么抢红包 编辑:程序博客网 时间:2024/05/21 19:39

            对于设备,我们从各种各样的书籍和资料都可以找到相关的描述,大致分为三类:字符设备(character devices), 块设备(block devices)和网络接口(network interface)。

         字符设备可以像一个文件一样被打开、读写、关闭。例如:文本控制台设备 /dev/console &串口设备 /dev/ttyS0和普通文件的区别:普通文件可以使用lseek等操作“来回”读数据,而“大部分”字符设备只是一个内核和用户空间的数据通道,这样的设备一般情况下只能顺序读取数据。当然也有某些字符设备看起来是一个数据区域,在这个区域内, mmap 和lseek可以来回移动读数据,这样的设备一般是对“数据帧”或成块的内存进行处理,比如视频数据在/dev目录下有文件与之对应。

       块设备是能“容纳”文件系统的设备,比如磁盘设备通常块设备只能处理以块为单位的数据操作,通常块的大小是512字节或者其整数倍。Linux块设备有点特殊之处在于可以对其以字节单位读取,这样的话,块设备&字符设备的在驱动程序的角度最大的区别是其在内核中的内部数据管理方式和接口的不同块设备在操作系统中在/dev目录下有文件与之对应。

        网络接口可以是硬件接口,也可以是纯软件接口通过内核网络部分相关代码驱动网络接口发送和接收数据包网络接口不在/dev目录下出现,而是以某个系统中唯一的名字出现,比如eth0

        然而今天我们从最简单的字符设备做起,编写一个 module程序,驱动程序可以模块的方式加载,当然也是参考网络上的例子:http://blog.chinaunix.net/uid-20799298-id-99675.html  当然也修改了部分代码。

//memory.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>

MODULE_LICENSE("Dual BSD/GPL");

int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count);
ssize_t memory_write(struct file *filp, char *buf, size_t count);
void memory_exit(void);
int memory_init(void);

struct file_operations memory_fops = {
         .read = memory_read,
         .write = memory_write,
         .open = memory_open,
         .release = memory_release
};

 module_init(memory_init);
 module_exit(memory_exit);

int memory_major = 60;

char *memory_buffer;

int memory_init(void)
{
         int result;

         result = register_chrdev(memory_major, "memory", &memory_fops);
         if (result < 0) {
                 printk("<1>memory: can't obtain major number %d\n", memory_major);
                 return result;
         }

         memory_buffer = kmalloc(1, GFP_KERNEL);
         if (!memory_buffer) {
                 result = - ENOMEM;
                 goto fail;
         }
         memset(memory_buffer, 0, 1);

         printk("<1>Inserting memory module\n");
         return 0;

 fail:
         memory_exit();
         return result;
}

void memory_exit(void)
{
         unregister_chrdev(memory_major, "memory");

         if (memory_buffer)
                 kfree(memory_buffer);

         printk("<1>Removing memory module\n");
}

int memory_open(struct inode *inode, struct file *filp)
{
         return 0;
}

int memory_release(struct inode *inode, struct file *filp)
{
         return 0;
}

 ssize_t memory_read(struct file *filp, char *buf,
         size_t count)
{
         copy_to_user(buf, memory_buffer, 1);

                 return 1;
 
}

 ssize_t memory_write(struct file *filp, char *buf,
         size_t count)
{
         char *tmp;
        
         tmp = buf + count - 1;
         copy_from_user(memory_buffer, tmp, 1);

         return 1;
}

#Makefile,切记命令是以tab开头,在module下面是有一个tab开头的命令

obj-m := memory.o

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

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

     由于参考的这篇文章写的非常好,具体一些细节不在累述。编辑好代码,编译:make   如图表示编译通过(我用的ubuntu)

          

加载模块: insmod ./memory.ko
卸载模块: rmmod ./memory.ko
查看模块: lsmod | grep memory
查看模块更多信息:modinfo memory
查看模块输出: dmesg |tail -n 1

通过以上的命令来检查模块是否加入成功,接下来是该写个测试程序来试试了,我查看网络上很多类似什么hello模块的书写,基本上是到了这儿通过上面的命令查看信息就以为成功,接下来试试:

//test.c

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>

int main(int argc, char **argv)
 {
      int fd;
      char buf = 'M';
      char ret;

     fd = open("/dev/memory", O_RDWR);
  if (fd < 0)
  {
      printf("can't open /dev/memory\n");
      return -1;
  }
     if(wirte(fd,buf,1))  printf("Write to Memory OK!\nWrite data is: %c\n", buf);
      
      if(read(fd, ret, 1)) printf("Read from Memory OK!\nRead data is: %c\n", ret);
 
      return 0;
 }

        通过gcc -o test test.c 生成一个可执行文件,激动人心的时刻来了,输入命令:./test然而并没有什么卵用,输出显示没能成功打开驱动,所以说 并不是看那几个命令输出信息就认为已经完成任务了,分析上面的原因,是因为在/dev/这个目录下根本没有memory设备文件。

     接下来,我们需要手工来添加设备文件 mknod  /dev/memory c 60 0

其中:memory是定义的设备文件名C表示的设备类型是字符设备60表示major number, 0 是minor number,可根据实际情况选择,还可以通过内核自动来分配:

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
dev               is an output-only parameter that will, on successful completion, hold the first number in your allocated range.
firstminor    should be the requested first minor number to use; it is usually 0.
The count and name parameters            work like those given to request_chrdev_region. 

再次执行:./test   已经正常了


 

1 0
原创粉丝点击