一个信号量本质上是一个整数值,它和一对函数联合使用,P(-1)和V(+1)。内核中的信号量通常用于互斥;用于互斥时,信号量的值初始化为1,用于同步时初始化为0。
内核使用信号量包含<linux/semaphore.h>,相关的类型为struct semaphore。
信号量初始化:
void sema_init(struct semaphore *sem, int val);
val:信号量的初始值
P函数:
int down(struct semaphore *sem);
减小信号量的值,并在必要时一直等待,不可中断。
int down_interruptible(struct semaphore *sem); //建议使用
减小信号量的值,并在必要时一直等待,但是操作是可以中断的,允许等待在某个信号量上的用户空间进程可被用户中断(如:ctrl+c)。如果返回非零值,表示操作被中断,应返回-ERESTARTSYS。
int down_trylock(struct semaphore *sem);
不会休眠,如果信号量在调用时不可获得会立即返回一个非零值。
当某个线程或进程P后,表示拥有信号量,即拥有访问该信号量保护的临界区的权利。
V函数:
void up(struct semaphore *sem);
当互斥操作完后,必须返回该信号量,调用up后,调用者不再拥有信号量。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
static int major = 277;
static int minor = 0;
static dev_t devnum1;
static struct cdev cdev1;
struct semaphore sem;
int hello_open(struct inode *pinode, struct file *pfile)
{
printk("hello_open, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
return 0;
}
int hello_release(struct inode *pinode, struct file *pfile)
{
printk("hello_release, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
up(&sem);
return 0;
}
ssize_t hello_read(struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
return 0;
}
ssize_t hello_write(struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{
/*
if(down_interruptible(&sem))
{
return -ERESTARTSYS;
}
...
copy_from_user();
...
up(&sem);
*/
return 0;
}
static struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
static int hello_init(void)
{
printk("hello_init\n");
devnum1 = MKDEV(major,minor);
printk("major:%d, minor:%d\n", MAJOR(devnum1), MINOR(devnum1));
if (register_chrdev_region(devnum1, 1, "hello1"))//分配设备号
{
printk("register_chrdev_region failed\n");
return -1;
}
cdev_init(&cdev1, &hello_ops);//初始化cdev,绑定fops
if (cdev_add(&cdev1, devnum1, 1) < 0)//添加设备到内核
{
printk("cdev_add failed\n");
unregister_chrdev_region(devnum1, 1);
return -1;
}
sema_init(&sem,1);//初始化信号量,值为1,用于互斥
return 0;
}
static void hello_exit(void)
{
printk("hello_exit\n");
cdev_del(&cdev1);
unregister_chrdev_region(devnum1, 1);
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char * argv [ ])
{
int fd = open("/dev/hello0", O_RDWR, 666);
if (fd < 0)
{
perror("open faied");
return -1;
}
printf("open successed: %d\n", fd);
sleep(10);
printf("close %d\n", fd);
close(fd);
return 0;
}
实现同一时间只能打开一个设备。
现象:运行两个demo,先运行的demo占有信号量,在sleep的过程中ctrl+c可中断,后运行的demo获取信号量。
还可以实现write互斥,同一时间只能允许一个调用者进行写操作。