/***********************************************************************
*文件名称:cjf_chdev.c
*简要描述:在cjf_chdev.c中增加信号量对cjf_chdev_var进行互斥保护
*当前版本:2.0
*作者:Ceagle
*修改说明:在多道处理程序中,用户对设备的并发访问是无法避免的,如果处理不
* 当,会造成使用错误甚至死机,为了保证设备正常使用,设备驱动程序
* 必须具备并发控制功能。
***********************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ceagle");
#define MAJOR_NUM 250 //定义主设备号为250,如果为0,表示内核动态分配设备号
#define DEVICE_NAME "cjf_chdev" //定义设备号的名称
#define DATA_NUM 20 //写入设备的字符串的最大长度
//声明读数据函数
static ssize_t cjf_chdev_read(struct file *filp,char *,size_t,loff_t *);
//声明写数据函数
static ssize_t cjf_chdev_write(struct file *filp,char *,size_t,loff_t *);
//定义写入设备的字符串数组
static char cjf_chdev_var[DATA_NUM] ;
//定义信号量
static struct semaphore sem;
static const struct file_operations cjf_chdev_fops =
{
.read = cjf_chdev_read,
.write = cjf_chdev_write,
};
static ssize_t cjf_chdev_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
//down_interruptible(&sem)函数用于获取信号量,如果sem大于0就减1
//返回sem;如果sem=0,就使进程进入休眠状态;该函数相当于信号量的
//P操作.
if (down_interruptible(&sem)) {
return -ERESTARTSYS;
}
//从内核拷贝数据到用户空间
if (copy_to_user(buf,&cjf_chdev_var,sizeof(cjf_chdev_var))) {
//up函数用于释放信号量,并将sem加1,接着唤醒处于等待状态的进程
//相当于信号量的V操作
up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(cjf_chdev_var);
}
static ssize_t cjf_chdev_write(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
if (down_interruptible(&sem)) {
return -ERESTARTSYS;
}
//从用户空间拷贝数据到内核空间
if (copy_from_user(&cjf_chdev_var,buf,sizeof(cjf_chdev_var))) {
up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(cjf_chdev_var);
}
static int __init cjf_chdev_init(void)
{
int reg;
//使用函数register_chrdev将主设备号和设备名注册到内核中,
//如果注册成功,返回0,否则返回负数;
//如果成功注册动态分配的设备,则返回设备号
reg = register_chrdev(MAJOR_NUM,DEVICE_NAME,&cjf_chdev_fops);
if (reg) {
printk("register fail!\n");
} else {
printk("register success!\n");
init_MUTEX(&sem);//初始化一个互斥锁,并将信号量设置为,1表示可用
}
return reg;
}
static void __exit cjf_chdev_exit(void)
{
//使用register_chrdev将设备号和设备名从内核卸载
unregister_chrdev(MAJOR_NUM,DEVICE_NAME);
printk("chardev has been unregistered!\n");
}
module_init(cjf_chdev_init);
module_exit(cjf_chdev_exit);
|