天天看點

linux completion 筆記

linux completion 筆記

-v0.1 2014.12.1 *** draft

1. 使用場景

   任務A停住,等待任務B完成後任務A再執行。

   一個簡單的例子是:在裝置驅動中,裝置發起一個DMA操作,這個操作完成是需要一定的

   時間的。這時可以使用完成量,叫這個程序被排程出去。當DMA操作完成時,裝置向CPU

   發中斷,我們在中斷處理程式中喚醒等待的任務,即剛剛被排程出去的程序重新執行

2. 使用方法

   完成量的核心資料結構是: 

struct completion {
	    unsigned int done;
	    wait_queue_head_t wait;
   }
           

   使用時先申明一個完成量:

   靜态申明:

       DECLARE_COMPLETION(work);

   動态申明:

       init_completion(struct completion *);

   使用函數:wait_for_completion(struct completion *);使目前程序挂起,等待完成

   量被喚醒。

   使用函數:complete(struct completion *); 喚醒完成量。上面的中斷處理程式中即

   使用該函數喚醒排程出去的程序。

3. 實作原理

  完成量依靠完成隊列實作,先看相關的資料結構:

  struct completion 包含一個完成隊列的頭,這是一個連結清單頭. 連結清單上挂的元素是

  struct __wait_queue, 如下:

struct __wait_queue {
	    unsigned int	flags;
    #define WQ_FLAG_EXCLUSIVE	0x01
	    void		*private;
	    wait_queue_func_t	func;
	    struct list_head	task_list;
    };
           

    wait_for_completion()的調用鍊如下:

        --> wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);

            --> __wait_for_common(x, schedule_timeout, timeout, state);

                --> do_wait_for_common(x, action, timeout, state);

    最後核心函數是:do_wait_for_common()

do_wait_for_common(struct completion *x,
		       long (*action)(long), long timeout, int state)
    {
	    if (!x->done) {
		    /* 生成一個__wait_queue結構, 目前程序的struct task_struct
		     * 結構到private上。
		     */
		    DECLARE_WAITQUEUE(wait, current);


                    /* 将上面生成的__wait_queue挂到完成量x的完成隊列頭上 */
		    __add_wait_queue_tail_exclusive(&x->wait, &wait);
		    do {
			    if (signal_pending_state(state, current)) {
				    timeout = -ERESTARTSYS;
				    break;
			    }
			    __set_current_state(state);
			    spin_unlock_irq(&x->wait.lock);
			    /* action為schedule_timeout, 運作該函數目前程序将
			     * 将睡眠,排程器排程其他程序執行
			     */
			    timeout = action(timeout);
			    spin_lock_irq(&x->wait.lock);
		    } while (!x->done && timeout);
		    __remove_wait_queue(&x->wait, &wait);
		    if (!x->done)
			    return timeout;
	    }
	    x->done--;
	    return timeout ?: 1;
    }

    void complete(struct completion *x)
    {
	    unsigned long flags;


	    spin_lock_irqsave(&x->wait.lock, flags);
	    /* 将完成量x中的done加一,再喚醒完成隊列上的程序,這時上面函數中的
	     * action()傳回,done不為0,上面的函數将跳出while(), 執行
	     * __remove_wait_queue()将目前程序移出相應的等待隊列
	     */
	    x->done++;
	    __wake_up_locked(&x->wait, TASK_NORMAL, 1);
	    spin_unlock_irqrestore(&x->wait.lock, flags);
    }
           

reference:

[1] http://blog.csdn.net/dreamxu/article/details/5866593 

繼續閱讀