设备驱动实现
1在www.kernel.org下载所需要版本的内核,我下载的是linux-4.1.19.tar.xz版本。
2解压到/home/kernel/目录下。
解压:
xz -d linux-4.1.19.tar.xz
tar -xf linux-4.1.19.tar
移动:
sudo mv linux-4.1.19 /home/kernel/
3编译内核
cd /home/kernel/
cp /boot/config-4.10.0-37-generic ./.config
make oldconfig
sudo make
编译完成之后:
sudo make modules_install
sudo make install
这里我的gcc和linux16.04内核版本是:

4编写globalmem驱动程序
1打开终端输入命令 mkdir /home/user/globalmem(创建 globalmem 文件夹)。
2然后再输入命令cd /home/user/globalmem(进入globalmem 文件夹)。
3接着输入命令gedit globalmem.c(编写globalmem.c程序)
在/home/user/globalmem新建文件globalmem.c如下:
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <linux/slab.h> #include <linux/version.h> #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0) #include <asm/switch_to.h> #else #include <asm/system.h> #endif #include <asm/uaccess.h> #define GLOBALMEM_SIZE 0x1000 /*全局内存最大 4K 字节*/ #define MEM_CLEAR 0x1 /*清 0 全局内存*/ #define GLOBALMEM_MAJOR 150 /*预设的 globalmem 的主设备号*/ static int globalmem_major = GLOBALMEM_MAJOR; /*globalmem 设备结构体*/ struct globalmem_dev { struct cdev cdev; /*cdev 结构体*/ unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/ }; struct globalmem_dev *globalmem_devp; /*设备结构体指针*/ int globalmem_open(struct inode *inode, struct file *filp) { filp->private_data = globalmem_devp; return 0; } int globalmem_release(struct inode *inode, struct file *filp) { return 0; } long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct globalmem_dev *dev = filp->private_data; /* 获得设备结构体指针 */ switch (cmd) { case MEM_CLEAR: memset(dev->mem, 0, GLOBALMEM_SIZE); printk(KERN_INFO "globalmem is set to zero\n"); break; default: return - EINVAL; } return 0; } static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*分析和获取有效的写长度*/ if (p >= GLOBALMEM_SIZE) return count ? - ENXIO: 0; if (count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; /*内核空间->用户空间*/ if (copy_to_user(buf, (void*)(dev->mem + p), count)) { ret = - EFAULT; } else { *ppos += count; ret = count; printk(KERN_INFO "read %d bytes(s) from %d\n", count, p); } return ret; } static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*分析和获取有效的写长度*/ if (p >= GLOBALMEM_SIZE) return count ? - ENXIO: 0; if (count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; /*用户空间->内核空间*/ if (copy_from_user(dev->mem + p, buf, count)) ret = - EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "written %d bytes(s) from %d\n", count, p); } return ret; } static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig) { loff_t ret = 0; switch (orig) { case 0: /*相对文件开始位置偏移*/ if (offset < 0) { ret = - EINVAL; break; } if ((unsigned int)offset > GLOBALMEM_SIZE) { ret = - EINVAL; break; } filp->f_pos = (unsigned int)offset; ret = filp->f_pos; break; case 1: /*相对文件当前位置偏移*/ if ((filp->f_pos + offset) > GLOBALMEM_SIZE) { ret = - EINVAL; break; } if ((filp->f_pos + offset) < 0) { ret = - EINVAL; break; } filp->f_pos += offset; ret = filp->f_pos; break; default: ret = - EINVAL; break; } return ret; } static const struct file_operations globalmem_fops = { .owner = THIS_MODULE, .llseek = globalmem_llseek, .read = globalmem_read, .write = globalmem_write, .unlocked_ioctl = globalmem_ioctl, .open = globalmem_open, .release = globalmem_release, }; static void globalmem_setup_cdev(struct globalmem_dev *dev, int index) { int err, devno = MKDEV(globalmem_major, index); cdev_init(&dev->cdev, &globalmem_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &globalmem_fops; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding LED%d", err, index); } int globalmem_init(void) { int result; dev_t devno = MKDEV(globalmem_major, 0); /* 申请设备号*/ if (globalmem_major) result = register_chrdev_region(devno, 1, "globalmem"); else /* 动态申请设备号 */ { result = alloc_chrdev_region(&devno, 0, 1, "globalmem"); globalmem_major = MAJOR(devno); } if (result < 0) return result; /* 动态申请设备结构体的内存*/ 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); /*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/ } MODULE_AUTHOR("Song Baohua"); MODULE_LICENSE("Dual BSD/GPL"); module_param(globalmem_major, int, S_IRUGO); module_init(globalmem_init); module_exit(globalmem_exit);
在/home/user/globalmem新建 Makefile 文件如下;
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
编译globalmem.c
命令行输入:make

目录中得到这些文件:

我没有管这些警告,直接进行下一步。
运行insmod globalmem.ko命令加载模块,通过lsmod命令,发现globalmem模块已被加载。再通 过cat /proc/devices命令查看,发现多出了主设备号为150的myglobalmem字符设备驱动,如下所示:

查看/proc/device(这个文件列出字符和块设备的主设备号, 以及分配到这些设备号的设备名称)发现设备号为150的globalmem。

编写应用程序test.c用于测试:
#include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <string.h> #include <fcntl.h> main() { char str[1024], str2[1024]; int fd, stop; int ret; fd = open("/dev/globalmem", O_RDWR, S_IRUSR | S_IWUSR); if (fd != -1) { printf("Please input the string written to globalmem\n"); scanf("%s", str); printf("The str is %s\n", str); ioctl(fd, 1, NULL); ret = write(fd, str, strlen(str)); printf("The ret is %d\n", ret); lseek(fd, 0, SEEK_SET); //scanf("%d", &stop); read(fd, str2, 100); printf("The globalmem is %s\n", str2); close(fd); } else { printf("Device open failure\n"); } }
输出结果:

在编译内核时,ubuntu的分区一定要充足,第一次在自己笔记本上编译内核,回来之后发现编译到一般磁盘容量以满,重启后ubuntu进入不了图形界面,只能在shell中输入sudo startx进入。之后只好在实验室编译内核。
在内核编译完后,需要修改globalmem.c中的程序,需要预设的globalmem的主设备号,还将globalmem_ioctl函数修改。并且将Makefile文件改为正确路径。这样就可以正确编译了。
在test.c文件中需要将中文符号改为英文符号即可正确运行。

浙公网安备 33010602011771号