Linux的completions同步機制

1. 什麼是completions機制?

在內核編程中常有這樣的場景,在當前線程中創建一個線程,並且等待它完成之後再繼續執行。通常可以用信號量來解決它,也可以用completion機制來解決。

2. 爲什麼用completions ,它比信號量好在哪?

使用completion比使用信號量簡單。

使用completion可以一次性喚醒所有等待進程,而用信號量會比較麻煩。

 The basic summary is that we had this (fairly common) way of waiting for certain events by having a locked semaphore on the stack of the waiter, and then having the waiter do a "down()" which caused it to block until the thing it was waiting for did an "up()".

This works fairly well, but it has a really small (and quite unlikely) race on SMP, that is not so much a race of the idea itself, as of the implementation of the semaphores. We could have fixed the semaphores, but there were a few reasons not to:

  • the semaphores are optimized (on purpose) for the non-contention case. The "wait for completion" usage has the opposite default case
  • the semaphores are quite involved and architecture-specific, exactly
    due to this optimization. Trying to change them is painful as hell.

 

Completions

A common pattern in kernel programming involves initiating some activity outside of the current thread, then waiting for that activity to complete. This activity can be the creation of a new kernel thread or user-space process, a request to an existing process, or some sort of hardware-based action. It such cases, it can be tempting to use a semaphore for synchronization of the two tasks, with code such as:

struct semaphore sem;

init_MUTEX_LOCKED(&sem);
start_external_task(&sem);
down(&sem);

The external task can then call up(&sem) when its work is done.

As is turns out, semaphores are not the best tool to use in this situation. In normal use, code attempting to lock a semaphore finds that semaphore available almost all the time; if there is significant contention for the semaphore, performance suffers and the locking scheme needs to be reviewed. So semaphores have been heavily optimized for the "available" case. When used to communicate task completion in the way shown above, however, the thread calling down will almost always have to wait; performance will suffer accordingly. Semaphores can also be subject to a (difficult) race condition when used in this way if they are declared as automatic variables. In some cases, the semaphore could vanish before the process calling up is finished with it.

 

3. 怎麼用completions?

 

/*
 * Completions 目前使用先進先出的隊列來存放等待‘completion’的事件
 */
struct completion {
        unsigned int done;
        wait_queue_head_t wait;
};

 

 

    
    (1) 定義completion
        struct completion my_completion;
    (2) 初始化completion
        init_completion(&my_completion);/*動態創建一個completion*/
		
        DECLEARE_COMPLETION(my_completion);/*(包含定義)靜態初始化completion*/
		
    (3) 等待completion
	/*This waits to be signaled for completion of a specific task. It is NOT interruptible and there is no timeout.*/
	void wait_for_completion(struct completion *c);
	/*This waits for completion of a specific task to be signaled. It is interruptible.*/
	int wait_for_completion_interruptible(struct completion *x); 
	/*This waits to be signaled for completion of a specific task. It can be
	* interrupted by a kill signal.
	*/
	int wait_for_completion_killable(struct completion *x);
		
	/*This waits for either a completion of a specific task to be signaled or for a
	* specified timeout to expire. The timeout is in jiffies. It is not
	* interruptible.
	*/
	unsigned long wait_for_completion_timeout(struct completion *x,
							   unsigned long timeout);
	/** This waits for either a completion of a specific task to be signaled or for a
	* specified timeout to expire. It is interruptible. The timeout is in jiffies.
	*/
	long wait_for_completion_interruptible_timeout(
				struct completion *x, unsigned long timeout);
	/*This waits for either a completion of a specific task to be
	* signaled or for a specified timeout to expire. It can be
	* interrupted by a kill signal. The timeout is in jiffies.
	*/
	long wait_for_completion_killable_timeout(
				struct completion *x, unsigned long timeout);
    (4) 喚醒completion
        void complete(struct completion *c);/*喚醒一個等待的Thread*/
        void complete_all(struct completion *c);/*喚醒所有等待此completion的Threads*/


[1] http://stackoverflow.com/questions/4764945/difference-between-completion-variables-and-semaphores

[2] http://www.makelinux.net/ldd3/chp-5-sect-4

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章