Linux内核组件——completion接口(一种内核线程的同步机制)

下面的内容翻译来源:《Essential Linux Device Drivers》 chapter3 Completion Interface。

Many parts of the kernel initiate certain activities as separate execution threads and then wait for them to complete. The completion interface is an efficient and easy way to implement such code patterns.

Linux内核的一些模块会激活一些独立的可执行的线程,然后等待他们执行完成。completion接口是一种有效的方法去实现这种代码模式(相当于一种解决方法)。

Some example usage scenarios include the following:

如下一些实例(三种应用的情形):

  • Your driver module is assisted by a kernel thread. If you rmmod the module, the release() method is invoked before removing the module code from kernel space. The release routine asks the thread to kill itself and blocks until the thread completes its exit. Listing 3.7 implements this case.

我们编写的驱动模块被一个内核线程加载到内核中(insmod),如果我们执行rmmod去移除这个模块,模块的release()函数就会被调用执行,在模块从内核空间清除之前。下面这个例子的release()会唤醒一个线程B,然后阻塞当前线程A ,B线程执行完成后把自己杀死。

  • You are writing a portion of a block device driver (discussed in Chapter 14, "Block Drivers") that queues a read request to a device. This triggers a state machine change implemented as a separate thread or work queue. The driver wants to wait until the operation completes before proceeding with another activity. Look at drivers/block/floppy.c for an example.

假如我们写一个块设备驱动,需要排队读取一个设备。这触发了一个状态机 变成一个独立的线程或者工作队列,等待操作完成,在执行下一次操作。drivers/block/floppy.c就是一个例子。

  • An application requests an Analog-to-Digital Converter (ADC) driver for a data sample. The driver initiates a conversion request waits, until an interrupt signals completion of conversion, and returns the data.

一个应用程序请求一次ADC驱动的采样功能,这个驱动初始化了一个转换请求等待,直到转换完成,产生一个中断信号,返回采样数据。

static DECLARE_COMPLETION(my_thread_exit);      /* Completion */
static DECLARE_WAIT_QUEUE_HEAD(my_thread_wait); /* Wait Queue */
int pink_slip = 0;                              /* Exit Flag */

/* Helper thread */
static int
my_thread(void *unused)
{
  DECLARE_WAITQUEUE(wait, current);

  daemonize("my_thread");
  add_wait_queue(&my_thread_wait, &wait);

  while (1) {
    /* Relinquish processor until event occurs */
    set_current_state(TASK_INTERRUPTIBLE);
    schedule();
    /* Control gets here when the thread is woken
       up from the my_thread_wait wait queue */

    /* Quit if let go */
    if (pink_slip) {
      break;
    }
    /* Do the real work */
    /* ... */

  }

  /* Bail out of the wait queue */
  __set_current_state(TASK_RUNNING);
  remove_wait_queue(&my_thread_wait, &wait);

  /* Atomically signal completion and exit */
  complete_and_exit(&my_thread_exit, 0);
}

/* Module Initialization */
static int __init
my_init(void)
{
  /* ... */

  /* Kick start the thread */
  kernel_thread(my_thread, NULL,
                CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);

  /* ... */
}

/* Module Release */
static void __exit
my_release(void)
{
  /* ... */
  pink_slip = 1;                        /* my_thread must go */
  wake_up(&my_thread_wait);             /* Activate my_thread */
  wait_for_completion(&my_thread_exit); /* Wait until my_thread
                                           quits */
  /* ... */
}

A completion object can be declared statically using DECLARE_COMPLETION() or created dynamically with init_completion(). A thread can signal completion with the help of complete() or complete_all(). A caller can wait for completion via wait_for_completion().

completion对象可以通过使用DECLARE_COMPLETION() 静态声明,也可以通过init_completion()动态声明。

一个线程可以使用complete()或者complete_all().表明completion。一个调用者(另外一个线程)可以使用wait_for_completion()等待completion。(可以理解为两个线程的同步操作。)

In Listing 3.7, my_release() raises an exit request flag by setting pink_slip before waking up my_thread(). It then calls wait_for_completion() to wait until my_thread() completes its exit. my_thread(), on its part, wakes up to find pink_slip set, and does the following:

上面的例子 my_release()函数在模块退出的时候被调用,然后设置pink_slip为1,唤醒线程my_thread()。然后调用wait_for_completion()阻塞当前线程。直到线程my_thread()执行完成后调用complete_and_exit(&my_thread_exit, 0);

  1. Signals completion to my_release()

  2. Kills itself

my_thread() accomplishes these two steps atomically using complete_and_exit(). Using complete_and_exit() shuts the window between module exit and thread exit that opens if you separately invoke complete() and exit().

We will use the completion API when we develop an example telemetry driver in Chapter 11.

my_thread()线程使用 complete_and_exit().已原子的方式退出,保证不被中断。在11章中会使用completion的API接口实例。

附加的一段代码(from LDD3):

#include <linux/module.h>
#include <linux/init.h>
 
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/completion.h>
 
MODULE_LICENSE("GPL");
 
static int complete_major=250;
DECLARE_COMPLETION(comp);
 
ssize_t complete_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{
	printk(KERN_ERR "process %i (%s) going to sleep\n",current->pid,current->comm);
	wait_for_completion(&comp);
	printk(KERN_ERR "awoken %i (%s)\n",current->pid,current->comm);
	return 0;
}
 
ssize_t complete_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)
{
	printk(KERN_ERR "process %i (%s) awakening the readers...\n",current->pid,current->comm);
	complete(&comp);
	return count;
}
 
struct file_operations complete_fops={
	.owner=THIS_MODULE,
	.read=complete_read,
	.write=complete_write,
};
 
int complete_init(void)
{
	int result;
	result=register_chrdev(complete_major,"complete",&complete_fops);
	if(result<0)
		return result;
	if(complete_major==0)
		complete_major=result;
	return 0;
}
void complete_cleanup(void)
{
	unregister_chrdev(complete_major,"complete");
}
module_init(complete_init);
module_exit(complete_cleanup);
发布了129 篇原创文章 · 获赞 322 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/seek_0380/article/details/85051398