linux设备驱动编程之同步

1 linux设备驱动同步的方法

在这里插入图片描述
如上图,Linux设备驱动在编程中同步的代码逻辑都是一个进程(例如进程1)在阻塞等待才可以进入想要访问代码区1,而另一进程(如进程2)负责当执行完代码区2后,具备了唤醒进程1的条件后便会唤醒进程1,然后进程1继续执行代码区1。整过过程叫做进程间信息同步。完成同步的方法目前linux设备驱动编程中主要有有如下两种:
(1)信号量

//头文件:
#include <linux/semaphore.h>

//API:
struct semaphore sem;//声明一个信号量
void sema_init(struct semaphore *sem, int val)//初始化信号量
void down(struct semaphore *sem);//获取等待信号量,s>=0,进程继续执行,否则阻塞等待唤醒
void up(struct semaphore *sem);//释放信号量,s+=1;等s>0=0时,唤醒阻塞等待的进程


//可被中断信号打断,被信号打断通常返回非零的值。
int __must_check down_interruptible(struct semaphore *sem);

//获取信号量不满足,不阻塞立即返回非0值,否则成功执行。
int __must_check down_trylock(struct semaphore *sem);

#define __must_check            __attribute__((warn_unused_result))
//__must_check函数是指调用函数一定要处理该函数的返回值,否则编译器会给出警告。

(2)完成量

//头文件:
#include <linux/completion.h>

//API:
struct completion my_completion;//声明一个完成量

//完成量的初始化
#define init_completion(x)						\
do {									\
	static struct lock_class_key __key;				\
	lockdep_init_map_crosslock((struct lockdep_map *)&(x)->map,	\
			"(complete)" #x,				\
			&__key, 0);					\
	__init_completion(x);						\
} while (0)

//阻塞等待完成完成量
void __sched wait_for_completion(struct completion *x)
{
	wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion);


//唤醒阻塞等待的完成量
void complete(struct completion *);//只能唤醒一个等待进程或者线程
void complete_all(struct completion *);//唤醒全部等待进程或者线程
//注意:如果调用complete_all后,如果果要重复使用一个completion结构,必须执行 INIT_COMPLETION(struct completion c)来重新初始化它
//当有多个完成量等待的时候,可以通过complete_all唤醒全部等待进程
/* Attach to any functions which should be ignored in wchan output. */
#define __sched         __attribute__((__section__(".sched.text")))

函数前面带__sched的意思就是把带有__sched的函数放到.sched.text段。
而.sched.text段的作用是如果不想让函数在waiting channel中显示出来,就该加上__sched。这是因为kernel有个waiting channel,如果用户空间的进程睡眠了,可以在用户空间用命令查到是停在内核空间哪个函数中等待,如下:

cat "/proc/<pid>/wchan"

那显然,.sched.text段的代码是会被wchan忽略的,schedule这个函数是不会出现在wchan的结果中的

2.代码实例

利用信号量以及完成量来完成设备驱动的信息同步。
例如,定义一个缓冲区,每次读进程要阻塞等待写进程写进去缓冲区才可以读取。/dev/globalmem设备驱动参考宋宝华老师的linux驱动设备编程,无冒犯抄袭之意,仅仅学习总结。

信号量方式:

#include "globalmem.h"
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <linux/sched/signal.h>
#include <linux/delay.h>
#include <linux/semaphore.h>

#define GLOBALMEM_BUFFER_SIZE 50
#define GLOBALMEM_MAJOR 255

struct globalmem_dev
{
	dev_t devno;
	struct cdev dev;
	unsigned char globalmem_buf[GLOBALMEM_BUFFER_SIZE];
	//(1)声明信号量
	struct semaphore sem;
};

struct globalmem_dev *globalmem_devp = NULL;

static int globalmem_open(struct inode *inode, struct file *filp)
{
	if(!globalmem_devp)
		return -ENODEV;

	filp->private_data = globalmem_devp;

	return 0;
}

static int globalmem_release(struct inode *inode, struct file *filp)
{
	if(filp->private_data)
		filp->private_data = NULL;

	if(!globalmem_devp)
		return -ENODEV;

}

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;
	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	//判断opps是否大于最大值
	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE -p;

	//(3)获取信号量,信号量的值-1,当信号量值为大于或者等于0,代表信号量获取成功,并且返回。否则将会休眠阻塞等待
	//阻塞过程中被信号打断,返回值为非零,直接返回
	if(down_interruptible(&dev->sem)){
		return	-ERESTARTSYS;
	}
	
	if(copy_to_user(buf,dev->globalmem_buf,count)){
		ret = -EFAULT;
	}else{
		*ppos = +count;
		ret = count;
	}
	//释放信号量,让信号量的值+1
	up(&dev->sem);
	return ret;
}

static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;

	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE - p;
		
	if(copy_from_user(dev->globalmem_buf,buf,count)){
		ret = -EFAULT;
	}else{
		*ppos += count;
		ret = count; 
	}
	//(4)释放信号量,唤醒读进程
	up(&dev->sem);
	return ret;
}

struct file_operations globalmem_fops = {
	.open = globalmem_open,
	.read = globalmem_read,
	.write = globalmem_write,
	.release = globalmem_release
};



static int __init globalmem_init(void)
{
	int ret ;

	globalmem_devp = (struct globalmem_dev *)kzalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
	if(!globalmem_devp)
		return -ENOMEM;

	globalmem_devp->devno = MKDEV(GLOBALMEM_MAJOR,0);

	if(GLOBALMEM_MAJOR)
		ret = register_chrdev_region(globalmem_devp->devno,1,"globalmem");
	else
		ret = alloc_chrdev_region(&globalmem_devp->devno,0,1,"globalmem");
	if(ret < 0)
		return ret;

	cdev_init(&globalmem_devp->dev,&globalmem_fops);
	globalmem_devp->dev.owner = THIS_MODULE;
	ret = cdev_add(&globalmem_devp->dev,globalmem_devp->devno,1);
	if(ret < 0 ){
		unregister_chrdev_region(globalmem_devp->devno,1);
		kfree(globalmem_devp);
		return ret;
	}

	//(2)初始化信号量值为0
	sema_init(&globalmem_devp->sem,0);

	return 0;
}

static void __exit globalmem_exit(void)
{
	cdev_del(&globalmem_devp->dev);
	unregister_chrdev_region(globalmem_devp->devno,1);
	kfree(globalmem_devp);
}

module_init(globalmem_init);
module_exit(globalmem_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("gentle");

(2)完成量

#include "globalmem.h"
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/signal.h>
#include <linux/sched/signal.h>
#include <linux/delay.h>
#include <linux/semaphore.h>


#define GLOBALMEM_BUFFER_SIZE 50
#define GLOBALMEM_MAJOR 255

struct globalmem_dev
{
	dev_t devno;
	struct cdev dev;
	unsigned char globalmem_buf[GLOBALMEM_BUFFER_SIZE];

	//(1)声明一个完成量
	struct completion globalmem_complet;
};

struct globalmem_dev *globalmem_devp = NULL;

static int globalmem_open(struct inode *inode, struct file *filp)
{
	if(!globalmem_devp)
		return -ENODEV;

	filp->private_data = globalmem_devp;

	return 0;
}

static int globalmem_release(struct inode *inode, struct file *filp)
{
	if(filp->private_data)
		filp->private_data = NULL;

	if(!globalmem_devp)
		return -ENODEV;

}

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;
	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	//判断opps是否大于最大值
	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE -p;

	//(3)阻塞等待完成量
	wait_for_completion(&dev->globalmem_complet);
	if(copy_to_user(buf,dev->globalmem_buf,count)){
		ret = -EFAULT;
	}else{
		*ppos = +count;
		ret = count;
	}

	return ret;


}

static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned long count = size;

	struct globalmem_dev *dev = filp->private_data;
	long long p = *ppos;

	if(p >= GLOBALMEM_BUFFER_SIZE)
		return 0;

	if(count > GLOBALMEM_BUFFER_SIZE - p)
		count = GLOBALMEM_BUFFER_SIZE - p;

	if(copy_from_user(dev->globalmem_buf,buf,count)){
		ret = -EFAULT;
	}else{
		*ppos += count;
		ret = count; 
	}

	//(4)唤醒等待完成量
	complete(&dev->globalmem_complet);
	return ret;

}

struct file_operations globalmem_fops = {
	.open = globalmem_open,
	.read = globalmem_read,
	.write = globalmem_write,
	.release = globalmem_release
};



static int __init globalmem_init(void)
{
	int ret ;

	globalmem_devp = (struct globalmem_dev *)kzalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
	if(!globalmem_devp)
		return -ENOMEM;

	globalmem_devp->devno = MKDEV(GLOBALMEM_MAJOR,0);

	if(GLOBALMEM_MAJOR)
		ret = register_chrdev_region(globalmem_devp->devno,1,"globalmem");
	else
		ret = alloc_chrdev_region(&globalmem_devp->devno,0,1,"globalmem");
	if(ret < 0)
		return ret;

	cdev_init(&globalmem_devp->dev,&globalmem_fops);
	globalmem_devp->dev.owner = THIS_MODULE;
	ret = cdev_add(&globalmem_devp->dev,globalmem_devp->devno,1);
	if(ret < 0 ){
		unregister_chrdev_region(globalmem_devp->devno,1);
		kfree(globalmem_devp);
		return ret;
	}

	//(2)初始化完成量
	init_completion(&globalmem_devp->globalmem_complet);

	return 0;
}

static void __exit globalmem_exit(void)
{
	cdev_del(&globalmem_devp->dev);
	unregister_chrdev_region(globalmem_devp->devno,1);
	kfree(globalmem_devp);
}

module_init(globalmem_init);
module_exit(globalmem_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("gentle");

(3)用户空间验证:
在终端1输入:这时候会一直阻塞,

cat /dev/globalmem

直到并在另一个终端2输入

echo "hello wr sync ..." >/dev/globalmem 

这时候第一个终端1会输入结果

cat /dev/globalmem 
hello wr sync ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值