semaphore
使用方法和自旋锁类似,与自旋锁相同,只有得到信号量的进程才能执行临界区代码,但是与自旋锁不同的是当获取不到信号量时,进程不原地打转而是进入休眠等待状态。
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. 例子:
使用信号量实现只能被一个进程打开:
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; }
读和写实现互斥:
#include <linux/kernel.h> #include <linux/init.h> #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);