驱动读写实验
前言
在编写驱动读写的时候,有个疑问:在内核中有一段固定的内存空间,假如应用多个进程在同时且连续的读写时,会发生错乱的情况吗?
验证平台:qemu 运行linux kernel 4.0 的虚拟机上进行验证,busybox 使用的是静态编译的库,故应用也需要静态编译。
驱动代码设计
驱动设计思路
- 内核中使用一个数组作为上述的固定内存空间。
- 驱动初始化注册字符设备并自动创建设备节点 /dev/rivotek_0。
- 实现驱动读写函数,写入时将应用层的字符写入到数组中,并打印写入的字符串;读取时,从数组中将该字符串传递给应用,驱动打印该字符串。
- 驱动退出时,销毁注册时创建的节点和注册的字符设备。
实现代码如下
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/device.h>#define CDE_NAME "rivotek_cdev"unsigned char lcdev_buff[32] = {0};struct my_char_dev
{unsigned int maj; unsigned int mio; unsigned int count;struct cdev *cdev;struct class *rivotek_class;};struct my_char_dev *lcdev;static int lcdev_open(struct inode *inode, struct file *file)
{printk(KERN_INFO"lcdev open\n");return 0;
}static int lcdev_release (struct inode *inode, struct file *file)
{printk(KERN_INFO"lcdev release\n");return 0;
}static ssize_t lcdev_write(struct file *l_file, const char __user *buf, size_t count, loff_t *f_ops)
{int ret = -1;ret = copy_from_user((void *)lcdev_buff, buf, sizeof(lcdev_buff));if(ret)printk(KERN_ERR"Write error,ret=%d\n",ret);printk(KERN_ERR"%s: %s,ret=%d\n",__FUNCTION__,lcdev_buff,ret);return ret;
}static ssize_t lcdev_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)
{int ret = -1;ret = copy_to_user((void *)buf, lcdev_buff, sizeof(lcdev_buff));if(ret)printk(KERN_ERR"Write error,ret=%d\n",ret);printk(KERN_ERR"%s: %s,ret=%d\n", __FUNCTION__,lcdev_buff,ret);return ret;
}static const struct file_operations lcdev_fops = {.owner = THIS_MODULE,.open = lcdev_open,.write = lcdev_write,.read = lcdev_read,.release = lcdev_release,};static int __init char_test_init(void)
{int ret;dev_t dev_n = 0;int i = 0;lcdev = kmalloc(sizeof(struct my_char_dev), GFP_KERNEL);if(!lcdev) {printk(KERN_ERR"No memory for lcdev");ret = -ENOMEM;goto out;}printk(KERN_ALERT"kmalloc ok \n");lcdev->maj = 0;lcdev->mio = 0;lcdev->count = 1;ret = alloc_chrdev_region(&dev_n, lcdev->mio, lcdev->count, CDE_NAME);if(0 > ret) {printk(KERN_ERR"register failed\n");goto register_err;}lcdev->maj = MAJOR(dev_n);lcdev->mio = MINOR(dev_n);printk(KERN_ALERT"register char dev ok ,ret:%d\n", ret);lcdev->cdev = cdev_alloc();if (!lcdev->cdev)goto register_err;cdev_init(lcdev->cdev, &lcdev_fops);ret = cdev_add(lcdev->cdev,MKDEV(lcdev->maj,lcdev->mio), lcdev->count);if(ret < 0)goto add_fail;printk(KERN_ALERT"maj: %d ,mio:%d\n", lcdev->maj, lcdev->mio);lcdev->rivotek_class = class_create(THIS_MODULE, "rivotek");if (IS_ERR(lcdev->rivotek_class)) {printk("Class create failed\n"); ret = PTR_ERR(lcdev->rivotek_class);goto register_err;}for(i = 0; i < lcdev->count; i++){char buf[16];sprintf(buf, "rivotek_%d", i);device_create(lcdev->rivotek_class, NULL, MKDEV(lcdev->maj, i), NULL, buf);memset(buf, 0, sizeof(buf));}return 0;add_fail:kobject_put(&lcdev->cdev->kobj);register_err:unregister_chrdev_region(MKDEV(lcdev->maj,lcdev->mio), lcdev->count);if(lcdev)kfree(lcdev);return -1;out:return ret;}static void __exit char_test_exit(void)
{unregister_chrdev(lcdev->maj, CDE_NAME);kfree(lcdev);printk(KERN_ALERT"char test exit\n");
}module_init(char_test_init);
module_exit(char_test_exit);MODULE_LICENSE("GPL");
应用代码设计
应用代码设计思路:
- 设计一个带参的应用,传入设备节点 和 写入该节点 的字符串
- 打开传入的设备节点
- while 死循环中将字符串写入设备节点,写入完成后打印当前的pid和提示“write done”;2s 后从该节点读取字符串并打印读取到的内容。
代码实现如下
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc,char *argv[])
{int ret = -1;int fd = -1;unsigned char buf[32];if (3 > argc) {printf("%s [path] [string]\n", argv[0]);return -1;}fd = open(argv[1], O_RDWR);if ( 0 > fd) {printf("%s open failed\n", argv[1]);return fd;}printf("open ok, fd=%d\n", fd);while(1){memset(buf, 0, sizeof(buf));sprintf(buf, argv[2], sizeof(buf));ret = write (fd, (void *)buf, sizeof(buf));if (0 > ret) {printf("Write fd=%d srting: %s error \n",fd ,argv[2]);return ret;}memset(buf, 0, sizeof(buf));printf("%d Write done\t",getpid());ret = read(fd, (void *)buf, sizeof(buf));if (0 > ret) {printf("Read fd=%d error \n",fd );return ret;}printf("read information: %s\n", buf);sleep(2);}return 0;
}
验证结果
- 执行 “ /wwp/rivote_driver_test_tmp /dev/rivotek_0 123 &”,向驱动中读写字符串“abcd”
从打印结果来看,当前进程pid 为705,写入和读取都是“123”,均是正常的行为。
- 继续执行“/wwp/rivote_driver_test_tmp /dev/rivotek_0 abcd &”, 向驱动中读写字符串“abcd”
1)从以上打印看,写入字符创abcd 的应用进程号是706,且读写都是正常的
2)先运行的应用pid 为 705 的进程,也在继续运行,且两个进程运行相互不干扰。
综上,驱动中同一个空间,多个应用读写的时候,是互不影响的。