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 ...