在module_init中的函数是模块加载时处理的函数,而模块卸载的函数则是在module_exit中。每一个设备都要对应一个基本的设备数据,当然为了使得这个设备注册在整个系统当中,我们还需要分配一个设备节点,alloc_chrdev_region完成这样一个功能。等到cdev_add的时候,整个设备注册的过程全部完成了,是这么简单。当然为了编写这个文件,我们还需要编写一个Makefile文件,

ifneq ($(KERNELRELEASE),)
obj-m := char.o

else
PWD  := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
 $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
 rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.*  Module.*
endif

  这个Makefile文件和我们之前编写的makefile基本上没有区别,的区别是文件名称改成了char.o,仅此而已。为了编写模块,我们直接输入make即可。这时候,char.ko文件可以生成了。然后,模块需要被注册在系统当中,insmod char.ko是少不了的。如果此时,我们还不确信是否模块已经加入到系统当中,完全可以通过输入lsmod | grep char进行查找和验证。为了创建设备节点,我们需要知道设备为我们创建的major、minor数值是多少,所以dmesg | tail 查找一下数值。在我hp的机器上,这两个数值分别是249和0,所以下面可以利用它们直接创建设备节点了,输入mknod /dev/chr_dev c 249 0即可,此时可以输入ls /dev/chr_dev验证一下。那么,按照这种方法,真的可以访问这个虚拟设备了吗,我们可以编写一段简单的代码验证一下,

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

#define CHAR_DEV_NAME "/dev/chr_dev"

int main()
{
 int ret;
 int fd;
 char buf[32];

 fd = open(CHAR_DEV_NAME, O_RDONLY | O_NDELAY);
 if(fd < 0)
 {
  printf("open failed! ");
  return -1;
 }
 
 read(fd, buf, 32);
 close(fd);
 
 return 0;
}

  代码的内容非常简单,是利用CHAR_DEV_NAME直接打开设备,读写设备。当然。首先还是需要对这个文件进行编译,文件名为test.c,输入gcc test.c -o test,其次是运行这个文件,直接输入./test即可。如果没有问题的话,那么说明我们的代码是ok的,但是我们还是没有看到任何内容。没关系,我们还是通过dmesg这个命令查看内核中是否存在相关的打印内容,直接输入dmesg | tail即可。此时如果没有意外的话,我们可以看到之前在chr_open和chr_read中留下的printk打印,这说明我们的代码完全是ok的。

  上面的代码只是一段小例子,真实的内容要比这复杂一下。不过既然我们都已经入门了,那么后面的内容其实也没有什么好怕的了。后有两个事情补充一下:(1)如果大家在创建节点后想删除设备节点,直接rm -rf /dev/chr_dev即可;(2)上面这段代码的原型来自于《深入Linux设备驱动程序内核机制》这本书,稍作修改,如果大家对内核机制的内容感兴趣,可以参考这本书的内容。