31 completion完成量,wait_queue_head_t等待队列和epoll

完成量, 功能与信号量差不多, 最大不同可以唤醒多个休眠的进程或线程

#include <linux/completion.h>
struct completion {
    unsigned int done; //done表示资源,上锁时done--. 当done为0时再上锁则会安排调用进程或线程进入休眠.
               //解锁时, done++
    wait_queue_head_t wait; //等待完成的进程链表
};

void init_completion(struct completion *x); //初始化done=0;

void wait_for_completion(struct completion *); //done--,当done为0时,调用进程或线程被安排进入休眠
int wait_for_completion_interruptible(struct completion *x); //可被信号唤醒的休眠


extern void complete(struct completion *); //完成 done++
extern void complete_all(struct completion *); //把done的值设为非常大, 让所有在等待锁资源的休眠进程或线程醒过来. 注意complete_all一般是工作退出时发出通知使用,只用一次. 如驱动卸载时上锁等工作线程退出时再解锁.


如实现一个设备驱动,多个用户进程在读操作时等待数据写入, 当写操作时唤醒所有的休眠进程.

test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/completion.h>


struct completion com;
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int ret;

    ret = wait_for_completion_interruptible(&com);
    if (ret < 0)
        return -ERESTART;

    printk(" pid = %d, %s waitting\n", current->pid, current->comm);

    return ret;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{   
    complete_all(&com); //设done的值为UINT_MAX/2, 唤醒所有的休眠进程
    return len; 
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

struct miscdevice mymdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mymdev",
    .fops = &fops,
};

static int __init test_init(void)
{
    init_completion(&com);
    return misc_register(&mymdev);
}

static void __exit test_exit(void)
{
    misc_deregister(&mymdev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

///

互斥锁,自旋锁,信号量,完成量等锁基本上都是按照先后顺序唤配休眠的进程.
但在某些场合,想唤醒满足特定条件的特定休眠进程时就无法做到. 在linux内核里可用等待队列来实现这种需求。

#include <linux/wait.h>

wait_queue_head_t //表示队列头类型. 相当于一个链表头.每个节点就是要等待某种条件的休眠进程.

void init_waitqueue_head(wait_queue_head_t *q); //初始化等待队列

// condition 是C语言表达式的条件语句
//wq 是等待队列头的变量. 下面三个函数把当前调用的进程根据condition创建出一个队列节点,加入指定的队列里
wait_event(wq, condition); //如果指定的condition条件不满足,就会创建节点加入队列.调用的进程就会进入休眠.
wait_event_timeout(wq, condition, timeout);
wait_event_interruptible(wq, condition);


//下面两个函数用于唤醒指定的队列,当唤醒时,内核会检查队列里每个节点的条件是否满足,如已满足,则让相应的进程醒过来. 通常情况下,唤醒队列前会改变条件的值.
wake_up(wait_queue_head_t *queue); 
wake_up_interruptible(wait_queue_head_t *queue);

///
如实现一个设备驱动,当用户进程在读操作时等待变量mypid的值与它的进程号的个位值一致,当写操作时根据传进来的内容设置变量mypid的值.

test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <linux/wait.h>

wait_queue_head_t wq;
int mypid = -1;
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int ret;

    ret = wait_event_interruptible(wq, mypid == (current->pid)%10);
    if (ret < 0)
        return -ERESTART; 
    printk(" pid = %d, %s after wait\n", current->pid, current->comm);

    return ret;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{   
    mypid = buf[0] - '0';
    wake_up(&wq); //让内核检查队列
    return len; 
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

struct miscdevice mymdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mymdev",
    .fops = &fops,
};

static int __init test_init(void)
{
    init_waitqueue_head(&wq);
    return misc_register(&mymdev);
}

static void __exit test_exit(void)
{
    misc_deregister(&mymdev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");


让设备驱动支持应用程序通过poll / select / epoll 函数来监控文件描述符的状态, 是由设备驱动里的struct file_opeartions结构体对象的poll成员实现的.

用法:

   wait_queue_head_t queue; //1. 声明一个等待队列头对象, 在驱动的poll函数里供调用poll_wait时使用
   init_waitqueue_head(&queue) //2. 初始化一个等待队列头对象

//3. 驱动里的poll函数主要就是调用poll_wait函数处理文件描述符和返回文件描述符的状态
  unsigned int (*poll) (struct file *fl, struct poll_table_struct *tbl)
 {
    poll_wait(fl, &queue, tbl); //把文件描述符fl添加到当前调用进程所监控的文件描述符列表.
        //注意调用poll_wait是不会发生休眠的,这里仅仅是把文件描述符加入监控列表而已.

    if (condition) //根据条件返回不同的文件描述符状态值
        return POLLIN|POLLRDNORM(可读), POLLOUT|POLLWRNORM(可写)
    else
        return 0; //文件描述符状态没有发生变化
    //内核会根据此函数的返回值来确定当前调用进程的文件描述符fl是否发生状态变化,如当前文件描述符和进程的其它所有文件描述符都没有发生变化, 内核则会安排当前进程进入休眠.
 }
 //4. 当条件condition满足时需要设备驱动调用wake_up(&queue)来唤醒 

//内核调用实现的poll函数在fs/select.c, 745行处
    mask = file->f_op->poll(file, pwait); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值