简单休眠实验(sleepy.c)
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h> /* current and everything */
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/types.h> /* size_t */
#include <linux/wait.h>
MODULE_LICENSE("GPL");
static int sleepy_major = 0;
static DECLARE_WAIT_QUEUE_HEAD(wq); //定义并初始化一个等待队列头
static int flag = 0;
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);
wait_event_interruptible(wq, flag != 0); //如果flag != 0条件成立,程序继续执行,否则保持休眠
flag = 0; //flag重新归零
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
flag = 1;
wake_up_interruptible(&wq); //执行写操作时,队列头wq所在队列的所有进程
return count; /* succeed, to avoid retrial */
}
struct file_operations sleepy_fops = {
.owner = THIS_MODULE,
.read = sleepy_read,
.write = sleepy_write,
};
int sleepy_init(void)
{
int result;
result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops);
if (result < 0)
return result;
if (sleepy_major == 0)
sleepy_major = result; /* dynamic */
return 0;
}
void sleepy_cleanup(void)
{
unregister_chrdev(sleepy_major, "sleepy");
}
module_init(sleepy_init);
module_exit(sleepy_cleanup);
代码结构很简单,file_operation要求只完成了读和写。代码的作用是,在调用read函数时,进程会进入休眠状态,在write函数里,休眠的进程将被唤醒。
为了管理休眠进程,需要建立等待队列,等待队列就是一个进程链表,其中包含等待某个特定事件的所有进程。等待队列通过“等待队列头”来管理,等待队列头是一个类型为wait_queue_head_t的结构体。可以静态初始化一个等待队列头:
DECLARE_WAIT_QUEUE_HEAD(name);
也可以动态初始化一个等待队列头:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
一个进程要进入休眠,最常用的函数是:
wait_event_interruptible(queue, condition);
queue是等待队列头,condition是一个条件表达式,进程进入休眠前和被唤醒后,都会检查condition的值是否为真,如果不为真,则进程会进入休眠。
对应wait_event_interruptible的唤醒函数是:
wake_up_interruptible(wait_queue_head_t *queue)
下面测试一下这个小模块:
首先看他的装载和卸载脚本:
#!/bin/sh
# $Id: complete_load,v 1.4 2004/11/03 06:19:49 rubini Exp $
module="sleepy"
device="sleepy"
mode="666"
# Group: since distributions do it differently, look for wheel or use staff
if grep -q '^staff:' /etc/group; then
group="staff"
else
group="wheel"
fi
# invoke insmod with all arguments we got
# and use a pathname, as insmod doesn't look in . by default
/sbin/insmod ./$module.ko $* || exit 1
# retrieve major number
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
# Remove stale nodes and replace them, then give gid and perms
# Usually the script is shorter, it's scull that has several devices in it.
rm -f /dev/${device}
mknod /dev/${device} c $major 0
chgrp $group /dev/${device}
chmod $mode /dev/${device}
这个程序的测试需要用两个端口,开发板上只能支持一个,所以我把它放在了红帽子五自带的内核上运行,结果如下图:#!/bin/sh module="sleepy" device="sleepy" # invoke rmmod with all arguments we got /sbin/rmmod $module $* || exit 1 # Remove stale nodes rm -f /dev/${device}
左端的窗口用cat命令从sleepy设备读取数据时,会导致进程休眠,当在另一个窗口向/dev/sleepy中写入数据时,进程又会被唤醒。
参考博客:http://blog.csdn.net/liuhaoyutz/article/details/7388163