/***************************************************************************************************
设备的阻塞与非阻塞操作
概念:
阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起,进入sleep 状态,从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作的进程在不能进行设备操作时,并不挂起。
方法:
在 Linux 驱动程序中,我们可以使用等待队列(wait queue)来实现阻塞操作。
wait queue是linux内核的一个基本单位,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。
等待队列可以用来同步对系统资源的访问,上节中所讲述 Linux 信号量在内核中也是由等待队列来实现的。
实例
下面我们重新定义设备“globalvar”,它可以被多个进程打开,但是每次只有当一个进
程写入了一个数据之后本进程或其它进程才可以读取该数据,否则一直阻塞。
等待队列的使用方法:
static wait_queue_head_t outq; //定义等待队列
static int flag = 0;
init_waitqueue_head(&outq); //初始化
在读函数使用:
if (wait_event_interruptible(outq, flag != 0)) //等待数据可获得
{
return - ERESTARTSYS;
}
flag = 0;
在写函数使用
flag = 1;
wake_up_interruptible(&outq);
***************************************************************************************************/
/***************************************************************************************************
操作流程:
终端1 # tail -f /var/log/message 用于查看信息
终端2 # make 编译驱动
# gcc usr_r.c -o ar.out 编译用户读函数
# gcc usr_w.c -o aw.out 编译用户写函数
# insmod globalvar.ko 需要root权限
# mknod /dev/globalvar c 1024 0
终端3 # ./ar.out
终端3 # ./aw.out 不能打开设备,操作失败
# rmmod globalvar.ko
***************************************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
#define MAJOR_NUM 1024 //主设备号
static ssize_t globalvar_read (struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
static int global_var = 0; //设备的全局变量
static wait_queue_head_t outq; //定义等待队列
static int flag = 0;
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
//等待数据可获得
if (wait_event_interruptible(outq, flag != 0))
{
return - ERESTARTSYS;
}
flag = 0;
//将global_var从内核空间复制到用户空间
if ( copy_to_user(buf, &global_var, sizeof(int)))
{
return - EFAULT;
}
printk(KERN_ALERT "globalvar_read enter ,the data is %d\n",global_var);
return sizeof(int);
}
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
//将用户空间的数据复制到内核空间的global_var
if (copy_from_user(&global_var, buf, sizeof(int)))
{
return - EFAULT;
}
printk(KERN_ALERT "globalvar_write enter ,the data is %d\n",global_var);
flag = 1;
wake_up_interruptible(&outq);
return sizeof(int);
}
struct file_operations globalvar_fops =
{
.write = globalvar_write,
.read = globalvar_read,
};
static int __init globalvar_init(void)
{
int ret;
ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops); //注册设备驱动
if (ret)
{
printk(KERN_ALERT "globalvar register failure\n");
}else
{
init_waitqueue_head(&outq);
printk(KERN_ALERT "globalvar register success\n");
}
return ret;
}
static void __exit globalvar_exit(void)
{
printk(KERN_ALERT "globalvar exit success\n");
unregister_chrdev(MAJOR_NUM, "globalvar");
}
module_init(globalvar_init);
module_exit(globalvar_exit);
/***************************************************************************************************
用户写入数据函数
user_w.c
***************************************************************************************************/
#include <stdio.h>
#include <sys/stat.h> //获得文件的属性,它可以返回一个结构,里面包含文件全部属性
#include <fcntl.h> //设备驱动程序接口是由结构说明,它定义在fcntl.h中
#include <sys/types.h> //类型 clock_t,dev_t,off_t,ptrdiff,size_t,ssize_t,time_t
int main(void)
{
int fd, num;
fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
printf("fd = %d \n",fd);
if (fd == -1 )
{
printf("Device open failure\n");
return 0;
}
while(1)
{
printf("Please input the globalvar:\n");
scanf("%d", &num);
write(fd, &num, sizeof(int));
if (num == 0) //如果输入是 0,则退出
{
close(fd);
break;
}
}
return 0;
}
/***************************************************************************************************
用户读出数据函数
user_r.c
在‘深入浅出LDD-4-并发控制1’代码上修改而成
***************************************************************************************************/
#include <stdio.h>
#include <sys/stat.h> //获得文件的属性,它可以返回一个结构,里面包含文件全部属性
#include <fcntl.h> //设备驱动程序接口是由结构说明,它定义在fcntl.h中
#include <sys/types.h> //类型 clock_t,dev_t,off_t,ptrdiff,size_t,ssize_t,time_t
int main(void)
{
int fd, num;
fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
printf("fd = %d \n",fd);
if (fd == -1 )
{
printf("Device open failure\n");
return 0;
}
while(1)
{
read(fd, &num, sizeof(int)); //程序将阻塞在此语句,除非有针对 globalvar 的输入
printf("The globalvar is %d\n", num);
if (num == 0) //如果输入是 0,则退出
{
close(fd);
break;
}
}
return 0;
}