struct completion:
有的时候我们需要在一个线程里面发起另外一个线程里的某些动作,然后等待另外一个线程的动作完成.这个我们可以用completion.这是信号量的一种简单实现.
完成变量是信号量的一种简单的实现。当一个任务运行需要请求某个资源或条件的情况下, wait_for_completion() 函数将此任务放入等待队列,等待。另外一个任务使用完这个资源通过 complete() 函数发送一个完成变量,通知等待队列中的这个任务继续执行。
完成变量的实现函数
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
void fastcall __sched wait_for_completion(struct completion *x)
{
might_sleep();
spin_lock_irq(&x->wait.lock);
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);
do {
__set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&x->wait.lock);
schedule();
spin_lock_irq(&x->wait.lock);
} while (!x->done);
__remove_wait_queue(&x->wait, &wait);
}
x->done--;
spin_unlock_irq(&x->wait.lock);
}
void fastcall complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++;
__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
1, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
完成变量的使用举例
demo 程序
/*
* chardev.c: Creates a read-only char device that says how many times
* you've read from the dev file
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
/* for put_user */
#include <linux/completion.h>
#if 1
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
#endif
#define SUCCESS 0
#define DEVICE_NAME "chardev"
/* Dev name as it appears in /proc/devices */
//#define BUF_LEN 80
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Edwin");
static int Major;
//struct completion mr_completion;
//init_completion(&mr_completion);
DECLARE_COMPLETION(mr_completion);
//struct completion mr_completion;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
/*
* Functions
*/
int completion_init(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk("Registering the character device failed with %d/n",
Major);
return Major;
}
#if 1
printk("<1>I was assigned major number %d. To talk to/n", Major);
printk("<1>the driver, create a dev file with/n");
printk("'mknod /dev/hello c %d 0'./n", Major);
printk("<1>Try various minor numbers. Try to cat and echo to/n");
printk("the device file./n");
printk("<1>Remove the device file and module when done./n");
#endif
return 0;
}
void completion_exit(void)
{
/*
* Unregister the device
*/
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0)
printk("Error in unregister_chrdev: %d/n", ret);
else
printk("The %s moudle is released./n",DEVICE_NAME);
}
module_init(completion_init);
module_exit(completion_exit);
/*
* Methods
*/
/*
* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
try_module_get(THIS_MODULE);
return SUCCESS;
}
/*
* Called when a process closes the device file.
*/
static int device_release(struct inode *inode, struct file *file)
{
/*
* Decrement the usage count, or else once you opened the file, you'll
* never get get rid of the module.
*/
module_put(THIS_MODULE);
return SUCCESS;
}
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp,
/* see include/linux/fs.h */
char *buffer,
size_t length,
loff_t * offset)
{
printk("<1>the current process %i %s will sleep at once./n", current->pid, current->comm);
wait_for_completion(&mr_completion);
printk("<1>the completion sig is released................................./n");
//printk("<1> device_read operation is executed/n");
return 0;
}
/*
* Called when a process writes to dev file: echo "hi" > /dev/hello
*/
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk("<1>the echo operation is executing /n");
complete(&mr_completion);
printk("<1>the completion sig is launched /n");
//printk("<1>Sorry, this operation isn't supported./n");
//return -EINVAL;
}