Andorid linux模块编译和加载(三)

上节中我们已经掌握了创建大量内核线程的能力,可惜线程之间还缺乏配合。要知道学习ITC(inter thread communication),和学习IPC(inter process communication)一样,不是件简单的事情。本节就暂且解释一种最简单的线程同步手段—completion。

        打开include/linux/completion.h,你就会看到completion使用的全部API。这里简单介绍一下。

  1. struct completion{  
  2.     unsigned int done;  
  3.     wait_queue_head_t wait;  
  4. };  
  5.   
  6. void init_completion(struct completion *x);  
  7. void wait_for_completion(struct completion *x);  
  8. void wait_for_completion_interruptible(struct completion *x);  
  9. void wait_for_completion_killable(struct completion *x);  
  10. unsigned long wait_for_completion_timeout(struct completion *x,   
  11.     unsigned long timeout);  
  12. unsigned long wait_for_completion_interruptible_timeout(struct completion *x,   
  13.     unsigned long timeout);  
  14. bool try_wait_for_completion(struct completion *x);  
  15. bool completion_done(struct completion *x);  
  16. void complete(struct completion *x);  
  17. void complete_all(struct completion *x);  


       首先是struct completion的结构,由一个计数值和一个等待队列组成。我们就大致明白,completion是类似于信号量的东西,用completion.done来表示资源是否可用,获取不到的线程会阻塞在completion.wait的等待队列上,直到其它线程释放completion。这样理解在实现上不错,但我认为completion不是与具体的资源绑定,而是单纯作为一种线程间同步的机制,它在概念上要比信号量清晰得多。以后会逐渐看到,线程间事件的同步大多靠completion,而资源临界区的保护大多靠信号量。所以说,completion是一种线程间的约会。

           init_completion初始化completion结构。初此之外,linux当然还有在定义变量时初始化的方法,都在completion.h中。

      wait_for_completion等待在completion上。如果加了interruptible,就表示线程等待可被外部发来的信号打断;如果加了killable,就表示线程只可被kill信号打断;如果加了timeout,表示等待超出一定时间会自动结束等待,timeout的单位是系统所用的时间片jiffies(多为1ms)。

      try_wait_for_completion则是非阻塞地获取completion。它相当于wait_for_completion_timeout调用中的timeout值为0。

      completion_done检查是否有线程阻塞在completion上。但这个API并不准确,它只是检查completion.done是否为0,为0则认为有线程阻塞。这个API并不会去检查实际的等待队列,所以用时要注意。

      complete唤醒阻塞在completion上的首个线程。

      complete_all唤醒阻塞在completion上的所有线程。它的实现手法很粗糙,把completion.done的值设为UINT_MAX/2,自然所有等待的线程都醒了。所以如果complete_all之后还要使用这个completion,就要把它重新初始化。

 

      好,completion介绍完毕,下面就来设计我们的模块吧。

      我们模拟1个周期性线程的运行,有completion变量。period_thread每个周期运行一次,然后等待在自己的completion变量上。为了唤醒period_thread,我们使用一个watchdog_thread来模拟时钟,每隔1s watchdog_thread就会用相应的completion唤醒线程。

      下面就动手实现吧。

1、把上节建立的kthread子目录,复制为新的completion子目录。

 

2、修改hello.c,使其内容如下。

#include <linux/init.h>  
#include <linux/module.h>  
#include <linux/kthread.h>  
//#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/completion.h>

MODULE_LICENSE("Dual BSD/GPL");  

static struct completion wakeup;
static struct task_struct *tsk;
static struct task_struct *watchdog_tsk;
static int exit_flag = 0;  
 
static int thread_function(void *data)  
{  
    int time_count = 0;
    do {  
        printk(KERN_INFO "thread_function: %d times", ++time_count);  
        //msleep(1000);
        //udelay(1000);
        wait_for_completion(&wakeup);
    }while(!kthread_should_stop());  

    return 0;  
}  

static int watchdog_thread(void *data)  
{  
    int count = 0;  
     
    do{  
        msleep(1000);  
        //count++;  
       //if (count%5 == 0)  
             complete(&wakeup);  

       printk(KERN_INFO "watchdog_function\n");  
    }while(!kthread_should_stop());  

    return count;  
}  

static int hello_init(void)  
{  
    printk(KERN_INFO "Hello, world!\n");  
    
    init_completion(&wakeup);

    tsk = kthread_run(thread_function, NULL, "mythread%d", 1);
    if (IS_ERR(tsk)) {  
            printk(KERN_INFO "create kthread failed!\n");  
    }  
    else {  
        printk(KERN_INFO "create ktrhead ok!\n");  
    }

       watchdog_tsk = kthread_run(watchdog_thread, NULL, "watchdog_thread");   
       if(IS_ERR(watchdog_tsk)){                                               
             printk(KERN_INFO "create watchdog_thread failed!\n");              
        }    

    return 0;  
}  

static void hello_exit()  
{  
    printk(KERN_INFO "Hello, exit! %d\n",tsk->state);  
    exit_flag = 1;

    complete_all(&wakeup);

    if (!IS_ERR(tsk)){  
       int ret = kthread_stop(tsk);  
       printk(KERN_INFO "thread function has run %ds\n", ret);  
    }

       if(!IS_ERR(watchdog_tsk)){  
           int ret = kthread_stop(watchdog_tsk);
           printk(KERN_INFO "watchdog thread function has run %ds\n", ret);  
     }
}  

module_init(hello_init);  
module_exit(hello_exit); 


3、编译运行模块,步骤参照前例。为保持模块的简洁性,我们仍然使用了kthread_stop结束线程,这种方法虽然简单,但在卸载模块时等待时间太长,而且这个时间会随线程个数和周期的增长而增长。

 


      经过本节,我们学会了一种内核线程间同步的机制—completion。线程们已经开始注意相互配合,以完成复杂的工作。相信它们会越来越聪明的。

 

     

附注:

     completion的实现在kernel/sched.c中。这里的每个API都较短,实现也较为简单。completion背后的实现机制其实是等待队列。等待队列的实现会涉及到较多的调度问题,这里先简单略过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值