内核工作队列 赏析

queue_work
/**
    向工作队列提交新的工作节点。
*/
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
    int ret;
    /**
        调用了这个函数
        get_cpu() 获取当前cpu,并禁止内核抢占
    */  
    ret = queue_work_on(get_cpu(), wq, work);
    /**
        开启内核抢占
    */
    put_cpu();

    return ret;
}
/**
    返回 0 表示这个工作节点已经处于队列中了。
    如果该工作节点还没有被处理,你再次提交,就会直接返回,不允许。
*/
int queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
    int ret = 0;
    /**
        #define work_data_bits(work) ((unsigned long *)(&(work)->data))

        将data的第0位设置为 1,并且返回第0位原来的值
        这里为什么要将第0位设置为 1,而且还要返回原来的第0位的值呢?

        如果第0位原来的值是 1,表示该工作节点已经提交 还没有被处理。
        要把它置1,是为了说明,这个工作节点已经提交了,正在等待处理。
        上面说过,这样提交是不允许的。

        在看看这个函数 :
                #define INIT_WORK(_work, _func)                 \
                    do {                            \
                        __INIT_WORK((_work), (_func), 0);       \
                    } while (0)

                #define __INIT_WORK(_work, _func, _onstack)             \
                    do {                                \
                        __init_work((_work), _onstack);             \
                        (_work)->data = (atomic_long_t) WORK_DATA_INIT();   \
                        INIT_LIST_HEAD(&(_work)->entry);            \
                        PREPARE_WORK((_work), (_func));             \
                    } while (0)     
                WORK_DATA_INIT()将work_struct的data初始化为0。
                所以,在我们调用queue_work()之前,使用INIT_WORK()后,work_struct的data是0了,      
                无需我们手动的对data的位清零。
                在后面:
                在执行工作节点上的函数之前,会使用work_clear_pending(work)将该位清零。

                #define PREPARE_WORK(_work, _func)              \
                        do {                            \
                            (_work)->func = (_func);            \
                        } while (0)
                初始化一个工作项的函数指针

        WORK_STRUCT_PENDING_BIT = 0, 等待执行的工作项   
    */
    if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) 
    {
        __queue_work(cpu, wq, work);
        ret = 1;
    }
    return ret;
}

static void __queue_work(unsigned int cpu, struct workqueue_struct *wq, struct work_struct *work)
{
    ...
    insert_work(cwq, work, worklist, work_flags);
}

/**
    添加一个工作节点到 队列中
    看看这个cpu_workqueue_struct,
    当使用create_singlethread_workqueue()创建工作队列时,创建一个工作队列,
    只为当前cpu创建一个内核线程。
    如果是使用create_workqueue()创建工作队列时,创建一个工作队列,为系统中的每个cpu都创建一个内核线程。


    struct cpu_workqueue_struct
    {
        spinlock_t lock;          //自旋锁。这里为什么要定义自旋锁:工作者会频繁的处理挂接再worklist上的工作节点,所以需要加锁
        struct list_head worklist;        //任务队列,提交的工作节点就是挂在这个链表上的
        wait_queue_head_t more_work;      //等待队列,如果没有工作要处理,那么工作者线程就会在这个队列上睡眠
        struct work_struct *current_work; //工作者线程正在处理的工作节点
        struct task_struct *thread;       //指向工作者线程的进程描述符
    }
*/
static void insert_work(struct cpu_workqueue_struct *cwq,
            struct work_struct *work, struct list_head *head,
            unsigned int extra_flags)
{
    ....
    /**
        添加到worklist链表的尾部。
    */
    list_add_tail(&work->entry, head);
    if (__need_more_worker(gcwq))
    {
        /**
            唤醒在该等待队列上睡眠的线程。
            唤醒后,最终会执行run_workqueue()函数来处理工作节点。
        */
        wake_up_worker(gcwq);
    }   
}           

/**
    工作者线程 :
            它的作用是处理工作队列中的工作节点的。
            如果工作队列中有待处理的工作节点,他会将该工作节点从worklist链表上删除
            然后调用该工作节点上的函数。
            如果工作队列中没有工作节点了,它会睡眠,有上面的cpu_workqueue_struct注释可知
            它睡眠在more_work等待队列上

*/
static int worker_thread(void * __cwq)
{
    struct cpu_workqueue_struct *cwq = __cwq;
    /**
        这个宏的分析已经在我的前面博文中讲过了,
        它就是定义了一个wait_queue_t 类型的等待队列项
        且该进程被唤醒后会执行默认的唤醒函数autoremove_wake_function()
    */
    DEFINE_WAIT(wait);

    /**
        设置该工作者线程可以被冻结
    */
    if(cwq->wq->freezeable)
    {
        set_freezable();
    }
    /**
        一个死循环。
        在什么时候 会跳出该转换呢?
    */
    for(;;)
    {
        /**
            1 : 将上面定义的等待队列项插入到等待队列
            2 : 将进程设置为 TASK_INTERRUPTIBLE状态 
        */
        prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE);

        /**
            如果cwq->worklist上没有工作节点等待处理而且也没有被stop而且该工作者线程已经被冷冻了
            那么就睡眠,睡眠在cwq->more_work等待队列上
        */
        if (!freezing(current) &&!kthread_should_stop() &&list_empty(&cwq->worklist))
        {
            schedule();
        }   
        /**
            如果向cwq->worklist上提交了一个工作节点,那么该工作者线程会被唤醒,唤醒后执行下面的代码:

            1 : 将进程的状态设置为TASK_RUNNING
            2 : 从等待队列中删除该进程
        */
        finish_wait(&cwq->more_work, &wait);
        try_to_freeze();

        /**
            如果工作者线程被stop的话 ,就退出本次循环
        */
        if (kthread_should_stop())
        {
            break;
        }   

        /**
            遍历cwq->worklist上的工作节点,执行它的函数。
        */
        run_workqueue(cwq);
    }

    return 0;
}

static void run_workqueue(struct cpu_workqueue_struct *cwq)
{
    spin_lock_irq(&cwq->lock);
    while (!list_empty(&cwq->worklist)) 
    {
        /**
            获取
        */
        struct work_struct *work = list_entry(cwq->worklist.next,struct work_struct, entry);
        /**
            获取工作节点上绑定的的处理函数指针
            这个函数是再 INIT_WORK()宏中绑定的。前面开头的时候说过。
        */
        work_func_t f = work->func;
        /**
            从worklist链表上删除这个工作节点
        */  
        list_del_init(cwq->worklist.next);
        spin_unlock_irq(&cwq->lock);
        ...
        /**
            清除work_struct->data的第0位。
            在提交工作节点到工作队列的时候 说过。
        */
        work_clear_pending(work);
        ....
        /**
            终于调用了。
        */
        f(work);
        .....
}

/**
    delay: 工作节点延迟挂到工作队列上的时间
*/
int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
{
    /**
        如果delay为0,还是调用queue_work()提交函数
    */
    if (delay == 0)
    {
        return queue_work(wq, &dwork->work);
    }   

    return queue_delayed_work_on(-1, wq, dwork, delay);
}

int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
{
    int ret = 0;
    struct timer_list *timer = &dwork->timer;
    struct work_struct *work = &dwork->work;

    /**
        还是判断data的0位是否是0.和上面说的queue_work()的一样。
    */
    if (!test_and_set_bit(WORK_STRUCT_PENDING, work_data_bits(work))) 
    {
        ....
        /**
            看,他是利用定时器来延时的。
        */

        timer->expires = jiffies + delay; 
        timer->data = (unsigned long)dwork;
        /**
            时间到后,会执行这个函数
        */
        timer->function = delayed_work_timer_fn;
        ....
    }
}
/**
    剩下的函数调用流程。和queyr_work()的一样了。
*/
static void delayed_work_timer_fn(unsigned long __data)
{
    struct delayed_work *dwork = (struct delayed_work *)__data;
    struct cpu_workqueue_struct *cwq = get_wq_data(&dwork->work);
    struct workqueue_struct *wq = cwq->wq;

    __queue_work(wq_per_cpu(wq, smp_processor_id()), &dwork->work);
}

这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值