Android深度探索:HAL与驱动开发学习笔记--并发控制之信号量&完成量
2017-09-28 08:57
661 查看
semaphore
使用方法和自旋锁类似,与自旋锁相同,只有得到信号量的进程才能执行临界区代码,但是与自旋锁不同的是当获取不到信号量时,进程不原地打转而是进入休眠等待状态。
声明变量:
struct semaphore sem;
快捷方式:
DECLARE_MUTEX(name) /* 定义一个名为name的信号量并初始化为1 */
DECLARE_MUTEX_LOCKED(name) /* 定义一个名为name的信号量并初始化为0 */
信号量的初始化:
void sema_init(struct semaphore *sem,int val);
参数:
sem,信号量
val,信号量的数量
信号量的获取: --相当于PV操作中的 P 操作
void down(struct semaphore *sem); -->深度睡眠(没有资源时深度睡眠,无法被唤醒,只有资源来时才醒,是睡眠等待)
int down_interruptible(struct semaphore *sem); -->可以被唤醒(浅睡)
int down_trylock(struct semaphore *sem); --> 如果能获得,获得,并返回0,否则,返回非0
if(down_interruptible(&sem))
return -ERESTARTSYS;
信号量的释放: --相当于PV操作中的 V 操作
void up(struct semaphore *sem);
/* 定义信号量 */
DECLARE_MUTEX(mount_sem);
down(&mount_sem); /* 获取信号量,保护临界区 */
...
critical section /* 临界区 */
...
up(&mount_sem); /* 释放信号量 */
使用信号量实现只能被一个进程打开:
[cpp] view
plain copy
static DECLARE_MUTEX(xxx_lock); /* 定义互斥锁 */
static int xxx_open(struct inode *inode, struct file *filp)
{
...
if(down_trylock(&xxx_lock)) /* 获取打开锁 */
return -EBUSY; /* 设备忙 */
...
return 0; /* 成功 */
}
static int xxx_release(struct inode *inode, struct file *filp)
{
up(&xxx_lock); /* 释放打开锁 */
return 0;
}
读和写实现互斥:
[cpp] view
plain copy
#include <linux/kernel.h>
#include <linux/init.h>
1eee0
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/module.h>
MODULE_LICENSE ("GPL");
int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
struct hello_device
{
char data[128];
struct semaphore sem;
struct cdev cdev;
} hello_device;
static int hello_open (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hey! device opened\n");
return 0;
}
static int hello_release (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hmmm... device closed\n");
return 0;
}
ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
ssize_t result = 0;
if (count > 127) count = 127;
if (down_interruptible(&hello_device.sem))
return -EINTR;
if (copy_to_user (buff, hello_device.data, count))
{
result = -EFAULT;
}
else
{
printk (KERN_INFO "wrote %d bytes\n", count);
result = count;
}
up(&hello_device.sem);
return result;
}
ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
ssize_t ret = 0;
if (count > 127) return -ENOMEM;
if (down_interruptible(&hello_device.sem)) return -EINTR;
if (copy_from_user (hello_device.data, buf, count)) {
ret = -EFAULT;
}
else {
hello_device.data[count] = '\0';
printk (KERN_INFO"Received: %s\n", hello_device.data);
ret = count;
}
up(&hello_device.sem);
return ret;
}
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write
};
static void char_reg_setup_cdev (void)
{
int error;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
cdev_init (&hello_device.cdev, &hello_fops);
hello_device.cdev.owner = THIS_MODULE;
error = cdev_add (&hello_device.cdev, devno , 1);
if (error)
printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}
static int __init hello_2_init (void)
{
int result;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (devno, number_of_devices, "hello");
if (result < 0) {
printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
return result;
}
char_reg_setup_cdev ();
//init_MUTEX(&hello_device.sem); for version not more than 2.6.35
sema_init(&hello_device.sem, 1);
printk (KERN_INFO "char device registered\n");
return 0;
}
static void __exit hello_2_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&hello_device.cdev);
unregister_chrdev_region (devno, number_of_devices);
}
module_init (hello_2_init);
module_exit (hello_2_exit);
同步:一个执行单元的继续执行需要等待另一个执行单元完成某事,保证执行的顺序
步骤:
信号量被初始化为0
执行单元 A 中 down(&sem) (这里等待 B 中完成到 up()&sem)
执行单元 B 中 up(&sem)
一个执行单元等待另一个执行单元执行完某事:
函数如下:
定义:
struct completion my_completion;
初始化:
init_completion(&my_completion);
定义和初始化:
DECLARE_COMPLETION(my_completion);
等待完成量: -- 等待一个完成量的唤醒
void wait_for_compleition(struct completion *c);
唤醒完成量:
void complete(struct completion *c); --> 唤醒一个等待的执行单元
void complete_all(struct completion *c); --> 释放所有等待同一完成量的执行单元
使用方法:
前者只使唤一个等待的执行单元,后者释放所有等待统一完成量的执行单元
读写信号量可能引起进程阻塞,它允许 N 个读执行单元同时访问共享资源,最多只能有 1 个写执行单元
函数如下:
读写信号量定义和初始化:
struct rw_semaphore my_rws; /* 定义 */
void init_rwsem(struct rw_semaphone *sem); /* 初始化 */
读信号获取:
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
读信号量释放:
void up_read(struct rw_semaphore *sem);
写信号量获取:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
写信号量释放:
void up_write(struct rw_semaphore *sem);
使用格式:
rw_semaphore rw_sem;
init_rwsem(&rw_sem);
/* 读时获取信号 */
down_read(&rw_sem);
...
up_read(&rw_sem);
/* 写时获取信号 */
down_write(&rw_sem);
...
up_write(&rw_sem);
使用方法和自旋锁类似,与自旋锁相同,只有得到信号量的进程才能执行临界区代码,但是与自旋锁不同的是当获取不到信号量时,进程不原地打转而是进入休眠等待状态。
1. 函数:
声明变量:struct semaphore sem;
快捷方式:
DECLARE_MUTEX(name) /* 定义一个名为name的信号量并初始化为1 */
DECLARE_MUTEX_LOCKED(name) /* 定义一个名为name的信号量并初始化为0 */
信号量的初始化:
void sema_init(struct semaphore *sem,int val);
参数:
sem,信号量
val,信号量的数量
信号量的获取: --相当于PV操作中的 P 操作
void down(struct semaphore *sem); -->深度睡眠(没有资源时深度睡眠,无法被唤醒,只有资源来时才醒,是睡眠等待)
int down_interruptible(struct semaphore *sem); -->可以被唤醒(浅睡)
int down_trylock(struct semaphore *sem); --> 如果能获得,获得,并返回0,否则,返回非0
if(down_interruptible(&sem))
return -ERESTARTSYS;
信号量的释放: --相当于PV操作中的 V 操作
void up(struct semaphore *sem);
2. 使用格式:
/* 定义信号量 */DECLARE_MUTEX(mount_sem);
down(&mount_sem); /* 获取信号量,保护临界区 */
...
critical section /* 临界区 */
...
up(&mount_sem); /* 释放信号量 */
3. 例子:
使用信号量实现只能被一个进程打开:[cpp] view
plain copy
static DECLARE_MUTEX(xxx_lock); /* 定义互斥锁 */
static int xxx_open(struct inode *inode, struct file *filp)
{
...
if(down_trylock(&xxx_lock)) /* 获取打开锁 */
return -EBUSY; /* 设备忙 */
...
return 0; /* 成功 */
}
static int xxx_release(struct inode *inode, struct file *filp)
{
up(&xxx_lock); /* 释放打开锁 */
return 0;
}
读和写实现互斥:
[cpp] view
plain copy
#include <linux/kernel.h>
#include <linux/init.h>
1eee0
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/module.h>
MODULE_LICENSE ("GPL");
int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
struct hello_device
{
char data[128];
struct semaphore sem;
struct cdev cdev;
} hello_device;
static int hello_open (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hey! device opened\n");
return 0;
}
static int hello_release (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hmmm... device closed\n");
return 0;
}
ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
ssize_t result = 0;
if (count > 127) count = 127;
if (down_interruptible(&hello_device.sem))
return -EINTR;
if (copy_to_user (buff, hello_device.data, count))
{
result = -EFAULT;
}
else
{
printk (KERN_INFO "wrote %d bytes\n", count);
result = count;
}
up(&hello_device.sem);
return result;
}
ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
ssize_t ret = 0;
if (count > 127) return -ENOMEM;
if (down_interruptible(&hello_device.sem)) return -EINTR;
if (copy_from_user (hello_device.data, buf, count)) {
ret = -EFAULT;
}
else {
hello_device.data[count] = '\0';
printk (KERN_INFO"Received: %s\n", hello_device.data);
ret = count;
}
up(&hello_device.sem);
return ret;
}
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write
};
static void char_reg_setup_cdev (void)
{
int error;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
cdev_init (&hello_device.cdev, &hello_fops);
hello_device.cdev.owner = THIS_MODULE;
error = cdev_add (&hello_device.cdev, devno , 1);
if (error)
printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}
static int __init hello_2_init (void)
{
int result;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (devno, number_of_devices, "hello");
if (result < 0) {
printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
return result;
}
char_reg_setup_cdev ();
//init_MUTEX(&hello_device.sem); for version not more than 2.6.35
sema_init(&hello_device.sem, 1);
printk (KERN_INFO "char device registered\n");
return 0;
}
static void __exit hello_2_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&hello_device.cdev);
unregister_chrdev_region (devno, number_of_devices);
}
module_init (hello_2_init);
module_exit (hello_2_exit);
4. 信号量可以用于同步:
同步:一个执行单元的继续执行需要等待另一个执行单元完成某事,保证执行的顺序步骤:
信号量被初始化为0
执行单元 A 中 down(&sem) (这里等待 B 中完成到 up()&sem)
执行单元 B 中 up(&sem)
5. 更好的实现同步的方法:完成量
一个执行单元等待另一个执行单元执行完某事:函数如下:
定义:
struct completion my_completion;
初始化:
init_completion(&my_completion);
定义和初始化:
DECLARE_COMPLETION(my_completion);
等待完成量: -- 等待一个完成量的唤醒
void wait_for_compleition(struct completion *c);
唤醒完成量:
void complete(struct completion *c); --> 唤醒一个等待的执行单元
void complete_all(struct completion *c); --> 释放所有等待同一完成量的执行单元
使用方法:
前者只使唤一个等待的执行单元,后者释放所有等待统一完成量的执行单元
6. 读写信号量:
读写信号量可能引起进程阻塞,它允许 N 个读执行单元同时访问共享资源,最多只能有 1 个写执行单元函数如下:
读写信号量定义和初始化:
struct rw_semaphore my_rws; /* 定义 */
void init_rwsem(struct rw_semaphone *sem); /* 初始化 */
读信号获取:
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
读信号量释放:
void up_read(struct rw_semaphore *sem);
写信号量获取:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
写信号量释放:
void up_write(struct rw_semaphore *sem);
使用格式:
rw_semaphore rw_sem;
init_rwsem(&rw_sem);
/* 读时获取信号 */
down_read(&rw_sem);
...
up_read(&rw_sem);
/* 写时获取信号 */
down_write(&rw_sem);
...
up_write(&rw_sem);
相关文章推荐
- Android深度探索:HAL与驱动开发学习笔记--并发控制之自旋锁
- Android深度探索:HAL与驱动开发学习笔记--并发控制之互斥锁
- Android深度探索:HAL与驱动开发学习笔记--并发控制之顺序锁
- Android深度探索:HAL与驱动开发学习笔记--并发控制总结
- Android深度探索:HAL与驱动开发学习笔记--工作队列
- Android深度探索:HAL与驱动开发学习笔记--内存管理(学习重点)
- Android深度探索:HAL与驱动开发学习笔记--等待队列
- Android深度探索:HAL与驱动开发学习笔记--中断
- Android深度探索:HAL与驱动开发学习笔记--时间管理
- Android深度探索 卷1 HAL与驱动开发 读书笔记1
- Android深度探索:HAL与驱动开发学习笔记--并发控制之原子操作
- Android深度探索(卷1)HAL与驱动开发 虚拟环境的安装
- linux 设备驱动开发学习笔记(一):并发控制
- android 开发零起步学习笔记(九):android 控制控件的位置和大小及Layout相关属性
- Android开发艺术探索学习笔记2——IPC机制
- android开发艺术探索 学习笔记(三) IntentFilter的匹配规则
- Android开发艺术探索学习笔记1——Avtivity生命周期和启动模式
- Android 开发艺术探索 学习笔记 Activity
- [Kotlin&Anko开发Android入门学习笔记]-02Kotlin如何使用Android第三方库
- Android开发艺术探索学习笔记(六)