Linux并发控制之原子操作Demo(以全局内存虚拟设备为例)

一、驱动源代码头文件

#ifndef __GLOBAL_MEM_H
#define __GLOBAL_MEM_H

#include <linux/cdev.h>

#define DRIVER_AUTHOR "xz@vi-chip.com.cn"
#define DRIVER_DESC   "A sample driver" 

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define DEV_NAME "globalmem"
#define GLOBALMEM_MAJOR 230 /* 主设备号 */

/**
*
* 定义全局内存字符设备的结构体:
* 借用面向对象程序设计中“封装”的思想,体现良好的编程习惯。
*
*/
struct globalmem_dev {
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};


#endif /* __GLOBAL_MEM_H */

二、驱动源代码文件

#include <linux/init.h>  
#include <linux/module.h> 
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#include <asm/atomic.h>

/* include local head files */
#include "global_mem.h"

static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major, int, S_IRUGO); /* mode:S_IRUGO */

struct globalmem_dev *globalmem_devp = NULL;

static atomic_t globalmem_available = ATOMIC_INIT(1);   //define atomic valiable 

/**
*
* 读写函数
* 让设备结构体globalmem_dev的mem[]数组与用户空间交互数据,
* 并随着访问的字节数变更更新文件读写偏移位置。
*/
// globalmem设备驱动的读函数,从设备中读取数据
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos; // 要读的位置相对于文件开头的偏移,如果该偏移大于或等于GLOBALMEM_SIZE,表示已经到达文件末尾,所以返回0(EOF)。
unsigned int count = size; // 要读取的字节数
int ret = 0;
struct globalmem_dev *dev = filp->private_data; // 使用文件的私有数据访问设备结构体


if (p >= GLOBALMEM_SIZE)
return 0; // 表示已经到达文件末尾,所以返回0(EOF)
if (count > (GLOBALMEM_SIZE - p))
count = GLOBALMEM_SIZE - p;


if (copy_to_user(buf, dev->mem + p, count)) { // 把内核空间中的数据拷贝到用户空间
ret = -EFAULT;
} else {
*ppos += count;
ret = count;
printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
}


return ret; // 实际读取的字节数
}


// globalmem设备驱动的写函数,向设备发送数据
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 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 %u bytes(s) from %lu\n", count, p);
}


return ret;
}


// globalmem设备驱动的seek函数
// seek函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)和文件尾(SEEK_END,2),假设globalmem支持从文件开头和当前位置的相对偏移。
// 在定位的时候,应该检查用户请求的合法性,若不合法,函数返回-EINVAL,合法时更新文件的当前位置并返回该位置。
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig) {
case 0: /* 从文件开头位置 seek */
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: /* 从文件当前位置开始 seek */
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;
}


// ioctl函数
// globalmem设备驱动的ioctl函数接受MEM_CLEAR命令,这个命令会将全局内存的有效数据长度清0,对于设备不支持的命令,ioctl函数应该返回-EINVAL。
static 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 int globalmem_open(struct inode *inode, struct file *filep)
{
if(!atomic_dec_and_test(&globalmem_available)) {  
        printk(KERN_ERR "already open!\n");  
        atomic_inc(&globalmem_available);  /* 原子变量增加1 */
        return -EBUSY;  //已经打开
    }  

filep->private_data = globalmem_devp;// 设置私有数据,将文件的私有数据private_data指向设备结构体的实例,体现Linux的面向对象的设计思想
printk(KERN_INFO "globalmem_open\n");
return 0;
}

static int globalmem_release(struct inode *inode, struct file *filep)
{
printk(KERN_INFO "globalmem_release\n");
atomic_inc(&globalmem_available); /* 原子变量增加1 */
return 0;
}


// globalmem设备驱动的文件操作结构体
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,
};
 
void globalmem_setup_cdev(struct globalmem_dev *dev, int minor)
{
int err, devno = MKDEV(globalmem_major, minor);


cdev_init(&dev->cdev, &globalmem_fops); // 初始化字符设备
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1); // 注册字符设备到内核
if (err)
printk(KERN_NOTICE "Error %d adding globalmem [%d]", err, minor);
}


static int __init global_mem_init(void)
{
int ret;

dev_t devno = MKDEV(globalmem_major, 0); /* 主设备号、次设备号合并为设备号 */
if (globalmem_major)
ret = register_chrdev_region(devno, 1, DEV_NAME); // 静态分配设备号
else {
ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME); // 动态分配设备号
globalmem_major = MAJOR(devno); // 提取主设备号
}
if (ret < 0)
return ret;

globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL); /* 分配内存并且清空内存 */
if (!globalmem_devp) {
ret = -ENOMEM;
goto fail_kmalloc;
}


globalmem_setup_cdev(globalmem_devp, 0); // 注册字符设备
    printk(KERN_INFO "----global_mem_init----\n");
    return 0; // 表示成功

fail_kmalloc:
unregister_chrdev_region(devno, 1); // 注销分配设备号
return ret;
}


static void __exit global_mem_exit(void)
{
cdev_del(&globalmem_devp->cdev); // 注销字符设备
kfree(globalmem_devp); // 释放分配的内存空间
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);// 注销分配设备号
    printk(KERN_INFO "----global_mem_exit----\n");
}


module_init(global_mem_init);
module_exit(global_mem_exit);


MODULE_VERSION("v1.0");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

MODULE_ALIAS(DRIVER_DESC);

三、Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:                               
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:                                             
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
else
    obj-m := global_mem.o

endif

四、驱动测试文件

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


#include <stdio.h>  
  
#define DEV_NAME "/dev/globalmem"

int main(void)  
{  
    int fd = -1;  
    fd = open(DEV_NAME, O_RDWR);  
    if(fd == -1) {  
        printf("open error!!\n");  
        return -1;  
    }  
    sleep(60);  // 延时60s
    printf("close fd!\n");  
    close(fd);  
    return 0;  

}      

五、验证结果

1、安装内核模块

sudo insmod global_mem.ko

2、执行测试程序

./test

./test

3、应用程序打印:open error!!

4、驱动程序打印: already open!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值