阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。非阻塞操作的进程在不能进行设备操作时,并不挂起。被挂起的进程进入 sleep状态,被从调度器的运行队列移走,直到等待的条件被满足。在 Linux驱动程序中,我们可以使用等待队列( wait queue)来实现阻塞操作。 wait queue很早就作为一个基本的功能单位出现在 Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。等待队列可以用来同步对系统资源的访问,上节中所讲述 Linux信号量在内核中也是由等待队列来实现的。
下面我们重新定义设备“ globalvar”,它可以被多个进程打开,但是每次只有当一个进程写入了一个数据之后本进程或其它进程才可以读取该数据,否则一直阻塞。
//globalvar.c
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
#define MAJOR_NUM 254 (如果编译的过程中出现 erro inserting "globalvar.ko":-1 Devices or resource busy这样的错误 ,则说明我们注册的这个254的字符设备号已经与系统的重名了,所以得重新设一个新的,可以通过查看/proc/devices下的已有设备号 防止编号重复 )
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*);
struct file_operations globalvar_fops =
{
read: globalvar_read, write: globalvar_write,
};
static int global_var = 0;
static struct semaphore sem;
static wait_queue_head_t outq;
static int flag = 0;
static int __init globalvar_init(void)
{
int ret;
ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
if (ret)
{
printk("globalvar register failure");
}
else
{
printk("globalvar register success");
init_MUTEX(&sem);
init_waitqueue_head(&outq);
}
return ret;
}
static void __exit globalvar_exit(void)
{
//int ret;
unregister_chrdev(MAJOR_NUM, "globalvar"); //在2.6中因为此函数没有返回值
/*if (ret)
{
printk("globalvar unregister failure");
}
else
{
printk("globalvar unregister success");
} */
}
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;
}
if (down_interruptible(&sem))
{
return -ERESTARTSYS;
}
flag = 0;
if (copy_to_user(buf, &global_var, sizeof(int)))
{
up(&sem);
return -EFAULT;
}
up(&sem);
return sizeof(int);
}
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len,
loff_t *off)
{
if (down_interruptible(&sem))
{
return -ERESTARTSYS;
}
if (copy_from_user(&global_var, buf, sizeof(int)))
{
up(&sem);
return -EFAULT;
}
up(&sem);
flag = 1;
//通知数据可获得
wake_up_interruptible(&outq);
return sizeof(int);
}
module_init(globalvar_init);
module_exit(globalvar_exit);
编写两个用户态的程序来测试,第一个用于阻塞地读
/dev/globalvar,另一个用于写 /dev/globalvar。只有当后一个对/dev/globalvar进行了输入之后,前者的
read才能返回。
读的程序为:
#include
#include
#include
#include
main()
{
int fd, num;
fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != - 1)
{
while (1)
{
read(fd, &num, sizeof(int)); //程序将阻塞在此语句,除非有针对
globalvar的输入
printf("The globalvar is %d\n", num);
//如果输入是
0,则退出
if (num == 0)
{
close(fd);
break;
}
}
}
else
{
printf("device open failure\n");
}
}
写的程序为:
#include
#include
#include
#include
main()
{
int fd, num;
fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != - 1)
{
while (1)
{
printf("Please input the globalvar:\n");
scanf("%d", &num);
write(fd, &num, sizeof(int));
//如果输入
0,退出
if (num == 0)
{
close(fd);
break;
}
}
}
else
{
printf("device open failure\n");
}
}
打开两个终端,分别运行上述两个应用程序,发现当在第二个终端中没有输入数据时,
第一个终端没有输出(阻塞),每当我们在第二个终端中给 globalvar输入一个值,第一个终端就会输出这个值,如下图:
/home/zhaoqing/桌面/Screenshot.png[attach]38758[/attach]
bamar6091 于 2012-08-08 21:44:07发表:
不要去打扰她的生活,她只会觉得从前看错人,你也会鄙视自己。
rformance 于 2012-08-08 21:29:18发表:
学习了